1
0
Fork 0
mirror of https://gitlab.com/oloturia/damastodon.git synced 2025-01-07 22:17:15 +01:00

abstracted game from the lobby, half implemented 4connect

This commit is contained in:
oloturia 2022-05-01 04:06:09 +02:00
parent 97298be316
commit 79986232ed
4 changed files with 139 additions and 78 deletions

4
.gitignore vendored
View file

@ -1,5 +1,9 @@
TOKEN TOKEN
TOKEN.LOCAL
TOKEN.OLD
DAMA.SECRET DAMA.SECRET
DAMA.SECRET.LOCAL
DAMA.SECRET.OLD
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

View file

@ -2,6 +2,7 @@
from mastodon import Mastodon from mastodon import Mastodon
import draughts_engine as dama import draughts_engine as dama
import four_engine
import login import login
import pickle import pickle
import random import random
@ -15,7 +16,10 @@ import logging
api_url = sys.argv[1] api_url = sys.argv[1]
save_position = "/tmp/" save_position = "/tmp/"
CLEANR = re.compile('<.*?>') CLEANR = re.compile('<.*?>')
botname = "@damastodon " #botname = "@damastodon "
botname = "@dama "
available_games = ("draughts","conn4")
#board appearence #board appearence
frstrow = "🇻1⃣ 2⃣ 3⃣ 4⃣ 5⃣ 6⃣ 7⃣ 8\n" frstrow = "🇻1⃣ 2⃣ 3⃣ 4⃣ 5⃣ 6⃣ 7⃣ 8\n"
@ -27,6 +31,9 @@ black_norm="◾ "
black_knight="" black_knight=""
empty="🟦 " empty="🟦 "
#conn4
conn4row = "🇻1⃣ 2⃣ 3⃣ 4⃣ 5⃣ 6⃣ 7\n"
#logging config #logging config
logging.basicConfig(filename="/tmp/dama.log",level=logging.DEBUG) logging.basicConfig(filename="/tmp/dama.log",level=logging.DEBUG)
@ -34,45 +41,34 @@ def cleanHTML(raw):
cleanText = re.sub(CLEANR, '',raw) cleanText = re.sub(CLEANR, '',raw)
return cleanText return cleanText
def check_message(notification): def lobby(notification,content,account,extension):
account = notification["account"]["acct"]
try:
content = cleanHTML(notification["status"]["content"])
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 by writing to me\nCHALL <USERNAME>\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")
return
if not os.path.exists(save_position+account): #If there is no a savegame file, then lobby mode is activated
try: try:
challenged = notification["status"]["mentions"][1]["acct"] #If there isn't another account cited, then the request is malformed challenged = notification["status"]["mentions"][1]["acct"] #If there isn't another account cited, then the request is malformed
except: except:
mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct")
return 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] if challenged == account: #The user tried to challenge him/herself
file_save_black = [sv for sv in saves if challenged in sv] mastodon.status_post("Hello @"+account+"\n You can't challenge yourself",visibility="direct")
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 return
elif len(file_save_black) > 0: #Our opponent is already playing with someone
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") mastodon.status_post("Hello @"+account+" \n the user you challenged is already playing a match",visibility="direct")
return return
else: 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 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) 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") 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 return
elif content.split(" ")[1].lower() == "ok": #The opponent accepted the match elif content.split(" ")[1].lower() == "ok" and content.split(" ")[2].lower() == extension: #The opponent accepted the match
try: try:
challenger = notification["status"]["mentions"][1]["acct"] challenger = notification["status"]["mentions"][1]["acct"]
except: except:
mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Somehow, the opponent answered with only one account mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Somehow, the opponent answered with only one account
return return
try: try:
with open(save_position+challenger,"rb") as f: with open(save_position+challenger+"."+extension,"rb") as f:
start = pickle.load(f) start = pickle.load(f)
except FileNotFoundError: except FileNotFoundError:
mastodon.status_post("Hello @"+account+" \n unfortunately, your savegame is corrupted or missing",visibility="direct") #The file has moved or corrupted mastodon.status_post("Hello @"+account+" \n unfortunately, your savegame is corrupted or missing",visibility="direct") #The file has moved or corrupted
@ -81,35 +77,57 @@ def check_message(notification):
if start != "WAIT": 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 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 return
os.symlink(save_position+challenger,save_position+account) #Linked the two savegames together os.symlink(save_position+challenger+"."+extension,save_position+account+"."+extension) #Linked the two savegames together
if extension == "draughts": #Draughts init
board = dama.init_board() board = dama.init_board()
with open(save_position+account,"wb") as 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")
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("START",f) #Now the game has started
pickle.dump("@"+account,f) pickle.dump("@"+account,f)
pickle.dump("@"+challenger,f) pickle.dump("@"+challenger,f)
pickle.dump(False,f) pickle.dump(False,f)
pickle.dump(board,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 return
elif content.split(" ")[1].lower() == "no": #The opponent refused the game elif content.split(" ")[1].lower() == "no" and content.split(" ")[2].lower == extension: #The opponent refused the game
try: try:
challenger = notification["status"]["mentions"][1]["acct"] challenger = notification["status"]["mentions"][1]["acct"]
except: except:
mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Only one user in the message mastodon.status_post("Hello @"+account+" \n your request is not valid",visibility="direct") #Only one user in the message
return return
os.remove(save_position+challenger) 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 mastodon.status_post("@"+account+" you cancelled the challenge from @"+challenger,visibility="direct") #Game is cancelled, free to challenge someone else
return return
else: 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 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 return
else: #We are in a game, so movements are parsed and lobby commands are disabled
with open(save_position+account,"rb") as f: def check_message(notification):
account = notification["account"]["acct"]
try:
content = cleanHTML(notification["status"]["content"])
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 <USERNAME>\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 <USERNAME>.",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: try:
start = pickle.load(f) start = pickle.load(f)
except EOFError: # Something went very wrong, file is corrupt? 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 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) logging.warning("%s file corrupted",account)
return return
if start: #The game is started, load other parameters if start: #The game is started, load other parameters
@ -119,14 +137,14 @@ def check_message(notification):
board = pickle.load(f) board = pickle.load(f)
if (start == "WAIT"): #The game is not started yet if (start == "WAIT"): #The game is not started yet
if "quit" in content.lower(): #Game withdrawn 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") mastodon.status_post("Hello @"+account+" \nthe challenge has been withdrawn.",visibility="direct")
else: #Lobby is disabled if a challenge request is active 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") mastodon.status_post("Hello @"+account+" \nyou have already challenged someone, type QUIT to withdraw,",visibility="direct")
return return
if "quit" in content.lower(): #The game is quitted if "quit" in content.lower(): #The game is quitted
os.remove(save_position+black[1:]) os.remove(save_position+black[1:]+".draughts")
os.remove(save_position+white[1:]) os.remove(save_position+white[1:]+".draughts")
mastodon.status_post(black+" "+white+" the match was cancelled.",visibility="direct") mastodon.status_post(black+" "+white+" the match was cancelled.",visibility="direct")
return return
if (black == "@"+account and turn == 1) or (white == "@"+account and turn == 0): #Check if the turn is right 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") mastodon.status_post("@"+account+" \nInvalid move.",visibility="direct")
return return
else: 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) pickle.dump("START",f)
turn = not turn turn = not turn
pickle.dump(black,f) pickle.dump(black,f)
@ -155,14 +173,20 @@ def check_message(notification):
winner_t = "WHITE" winner_t = "WHITE"
else: else:
winner_t = "BLACK" winner_t = "BLACK"
os.remove(save_position+black[1:]) os.remove(save_position+black[1:]+".draughts")
os.remove(save_position+white[1:]) 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+" ◽: "+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 return
else: #We moved in a wrong turn else: #We moved in a wrong turn
mastodon.status_post("@"+account+" \nIt's not your turn.",visibility="direct") 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 __name__ == "__main__":
if api_url[:4] != "http": if api_url[:4] != "http":
logging.error("Invalid address") logging.error("Invalid address")

