# 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 <= 2: clearScreen() print("Size of the playfield (n*n):", size, "(changed to 3*3)") size = 3 # 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("Number of symbols inline needed to win:", symbolCount) F_printOneMoreTime = 0 # 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()