From 209fdc7f0581f4f2d940cc2166626fa46c0e47d6 Mon Sep 17 00:00:00 2001 From: oloturia <5429234+oloturia@users.noreply.github.com> Date: Sat, 30 Apr 2022 04:19:26 +0200 Subject: [PATCH 1/5] connect4 engine working --- .gitignore | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4_engine.py | 108 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 .gitignore create mode 100644 4_engine.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28fb9d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,132 @@ +TOKEN +DAMA.SECRET + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/4_engine.py b/4_engine.py new file mode 100644 index 0000000..2a48e72 --- /dev/null +++ b/4_engine.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 + +def drawChequerboard(status,players=[],toprow="1234567"): + print(toprow) + for row in status: + for cell in row: + if cell == 0: + print("░",end="") + else: + try: + print(players[cell],end="") + except IndexError: + print(str(cell),end="") + print(" ") + +def initChequerboard(cols=7,rows=6): + board = list() + for row in range(rows): + board.append([0]*7) + return board + +def dropChip(board,move_str,player): + failure = (False,0) + try: + move = int(move_str)-1 + except ValueError: + return failure + if move < 0 or move > 6: + return failure + free_space = -1 + for row in board: + if row[move] != 0: + break + else: + free_space += 1 + if free_space == -1: + return failure + board[free_space][move] = player + return board, checkFour(board,free_space,move) + +def checkFour(board,row,col): + offset_pairs = ( ((0,1),(0,-1)) , ((1,0),(-1,0)) , ((1,1),(-1,-1)) , ((-1,1),(1,-1)) ) + points = 0 + for offset in offset_pairs: + count = 0 + count += checkSide(board,row,col,offset[0]) + count += checkSide(board,row,col,offset[1]) + if count >= 4: + points += 1 + return points + + +def checkSide(board,row,col,incs): + incY,incX = incs + count = 0 + player = board[row][col] + offsetX = 0 + offsetY = 0 + while count < 4: + try: + if board[row+offsetX][col+offsetY] == player: + count += 1 + offsetX += incX + offsetY += incY + else: + break + except IndexError: + break + return count + + +if __name__ == "__main__": + board = initChequerboard() + match = True + player = 1 + points_1 = 0 + points_2 = 0 + while match: + drawChequerboard(board) + move = input("Player "+str(player)+" turn:") + if move == "q": + print("quitting") + quit() + valid, point = dropChip(board,move,player) + if not(valid): + print("Invalid move") + else: + board = valid + if player == 2: + points_2 += point + player = 1 + else: + points_1 += point + player = 2 + match = False + for row in board: + for cell in row: + if cell == 0: + match = True + print("Match over") + print("Player 1 scored "+str(points_1)+" points") + print("Player 2 scored "+str(points_2)+" points") + if points_1 > points_2: + print("Player 1 won!") + elif points_2 > points_2: + print("Player 2 won!") + else: + print("Draw!") From 97298be316ade0536abac3a8dc2702f7c7add546 Mon Sep 17 00:00:00 2001 From: oloturia <5429234+oloturia@users.noreply.github.com> Date: Sat, 30 Apr 2022 19:01:35 +0200 Subject: [PATCH 2/5] todo: checkFour --- 4_engine.py | 52 +++++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/4_engine.py b/4_engine.py index 2a48e72..1148e81 100644 --- a/4_engine.py +++ b/4_engine.py @@ -39,35 +39,27 @@ def dropChip(board,move_str,player): return board, checkFour(board,free_space,move) def checkFour(board,row,col): - offset_pairs = ( ((0,1),(0,-1)) , ((1,0),(-1,0)) , ((1,1),(-1,-1)) , ((-1,1),(1,-1)) ) - points = 0 - for offset in offset_pairs: - count = 0 - count += checkSide(board,row,col,offset[0]) - count += checkSide(board,row,col,offset[1]) - if count >= 4: - points += 1 - return points - - -def checkSide(board,row,col,incs): - incY,incX = incs - count = 0 + sumOr = lambda a,b : (a[0]+b[0], a[1]+b[1]) + orients = { "N":(1,0),"S":(-1,0),"E":(0,1),"W":(0,-1) } player = board[row][col] - offsetX = 0 - offsetY = 0 - while count < 4: - try: - if board[row+offsetX][col+offsetY] == player: - count += 1 - offsetX += incX - offsetY += incY - else: - break - except IndexError: - break - return count - + offX = 0 + offY = 0 + #TODO + + + return points + +def checkPly(board,row,col,player): + if row < 0 or col < 0: + return False + try: + if board[row][col] == player: + return True + else: + return False + except IndexError: + return False + if __name__ == "__main__": board = initChequerboard() @@ -77,6 +69,8 @@ if __name__ == "__main__": points_2 = 0 while match: drawChequerboard(board) + print("Player 1:"+str(points_1)) + print("Player 2:"+str(points_2)) move = input("Player "+str(player)+" turn:") if move == "q": print("quitting") @@ -102,7 +96,7 @@ if __name__ == "__main__": print("Player 2 scored "+str(points_2)+" points") if points_1 > points_2: print("Player 1 won!") - elif points_2 > points_2: + elif points_2 > points_1: print("Player 2 won!") else: print("Draw!") From 79986232ed1847f21ae91e17a48afaad8017a3ae Mon Sep 17 00:00:00 2001 From: oloturia <5429234+oloturia@users.noreply.github.com> Date: Sun, 1 May 2022 04:06:09 +0200 Subject: [PATCH 3/5] abstracted game from the lobby, half implemented 4connect --- .gitignore | 4 + damastodon.py | 166 +++++++++++++++++++--------------- 4_engine.py => four_engine.py | 45 +++++++-- login.py | 2 +- 4 files changed, 139 insertions(+), 78 deletions(-) rename 4_engine.py => four_engine.py (63%) mode change 100644 => 100755 diff --git a/.gitignore b/.gitignore index 28fb9d8..d3e18e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ TOKEN +TOKEN.LOCAL +TOKEN.OLD DAMA.SECRET +DAMA.SECRET.LOCAL +DAMA.SECRET.OLD # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/damastodon.py b/damastodon.py index f01642c..06a92c1 100755 --- a/damastodon.py +++ b/damastodon.py @@ -2,6 +2,7 @@ from mastodon import Mastodon import draughts_engine as dama +import four_engine import login import pickle import random @@ -15,7 +16,10 @@ import logging api_url = sys.argv[1] save_position = "/tmp/" CLEANR = re.compile('<.*?>') -botname = "@damastodon " +#botname = "@damastodon " +botname = "@dama " + +available_games = ("draughts","conn4") #board appearence frstrow = "🇻1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣\n" @@ -27,6 +31,9 @@ black_norm="◾ " black_knight="⚫ " empty="🟦 " +#conn4 +conn4row = "🇻1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣\n" + #logging config logging.basicConfig(filename="/tmp/dama.log",level=logging.DEBUG) @@ -34,6 +41,71 @@ def cleanHTML(raw): cleanText = re.sub(CLEANR, '',raw) return cleanText +def lobby(notification,content,account,extension): + try: + challenged = notification["status"]["mentions"][1]["acct"] #If there isn't another account cited, then the request is malformed + except: + mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") + return + + if challenged == account: #The user tried to challenge him/herself + mastodon.status_post("Hello @"+account+"\n You can't challenge yourself",visibility="direct") + return + + if content.split(" ")[0].lower() == extension: #Challenge someone, check if a savegame is already existing + if os.path.exists(save_position+challenged+"."+extension): + mastodon.status_post("Hello @"+account+" \n the user you challenged is already playing a match",visibility="direct") + return + else: + with open(save_position+account+"."+extension,"wb") as f: #The request is valid, writes a savegame with the first element as False, that marks that the game isn't started yet + pickle.dump("WAIT",f) + ident = mastodon.status_post("Hello @"+challenged+" \n@"+account+" challenged you to a match of "+extension+"! Answer \n @"+account+" OK "+extension.upper()+"\n to accept the challenge or \n@"+account+" NO "+extension.upper()+"\n to cancel.",visibility="direct") + return + elif content.split(" ")[1].lower() == "ok" and content.split(" ")[2].lower() == extension: #The opponent accepted the match + try: + challenger = notification["status"]["mentions"][1]["acct"] + except: + mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Somehow, the opponent answered with only one account + return + try: + with open(save_position+challenger+"."+extension,"rb") as f: + start = pickle.load(f) + except FileNotFoundError: + mastodon.status_post("Hello @"+account+" \n unfortunately, your savegame is corrupted or missing",visibility="direct") #The file has moved or corrupted + logging.warning("%s file not found",account) + return + if start != "WAIT": + mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #The user that challenged us is playing a game with someone else + return + os.symlink(save_position+challenger+"."+extension,save_position+account+"."+extension) #Linked the two savegames together + + + if extension == "draughts": #Draughts init + board = dama.init_board() + mastodon.status_post("◾: @"+account+" ◽: @"+challenger+" \nturn ◽\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") + elif extension == "conn4": #Conn4 init + board = four_engine.initChequerboard() + mastodon.status_post(four_engine.drawChequerboard(board,players=[white_knight,black_knight],space=empty,toprow=conn4row),visibility="direct") + with open(save_position+account+"."+extension,"wb") as f: + pickle.dump("START",f) #Now the game has started + pickle.dump("@"+account,f) + pickle.dump("@"+challenger,f) + pickle.dump(False,f) + pickle.dump(board,f) + return + elif content.split(" ")[1].lower() == "no" and content.split(" ")[2].lower == extension: #The opponent refused the game + try: + challenger = notification["status"]["mentions"][1]["acct"] + except: + mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Only one user in the message + return + os.remove(save_position+challenger+"."+extension) + mastodon.status_post("@"+account+" you cancelled the challenge from @"+challenger,visibility="direct") #Game is cancelled, free to challenge someone else + return + else: + mastodon.status_post("Hello @"+account+" \nI can't understand your command or you're not in a match.\nWrite HELP to see the list of available commands.",visibility="direct") #Every other command for the lobby ends here + return + def check_message(notification): account = notification["account"]["acct"] try: @@ -41,75 +113,21 @@ def check_message(notification): except KeyError: return content = content[len(botname):] - saves = os.listdir(save_position) + #saves = os.listdir(save_position) if "help" in content.lower(): #Ask for help - mastodon.status_post("Hello @"+account+" \nchallenge an user by writing to me\nCHALL \nEx. \"CHALL @someone@mastdn.inst.wxyz\"\nThe challenger takes WHITE and begins the match.\nFor movements and jumps, write the coords separated by spaces.\nEx. \"A4 B5\" (normal movement) or \"A4 C6 D8\" (double jump)\nQUIT ends the match.\nCommands are NOT case sensitive.",visibility="direct") + mastodon.status_post("Hello @"+account+" \nchallenge an user in a game of draughts by writing to me\nDRAUGHTS \nEx. \"DRAUGHTS @someone@mastdn.inst.wxyz\"\nThe challenger takes WHITE and begins the match.\nFor movements and jumps, write the coords separated by spaces.\nEx. \"A4 B5\" (normal movement) or \"A4 C6 D8\" (double jump)\nQUIT ends the match.\nCommands are NOT case sensitive.\nTo challenge someone in a game of Connect 4 write CONN4 .",visibility="direct") return - if not os.path.exists(save_position+account): #If there is no a savegame file, then lobby mode is activated - try: - challenged = notification["status"]["mentions"][1]["acct"] #If there isn't another account cited, then the request is malformed - except: - mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") - return - if content[:5].lower() == "chall": #Challenge someone, check if a savegame is already existing - file_save_white = [sv for sv in saves if account in sv] - file_save_black = [sv for sv in saves if challenged in sv] - if len(file_save_white) > 0: #We are playing a match - mastodon.status_post("Hello @"+account+" \n you're already playing a match",visibility="direct") - return - elif len(file_save_black) > 0: #Our opponent is already playing with someone - mastodon.status_post("Hello @"+account+" \n the user you challenged is already playing a match",visibility="direct") - return - else: - with open(save_position+account,"wb") as f: #The request is valid, writes a savegame with the first element as False, that marks that the game isn't started yet - pickle.dump("WAIT",f) - ident = mastodon.status_post("Hello @"+challenged+" \n@"+account+" challenged you to a match of draughts! Answer \n@"+account+" OK\n to accept the chellenge or \n@"+account+" NO\n to cancel.",visibility="direct") - return - elif content.split(" ")[1].lower() == "ok": #The opponent accepted the match - try: - challenger = notification["status"]["mentions"][1]["acct"] - except: - mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Somehow, the opponent answered with only one account - return - try: - with open(save_position+challenger,"rb") as f: - start = pickle.load(f) - except FileNotFoundError: - mastodon.status_post("Hello @"+account+" \n unfortunately, your savegame is corrupted or missing",visibility="direct") #The file has moved or corrupted - logging.warning("%s file not found",account) - return - if start != "WAIT": - mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #The user that challenged us is playing a game with someone else - return - os.symlink(save_position+challenger,save_position+account) #Linked the two savegames together - board = dama.init_board() - with open(save_position+account,"wb") as f: - pickle.dump("START",f) #Now the game has started - pickle.dump("@"+account,f) - pickle.dump("@"+challenger,f) - pickle.dump(False,f) - pickle.dump(board,f) - mastodon.status_post("◾: @"+account+" ◽: @"+challenger+" \nturn ◽\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") - return - elif content.split(" ")[1].lower() == "no": #The opponent refused the game - try: - challenger = notification["status"]["mentions"][1]["acct"] - except: - mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Only one user in the message - return - os.remove(save_position+challenger) - mastodon.status_post("@"+account+" you cancelled the challenge from @"+challenger,visibility="direct") #Game is cancelled, free to challenge someone else - return - else: - mastodon.status_post("Hello @"+account+" \nI can't understand your command or you're not in a match.\nWrite HELP to see the list of available commands.",visibility="direct") #Every other command for the lobby ends here - return - else: #We are in a game, so movements are parsed and lobby commands are disabled - with open(save_position+account,"rb") as f: + + + + #Draughts + if os.path.exists(save_position+account+".draughts"): #We are in a game, so movements are parsed and lobby commands are disabled + with open(save_position+account+".draughts","rb") as f: try: start = pickle.load(f) except EOFError: # Something went very wrong, file is corrupt? mastodon.status_post("Hello @"+account+" \n unfortunately, your savegame is corrupted or missing",visibility="direct") #The file has been moved or is corrupted - os.remove(save_position+account) + os.remove(save_position+account+".draughts") logging.warning("%s file corrupted",account) return if start: #The game is started, load other parameters @@ -119,14 +137,14 @@ def check_message(notification): board = pickle.load(f) if (start == "WAIT"): #The game is not started yet if "quit" in content.lower(): #Game withdrawn - os.remove(save_position+account) + os.remove(save_position+account+".draughts") mastodon.status_post("Hello @"+account+" \nthe challenge has been withdrawn.",visibility="direct") else: #Lobby is disabled if a challenge request is active mastodon.status_post("Hello @"+account+" \nyou have already challenged someone, type QUIT to withdraw,",visibility="direct") return if "quit" in content.lower(): #The game is quitted - os.remove(save_position+black[1:]) - os.remove(save_position+white[1:]) + os.remove(save_position+black[1:]+".draughts") + os.remove(save_position+white[1:]+".draughts") mastodon.status_post(black+" "+white+" the match was cancelled.",visibility="direct") return if (black == "@"+account and turn == 1) or (white == "@"+account and turn == 0): #Check if the turn is right @@ -135,7 +153,7 @@ def check_message(notification): mastodon.status_post("@"+account+" \nInvalid move.",visibility="direct") return else: - with open(save_position+account,"wb") as f: #Save the updated board + with open(save_position+account+".draughts","wb") as f: #Save the updated board pickle.dump("START",f) turn = not turn pickle.dump(black,f) @@ -155,13 +173,19 @@ def check_message(notification): winner_t = "WHITE" else: winner_t = "BLACK" - os.remove(save_position+black[1:]) - os.remove(save_position+white[1:]) + os.remove(save_position+black[1:]+".draughts") + os.remove(save_position+white[1:]+".draughts") mastodon.status_post("◾: "+black+" ◽: "+white+"\n"+winner_t+" WINS!\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") return else: #We moved in a wrong turn mastodon.status_post("@"+account+" \nIt's not your turn.",visibility="direct") - return + return + + #Lobby + for game in available_games: + if game in content.lower(): + if not os.path.exists(save_position+account+"."+game): #If there is no a savegame file, then lobby mode is activated + lobby(notification,content,account,game) if __name__ == "__main__": if api_url[:4] != "http": diff --git a/4_engine.py b/four_engine.py old mode 100644 new mode 100755 similarity index 63% rename from 4_engine.py rename to four_engine.py index 1148e81..80fcffd --- a/4_engine.py +++ b/four_engine.py @@ -1,11 +1,11 @@ #!/usr/bin/python3 -def drawChequerboard(status,players=[],toprow="1234567"): +def drawChequerboard(status,players=[],space="░",toprow="1234567"): print(toprow) for row in status: for cell in row: if cell == 0: - print("░",end="") + print(space,end="") else: try: print(players[cell],end="") @@ -42,13 +42,45 @@ def checkFour(board,row,col): sumOr = lambda a,b : (a[0]+b[0], a[1]+b[1]) orients = { "N":(1,0),"S":(-1,0),"E":(0,1),"W":(0,-1) } player = board[row][col] - offX = 0 - offY = 0 - #TODO - + points = 0 + count = 0 + count += countTokens(board,orients["N"],row,col) + count += countTokens(board,orients["S"],row,col) + if count >= 3: + points +=1 + count = 0 + count += countTokens(board,orients["E"],row,col) + count += countTokens(board,orients["W"],row,col) + if count >= 3: + points +=1 + count = 0 + count += countTokens(board,sumOr( orients["N"],orients["E"] ),row,col ) + count += countTokens(board,sumOr( orients["S"],orients["W"] ),row,col ) + if count >= 3: + points +=1 + count = 0 + count += countTokens(board,sumOr( orients["N"],orients["W"] ),row,col ) + count += countTokens(board,sumOr( orients["S"],orients["E"] ),row,col ) + if count >= 3: + points +=1 + return points +def countTokens(board,check,row,col): + player = board[row][col] + offX = check[0] + offY = check[1] + count = 0 + while True: + if row+offY < 0 or row+offY >= len(board[row])-1 or col+offX < 0 or col+offX > len(board) or (board[row+offY][col+offX] != player) : + break + else: + count += 1 + offX += check[0] + offY += check[1] + return count + def checkPly(board,row,col,player): if row < 0 or col < 0: return False @@ -92,6 +124,7 @@ if __name__ == "__main__": if cell == 0: match = True print("Match over") + drawChequerboard(board) print("Player 1 scored "+str(points_1)+" points") print("Player 2 scored "+str(points_2)+" points") if points_1 > points_2: diff --git a/login.py b/login.py index 1f857ac..39c34ba 100644 --- a/login.py +++ b/login.py @@ -6,7 +6,7 @@ fileDir = fileDir +"/" def login(url): mastodon = Mastodon( - access_token = fileDir+"DAMA.SECRET", + access_token = fileDir+"DAMA.SECRET.LOCAL", api_base_url = url ) return mastodon From 959472157d44e7ef47654f7fe6ad52704a23057b Mon Sep 17 00:00:00 2001 From: oloturia <5429234+oloturia@users.noreply.github.com> Date: Mon, 2 May 2022 02:41:46 +0200 Subject: [PATCH 4/5] connec4 implemented --- damastodon.py | 101 +++++++++++++++++++++++++++++++++++-------------- four_engine.py | 20 ++++++---- 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/damastodon.py b/damastodon.py index 06a92c1..8aca967 100755 --- a/damastodon.py +++ b/damastodon.py @@ -32,7 +32,7 @@ black_knight="⚫ " empty="🟦 " #conn4 -conn4row = "🇻1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣\n" +conn4row = "1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣\n" #logging config logging.basicConfig(filename="/tmp/dama.log",level=logging.DEBUG) @@ -59,7 +59,7 @@ def lobby(notification,content,account,extension): else: with open(save_position+account+"."+extension,"wb") as f: #The request is valid, writes a savegame with the first element as False, that marks that the game isn't started yet pickle.dump("WAIT",f) - ident = mastodon.status_post("Hello @"+challenged+" \n@"+account+" challenged you to a match of "+extension+"! Answer \n @"+account+" OK "+extension.upper()+"\n to accept the challenge or \n@"+account+" NO "+extension.upper()+"\n to cancel.",visibility="direct") + ident = mastodon.status_post("Hello @"+challenged+" \n@"+account+" challenged you to a match of "+extension+"! Reply \n @"+account+" OK "+extension.upper()+"\n to accept the challenge or \n@"+account+" NO "+extension.upper()+"\n to cancel.",visibility="direct") return elif content.split(" ")[1].lower() == "ok" and content.split(" ")[2].lower() == extension: #The opponent accepted the match try: @@ -85,8 +85,8 @@ def lobby(notification,content,account,extension): mastodon.status_post("◾: @"+account+" ◽: @"+challenger+" \nturn ◽\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") elif extension == "conn4": #Conn4 init board = four_engine.initChequerboard() - mastodon.status_post(four_engine.drawChequerboard(board,players=[white_knight,black_knight],space=empty,toprow=conn4row),visibility="direct") - with open(save_position+account+"."+extension,"wb") as f: + mastodon.status_post("⚪: @"+account+" \n⚫: @"+challenger+" \nturn ⚪\n"+four_engine.drawChequerboard(board,players=[white_knight,black_knight],space=empty,toprow=conn4row),visibility="direct") + with open(save_position+account+"."+extension,"wb") as f: #Writing the file with the status of the game pickle.dump("START",f) #Now the game has started pickle.dump("@"+account,f) pickle.dump("@"+challenger,f) @@ -106,6 +106,36 @@ def lobby(notification,content,account,extension): mastodon.status_post("Hello @"+account+" \nI can't understand your command or you're not in a match.\nWrite HELP to see the list of available commands.",visibility="direct") #Every other command for the lobby ends here return +def load_status(account,extension,content): + with open(save_position+account+"."+extension,"rb") as f: #Open the status file - extension is the type of the game + try: + start = pickle.load(f) + except EOFError: #Something wrong happened + mastodon.status_post("Hello @"+account+" \n unfortunately your savegame is corrupted or missing. The game is cancelled.",visibility="direct") + os.remove(save_position+account+"."+extension) + logging.warning("% file corrupted or missing",account+"."+extension) + return False + + player_1 = pickle.load(f) #Read status from file + player_2 = pickle.load(f) + turn = pickle.load(f) + board = pickle.load(f) + + if (start == "WAIT"): #The game is not started yet + if "quit" in content.lower(): #Game withdrawn + os.remove(save_position+account+"."+extension) + mastodon.status_post("Hello @"+account+" \nthe challenge has been withdrawn.",visibility="direct") + else: #Lobby is disabled if a challenge request is active + mastodon.status_post("Hello @"+account+" \nyou have already challenged someone, type QUIT to withdraw,",visibility="direct") + return False + if "quit" in content.lower(): #The game is quitted + os.remove(save_position+player_1[1:]+"."+extension) + os.remove(save_position+player_2[1:]+"."+extension) + mastodon.status_post(player_2+" "+player_1+" the match was cancelled.",visibility="direct") + return False + + return True,player_1,player_2,turn,board + def check_message(notification): account = notification["account"]["acct"] try: @@ -113,40 +143,53 @@ def check_message(notification): except KeyError: return content = content[len(botname):] - #saves = os.listdir(save_position) if "help" in content.lower(): #Ask for help mastodon.status_post("Hello @"+account+" \nchallenge an user in a game of draughts by writing to me\nDRAUGHTS \nEx. \"DRAUGHTS @someone@mastdn.inst.wxyz\"\nThe challenger takes WHITE and begins the match.\nFor movements and jumps, write the coords separated by spaces.\nEx. \"A4 B5\" (normal movement) or \"A4 C6 D8\" (double jump)\nQUIT ends the match.\nCommands are NOT case sensitive.\nTo challenge someone in a game of Connect 4 write CONN4 .",visibility="direct") return + #Conn4 + if os.path.exists(save_position+account+".conn4"): + start,player_1,player_2,turn,board = load_status(account,"conn4",content) + if not(start): + return + + if (player_2 == "@"+account and turn == 1) or (player_1 == "@"+account and turn == 0): + board,win = four_engine.dropChip(board,content.lower()[-1],turn+1) + if not(board): + mastodon.status_post("@"+account+" \nInvalid move.",visibility="direct") + return + else: + with open(save_position+account+".conn4","wb") as f: + pickle.dump("START",f) + turn = not turn + pickle.dump(player_1,f) + pickle.dump(player_2,f) + pickle.dump(turn,f) + pickle.dump(board,f) + if turn == 0: #the first is the current turn, the second is the last turn + colour = (white_knight,black_knight) + else: + colour = (black_knight,white_knight) + if win == 0: + mastodon.status_post("⚪: "+player_1+" \n⚫: "+player_2+" \nturn "+colour[0]+"\n"+four_engine.drawChequerboard(board,players=[white_knight,black_knight],space=empty,toprow=conn4row),visibility="direct") + return + else: #Someone won! + mastodon.status_post("⚪: "+player_1+" \n⚫: "+player_2+" \n"+colour[1]+" WINS!\n"+four_engine.drawChequerboard(board,players=[white_knight,black_knight],space=empty,toprow=conn4row),visibility="direct") + os.remove(save_position+player_1[1:]+".conn4") + os.remove(save_position+player_2[1:]+".conn4") + return + else: #We moved in a wrong turn + mastodon.status_post("@"+account+" \nIt's not your turn.",visibility="direct") + return #Draughts if os.path.exists(save_position+account+".draughts"): #We are in a game, so movements are parsed and lobby commands are disabled - with open(save_position+account+".draughts","rb") as f: - try: - start = pickle.load(f) - except EOFError: # Something went very wrong, file is corrupt? - mastodon.status_post("Hello @"+account+" \n unfortunately, your savegame is corrupted or missing",visibility="direct") #The file has been moved or is corrupted - os.remove(save_position+account+".draughts") - logging.warning("%s file corrupted",account) - return - if start: #The game is started, load other parameters - black = pickle.load(f) - white = pickle.load(f) - turn = pickle.load(f) - board = pickle.load(f) - if (start == "WAIT"): #The game is not started yet - if "quit" in content.lower(): #Game withdrawn - os.remove(save_position+account+".draughts") - mastodon.status_post("Hello @"+account+" \nthe challenge has been withdrawn.",visibility="direct") - else: #Lobby is disabled if a challenge request is active - mastodon.status_post("Hello @"+account+" \nyou have already challenged someone, type QUIT to withdraw,",visibility="direct") - return - if "quit" in content.lower(): #The game is quitted - os.remove(save_position+black[1:]+".draughts") - os.remove(save_position+white[1:]+".draughts") - mastodon.status_post(black+" "+white+" the match was cancelled.",visibility="direct") + start,black,white,turn,board = load_status(account,"conn4") + if not(start): return + + if (black == "@"+account and turn == 1) or (white == "@"+account and turn == 0): #Check if the turn is right board = dama.valid_move(content.lower(),turn,board,inversion=True) #Function dama.valid_move parses the input for couples of letter and number if board == -1: #We made an invalid move diff --git a/four_engine.py b/four_engine.py index 80fcffd..8b57044 100755 --- a/four_engine.py +++ b/four_engine.py @@ -1,17 +1,20 @@ #!/usr/bin/python3 def drawChequerboard(status,players=[],space="░",toprow="1234567"): - print(toprow) + bstr = "" + bstr += toprow+"\n" for row in status: for cell in row: if cell == 0: - print(space,end="") + bstr += space else: try: - print(players[cell],end="") + bstr += players[cell-1] except IndexError: - print(str(cell),end="") - print(" ") + bstr += str(cell) + + bstr += "\n" + return bstr def initChequerboard(cols=7,rows=6): board = list() @@ -25,7 +28,7 @@ def dropChip(board,move_str,player): move = int(move_str)-1 except ValueError: return failure - if move < 0 or move > 6: + if move < 0 or move >= len(board[0]): return failure free_space = -1 for row in board: @@ -100,7 +103,8 @@ if __name__ == "__main__": points_1 = 0 points_2 = 0 while match: - drawChequerboard(board) + print( drawChequerboard(board) ) + print("Player 1:"+str(points_1)) print("Player 2:"+str(points_2)) move = input("Player "+str(player)+" turn:") @@ -124,7 +128,7 @@ if __name__ == "__main__": if cell == 0: match = True print("Match over") - drawChequerboard(board) + print( drawChequerboard(board) ) print("Player 1 scored "+str(points_1)+" points") print("Player 2 scored "+str(points_2)+" points") if points_1 > points_2: From f0fb84321bd11830f6e2e65fdb696d05b35ede16 Mon Sep 17 00:00:00 2001 From: oloturia <5429234+oloturia@users.noreply.github.com> Date: Mon, 2 May 2022 02:51:28 +0200 Subject: [PATCH 5/5] bugfix --- damastodon.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/damastodon.py b/damastodon.py index 8aca967..fe6d11a 100755 --- a/damastodon.py +++ b/damastodon.py @@ -82,7 +82,7 @@ def lobby(notification,content,account,extension): if extension == "draughts": #Draughts init board = dama.init_board() - mastodon.status_post("◾: @"+account+" ◽: @"+challenger+" \nturn ◽\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") + mastodon.status_post("◾: @"+account+" \n◽: @"+challenger+" \nturn ◽\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") elif extension == "conn4": #Conn4 init board = four_engine.initChequerboard() mastodon.status_post("⚪: @"+account+" \n⚫: @"+challenger+" \nturn ⚪\n"+four_engine.drawChequerboard(board,players=[white_knight,black_knight],space=empty,toprow=conn4row),visibility="direct") @@ -185,7 +185,7 @@ def check_message(notification): #Draughts if os.path.exists(save_position+account+".draughts"): #We are in a game, so movements are parsed and lobby commands are disabled - start,black,white,turn,board = load_status(account,"conn4") + start,black,white,turn,board = load_status(account,"draughts",content) if not(start): return @@ -209,7 +209,7 @@ def check_message(notification): colour = "◾" winner = dama.checkWin(board) #Check for winner if winner == (False,False): #No one is winning yet - mastodon.status_post("◾: "+black+" ◽: "+white+" \nturn "+colour+"\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") + mastodon.status_post("◾: "+black+" \n◽: "+white+" \nturn "+colour+"\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") return else: #Someone won if winner == (True,False): @@ -218,7 +218,7 @@ def check_message(notification): winner_t = "BLACK" os.remove(save_position+black[1:]+".draughts") os.remove(save_position+white[1:]+".draughts") - mastodon.status_post("◾: "+black+" ◽: "+white+"\n"+winner_t+" WINS!\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") + mastodon.status_post("◾: "+black+" \n◽: "+white+"\n"+winner_t+" WINS!\n"+dama.draw_checkerboard(board,space,white_norm,white_knight,black_norm,black_knight,empty,column,frstrow),visibility="direct") return else: #We moved in a wrong turn mastodon.status_post("@"+account+" \nIt's not your turn.",visibility="direct")