43
4_engine.py → four_engine.py Normal file → Executable file
View file

@ -1,11 +1,11 @@
#!/usr/bin/python3 #!/usr/bin/python3
def drawChequerboard(status,players=[],toprow="1234567"): def drawChequerboard(status,players=[],space="",toprow="1234567"):
print(toprow) print(toprow)
for row in status: for row in status:
for cell in row: for cell in row:
if cell == 0: if cell == 0:
print("",end="") print(space,end="")
else: else:
try: try:
print(players[cell],end="") 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]) 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) } orients = { "N":(1,0),"S":(-1,0),"E":(0,1),"W":(0,-1) }
player = board[row][col] player = board[row][col]
offX = 0 points = 0
offY = 0 count = 0
#TODO 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 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): def checkPly(board,row,col,player):
if row < 0 or col < 0: if row < 0 or col < 0:
return False return False
@ -92,6 +124,7 @@ if __name__ == "__main__":
if cell == 0: if cell == 0:
match = True match = True
print("Match over") print("Match over")
drawChequerboard(board)
print("Player 1 scored "+str(points_1)+" points") print("Player 1 scored "+str(points_1)+" points")
print("Player 2 scored "+str(points_2)+" points") print("Player 2 scored "+str(points_2)+" points")
if points_1 > points_2: if points_1 > points_2:

View file

@ -6,7 +6,7 @@ fileDir = fileDir +"/"
def login(url): def login(url):
mastodon = Mastodon( mastodon = Mastodon(
access_token = fileDir+"DAMA.SECRET", access_token = fileDir+"DAMA.SECRET.LOCAL",
api_base_url = url api_base_url = url
) )
return mastodon return mastodon