452 lines
13 KiB
Python
452 lines
13 KiB
Python
# TODO -
|
|
|
|
# add:
|
|
# fix:
|
|
# update:
|
|
|
|
# algorithm steps
|
|
#
|
|
# prerequisites
|
|
# 01 clear the screen
|
|
# 02 allow a user to determine the size of the playfield
|
|
# 03 allow a user to enter names of two players
|
|
# 04 allow a user to determine the symbol count needed to win
|
|
#
|
|
# 1 clear the screen
|
|
# 2 print the current state of the game
|
|
# 3 allow for one player or the other to insert coords by moving with arrow keys and to put the symbol to the selected place by pressing space
|
|
# 5 check if the game should end (if someone won)
|
|
# 5.1 if yes -> end the game
|
|
# 5.2 if no -> jump to step 1
|
|
|
|
import os, time
|
|
from pynput import keyboard
|
|
from pynput.keyboard import Key, Listener, KeyCode
|
|
|
|
# these global variables are flags
|
|
end = 0
|
|
turn = 0
|
|
error = 0
|
|
|
|
# misc. global vars
|
|
_size = 0
|
|
winLine = []
|
|
cursorX = cursorY = 0 # setting the default cursor position to the left top corner
|
|
|
|
# error codes
|
|
#
|
|
# 1 - invalid value
|
|
# 2 - invalid index/position
|
|
# 3 - index/position occupied
|
|
|
|
def init():
|
|
global end, turn, pf, name1, name2, symbolCount, cursorX, cursorY, size, _size
|
|
|
|
# 01 -> clear the screen
|
|
clearScreen()
|
|
|
|
# 02 -> allow a user to determine the size of the playfield
|
|
size = None
|
|
F_printOneMoreTime = 0 # variables starting with 'F_' are flags
|
|
while size == None:
|
|
try:
|
|
size = int(input("Size of the playfield (n*n): "))
|
|
_size = size
|
|
except ValueError:
|
|
clearScreen()
|
|
print("Enter a number")
|
|
F_printOneMoreTime = 1
|
|
continue
|
|
if F_printOneMoreTime == 1:
|
|
clearScreen()
|
|
print("Size of the playfield (n*n):", size)
|
|
F_printOneMoreTime = 0
|
|
if size < 3:
|
|
clearScreen()
|
|
print("Size of the playfield (n*n):", size, "(changed to 3*3)")
|
|
size = 3
|
|
_size = size
|
|
|
|
|
|
# 03 -> allow a user to enter names of two players
|
|
name1 = input("Name of the first player: ")
|
|
name2 = input("Name of the second player: ")
|
|
|
|
# 04 -> allow a user to determine the symbol count needed to win
|
|
symbolCount = None
|
|
F_printOneMoreTime = 0
|
|
while symbolCount == None:
|
|
try:
|
|
symbolCount = int(input("Number of symbols inline needed to win: "))
|
|
except ValueError:
|
|
clearScreen()
|
|
print("Enter a number")
|
|
F_printOneMoreTime = 1
|
|
continue
|
|
if F_printOneMoreTime == 1:
|
|
clearScreen()
|
|
print("Size of the playfield (n*n):", size, "(changed to 3*3)")
|
|
print("Name of the first player:", name1)
|
|
print("Name of the second player:", name2)
|
|
print("Number of symbols inline needed to win:", symbolCount)
|
|
F_printOneMoreTime = 0
|
|
if symbolCount > _size >= 0:
|
|
clearScreen()
|
|
print("Size of the playfield (n*n):", _size)
|
|
print("Name of the first player:", name1)
|
|
print("Name of the second player:", name2)
|
|
print("Number of symbols inline needed to win:", symbolCount, f"(changed to {_size}*{_size})")
|
|
symbolCount = _size
|
|
time.sleep(2)
|
|
|
|
# creating the playfield
|
|
pf = [] # pf is shor for PlayField
|
|
temp = []
|
|
for i in range(size):
|
|
for j in range(size):
|
|
temp.append(' ')
|
|
pf.append(temp)
|
|
temp = []
|
|
|
|
# setting the hotkeys for moving with the cursor and putting down symbols
|
|
|
|
while end == 0:
|
|
gameRound(pf, name1, name2)
|
|
|
|
# clears the screen
|
|
def clearScreen():
|
|
if os.name == "posix":
|
|
os.system("clear")
|
|
if os.name == "nt":
|
|
os.system("cls")
|
|
|
|
# detects if a position is inside the playfield
|
|
def inBound(x):
|
|
global _size
|
|
|
|
if 0 <= x < _size:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def blinkPrint(pf, name1, name2):
|
|
clearScreen()
|
|
print(name1, "(o) vs", name2, "(x)")
|
|
for i in range(_size):
|
|
for j in range(_size):
|
|
if [i, j] in winLine:
|
|
print("[ ]", end=" ")
|
|
continue
|
|
print(f"[{pf[i][j]}]", end=" ")
|
|
print("")
|
|
time.sleep(0.5)
|
|
|
|
def _print(pf, name1, name2, blink = 0):
|
|
clearScreen()
|
|
if blink == 2:
|
|
print("o won!")
|
|
elif blink == 3:
|
|
print("x won!")
|
|
elif blink == 4:
|
|
print("Draw!")
|
|
|
|
print(name1, "(o) vs", name2, "(x)")
|
|
for i in range(len(pf)):
|
|
for j in range(len(pf)):
|
|
if cursorX == j and cursorY == i and blink == 0:
|
|
print(f"#{pf[i][j]}#", end=" ")
|
|
continue
|
|
print(f"[{pf[i][j]}]", end=" ")
|
|
print("")
|
|
if blink == 1:
|
|
time.sleep(0.5)
|
|
|
|
# prints the current state of the game
|
|
def gameState(pf, name1, name2, blink = 0):
|
|
global error, cursorX, cursorY
|
|
|
|
if error == 1:
|
|
print("Invalid value")
|
|
error = 0
|
|
if error == 2:
|
|
print("Invalid position")
|
|
error = 0
|
|
if error == 3:
|
|
print("Position occupied")
|
|
error = 0
|
|
|
|
if blink == 1:
|
|
for i in range(6):
|
|
if i % 2 == 0:
|
|
blinkPrint(pf, name1, name2)
|
|
if i % 2 == 1:
|
|
_print(pf, name1, name2, 1)
|
|
elif blink == 0:
|
|
_print(pf, name1, name2)
|
|
elif blink == 2:
|
|
_print(pf, name1, name2, 2)
|
|
elif blink == 3:
|
|
_print(pf, name1, name2, 3)
|
|
elif blink == 4:
|
|
_print(pf, name1, name2, 4)
|
|
|
|
# checks whether someone has won
|
|
def check(pf):
|
|
global symbolCount, winLine
|
|
|
|
winLine = []
|
|
|
|
# horizontal checking
|
|
# 0 is for when 'o's win and 1 is for when 'x's win
|
|
temp = []
|
|
o = x = 0
|
|
for i in range(_size):
|
|
for j in range(_size):
|
|
if pf[i][j] == 'o':
|
|
temp.append([i, j])
|
|
o += 1
|
|
if o == symbolCount:
|
|
winLine = temp
|
|
return 0
|
|
if pf[i][j] == ' ' or pf[i][j] == 'x':
|
|
temp = []
|
|
o = 0
|
|
if o < symbolCount:
|
|
temp = []
|
|
o = 0
|
|
|
|
for i in range(_size):
|
|
for j in range(_size):
|
|
if pf[i][j] == 'x':
|
|
temp.append([i, j])
|
|
x += 1
|
|
if x == symbolCount:
|
|
winLine = temp
|
|
return 1
|
|
if pf[i][j] == ' ' or pf[i][j] == 'o':
|
|
temp = []
|
|
x = 0
|
|
if x < symbolCount:
|
|
temp = []
|
|
x = 0
|
|
|
|
# vertical checking
|
|
temp = []
|
|
o = x = 0
|
|
for j in range(_size):
|
|
for i in range(_size):
|
|
if pf[i][j] == 'o':
|
|
temp.append([i, j])
|
|
o += 1
|
|
if o == symbolCount:
|
|
winLine = temp
|
|
return 0
|
|
if pf[i][j] == ' ' or pf[i][j] == 'x':
|
|
temp = []
|
|
o = 0
|
|
if o < symbolCount:
|
|
temp = []
|
|
o = 0
|
|
|
|
for j in range(_size):
|
|
for i in range(_size):
|
|
if pf[i][j] == 'x':
|
|
temp.append([i, j])
|
|
x += 1
|
|
if x == symbolCount:
|
|
winLine = temp
|
|
return 1
|
|
if pf[i][j] == ' ' or pf[i][j] == 'o':
|
|
temp = []
|
|
x = 0
|
|
if x < symbolCount:
|
|
temp = []
|
|
x = 0
|
|
|
|
# diagonal checking
|
|
temp = []
|
|
o = x = 0
|
|
for i in range(len(pf)):
|
|
for j in range(len(pf)):
|
|
if pf[i][j] == 'o':
|
|
try:
|
|
if pf[i+1][j-1] == 'o':
|
|
for k in range(symbolCount):
|
|
if pf[i+k][j-k] == 'o' and 0 <= i+k < _size and 0 <= j-k < _size:
|
|
temp.append([i+k, j-k])
|
|
o += 1
|
|
else:
|
|
winLine = []
|
|
temp = []
|
|
o = 0
|
|
except IndexError:
|
|
pass
|
|
try:
|
|
if pf[i+1][j+1] == 'o':
|
|
for k in range(symbolCount):
|
|
if pf[i+k][j+k] == 'o' and 0 <= i+k < _size and 0 <= j+k < _size:
|
|
temp.append([i+k, j+k])
|
|
o += 1
|
|
else:
|
|
winLine = []
|
|
temp = []
|
|
o = 0
|
|
except IndexError:
|
|
pass
|
|
if o == symbolCount:
|
|
winLine = temp
|
|
return 0
|
|
elif o < symbolCount:
|
|
winLine = []
|
|
temp = []
|
|
o = 0
|
|
|
|
if pf[i][j] == 'x':
|
|
try:
|
|
if pf[i+1][j-1] == 'x':
|
|
for k in range(symbolCount):
|
|
if pf[i+k][j-k] == 'x' and 0 <= i+k < _size and 0 <= j-k < _size:
|
|
temp.append([i+k, j-k])
|
|
x += 1
|
|
else:
|
|
winLine = []
|
|
temp = []
|
|
x = 0
|
|
except IndexError:
|
|
pass
|
|
try:
|
|
if pf[i+1][j+1] == 'x':
|
|
for k in range(symbolCount):
|
|
if pf[i+k][j+k] == 'x' and 0 <= i+k < _size and 0 <= j+k < _size:
|
|
temp.append([i+k, j+k])
|
|
x += 1
|
|
else:
|
|
winLine = []
|
|
temp = []
|
|
x = 0
|
|
except IndexError:
|
|
pass
|
|
if x == symbolCount:
|
|
winLine = temp
|
|
return 1
|
|
elif x < symbolCount:
|
|
winLine = []
|
|
temp = []
|
|
x = 0
|
|
|
|
# draw checking
|
|
# 2 is for when a draw occurs
|
|
count = 0
|
|
for i in range(len(pf)):
|
|
for j in range(len(pf)):
|
|
if pf[i][j] == 'o' or pf[i][j] == 'x':
|
|
count += 1
|
|
if count == len(pf)*len(pf):
|
|
return 2
|
|
|
|
def cursorLeft():
|
|
global cursorX, cursorY, pf, name1, name2, error
|
|
|
|
if inBound(cursorX-1) == 1 and inBound(cursorY):
|
|
cursorX -= 1
|
|
|
|
gameState(pf, name1, name2)
|
|
|
|
def cursorRight():
|
|
global cursorX, cursorY, pf, name1, name2, error
|
|
|
|
if inBound(cursorX+1) == 1 and inBound(cursorY) == 1:
|
|
cursorX += 1
|
|
|
|
gameState(pf, name1, name2)
|
|
|
|
def cursorUp():
|
|
global cursorX, cursorY, pf, name1, name2, error
|
|
|
|
if inBound(cursorX) == 1 and inBound(cursorY-1) == 1:
|
|
cursorY -= 1
|
|
|
|
gameState(pf, name1, name2)
|
|
|
|
def cursorDown():
|
|
global cursorX, cursorY, pf, name1, name2, error
|
|
|
|
if inBound(cursorX) == 1 and inBound(cursorY+1) == 1:
|
|
cursorY += 1
|
|
|
|
gameState(pf, name1, name2)
|
|
|
|
def putSymbol():
|
|
global cursorX, cursorY, turn, pf, name1, name2
|
|
|
|
if turn == 0 and pf[cursorY][cursorX] == ' ':
|
|
pf[cursorY][cursorX] = 'o'
|
|
turn = 1
|
|
elif turn == 1 and pf[cursorY][cursorX] == ' ':
|
|
pf[cursorY][cursorX] = 'x'
|
|
turn = 0
|
|
clearScreen()
|
|
gameState(pf, name1, name2)
|
|
|
|
def keys(key):
|
|
global listener
|
|
|
|
if key == keyboard.Key.left:
|
|
cursorLeft()
|
|
if key == keyboard.Key.right:
|
|
cursorRight()
|
|
if key == keyboard.Key.up:
|
|
cursorUp()
|
|
if key == keyboard.Key.down:
|
|
cursorDown()
|
|
if key == keyboard.Key.space:
|
|
listener.stop()
|
|
putSymbol()
|
|
|
|
# logic for a round of the game
|
|
def gameRound(pf, name1, name2):
|
|
global end, turn, error, listener
|
|
|
|
# 1 -> clear the screen
|
|
#
|
|
# cross-platform (Windows, Unix-like OSs)
|
|
clearScreen()
|
|
|
|
# 2 -> print the current state of the game
|
|
gameState(pf, name1, name2)
|
|
|
|
# 3 -> allow for one player or the other to move with arrow keys and to put the symbol to the selected place by pressing space
|
|
#
|
|
# only if there isn't another symbol present and the coords are valid
|
|
# the 'o' or 'x' is determined by the turn variable
|
|
# - if it is 0, then an 'o' is placed, if it is 1, an 'x' is placed instead
|
|
# the rows and columns are marked 1, 2 and 3, not 0, 1 and 2 as the indexes
|
|
# for better user experience
|
|
with keyboard.Listener(
|
|
on_press=keys,
|
|
supress=True
|
|
) as listener:
|
|
listener.join()
|
|
|
|
# 5 -> check if the game should end (if someone won)
|
|
won = check(pf)
|
|
|
|
if won == 0:
|
|
# 5.1 -> if yes -> end the game
|
|
gameState(pf, name1, name2, 1)
|
|
gameState(pf, name1, name2, 2)
|
|
end = 1
|
|
# 5.2 -> if no -> jump to step 1
|
|
|
|
if won == 1:
|
|
gameState(pf, name1, name2, 1)
|
|
gameState(pf, name1, name2, 3)
|
|
end = 1
|
|
|
|
if won == 2:
|
|
gameState(pf, name1, name2, 4)
|
|
end = 1
|
|
|
|
# driver code
|
|
if __name__ == "__main__":
|
|
init() |