From 028e7524f9213055f1aaff31760fc98dc8abf4bf Mon Sep 17 00:00:00 2001 From: ReneCareenium Date: Mon, 30 Aug 2021 19:16:38 +0200 Subject: [PATCH] second iteration --- .gitignore | 1 + __pycache__/sgfengine.cpython-39.pyc | Bin 2178 -> 2252 bytes rengobot.py | 171 +++++++++++++++++++++------ sgfengine.py | 16 ++- state.txt | 2 +- 5 files changed, 150 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 22fae77..9964e89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ token.txt +state.txt *.sgf *.png diff --git a/__pycache__/sgfengine.cpython-39.pyc b/__pycache__/sgfengine.cpython-39.pyc index 201d8df1afc68eda0322d2359e6efe6abfba4a1b..b9f878f25a4a782451a1596b45ba3ecc37e5f8f9 100644 GIT binary patch delta 1047 zcmYjQzi$*r6n<}ZcJ}A_?$-95e}J(NBp8ErI1(Z!B|=K%A_YQ8VK{~2%q$kz+`@PP zVa;-pkEBQwNBa-#mI+mqK1D>yKaeeGQn^SI(UUi86nR$rX1;H}_ukBXdk5_|?bTtU z;RB7|gUdsoR(~6Ca##ee)4TWi&#RZZz7*1Vi33Nv(t8O5SK>FY68yGX;CSOMKan_1 zYg73PlY5H+q@YpaJQP^M1XGY~f^(2@&84-&)diaQHNq()9hdMunr>Dc#hK(#qV&e`;IvdrZ4KO#PcS zr8~g`lpa-Ul`wt^hp4#pM@$A;gHqdcgw}qfTU3M+>muz<7_D(gn}_)PCPEe=?9WiC z(3IMQ+vfPui)iE@&pqe?O5}qI>{%VA>soUFF^9Hq!?{tD`>*Fl1n3b*4bj*bpFa-@V(?|te&a+wEf!txL?^@$xz9G{gC**fjvrOyk3dB$!_vQRhCMg zRThDPsAcN|v6E~KN9z^F`Dt+mzsf%o-D?~Ly2vqPEo`w6UDjbPGK{c=ODrPn;u6jg z4zYtBQmqlEuI2~gpZ>DB(rF6&Z1rnpZn+Yw(2(>m0u`8W-h_`#Xi})SN|IEq<-a&* waXo+I%%AF+z1jam$Ebvg_wH|xHga?qojx%uxREcr7sXYh>*qJ!9`ir>4~E$8Bme*a delta 982 zcmYjPJxmlq6n<~^Z|?tx<2Vj-5Pzb=fj~k+j0N$pGBy^&xdj86m4wqm z8_#Sj13k$2OOsptOjGdit7Gs#r%=_No_ujslFNMRxWHXaV007!_&J3A z5)q{|(LGqwt>vEQTFDbR12f>|!-7gg7|U#go2-ZOHsz#s4wWuap}fk611cqwv3E^y zY9n*fY{U?hBVY%62Pj|bK#^6>I>rvIvsC~YJ01_Bm0JI zKkFN$H0;3@$SRSX5GZQ&xNzb!1X(h&yVUhG2D: play a move. For example, `$play Q16`\n\n'+ + '$play : play a move. For example, `$play Q16`\n'+ + '$edit : if you make a mistake in your move, you have 5 minutes to correct it with this command\n\n'+ + '$sgf: get the sgf file of the game\n'+ + '$board: shows the current board\n'+ '$queue: get the queue of players\n\n'+ + '$newgame : starts a game in this channel (admin only!)\n'+ '$resign : resigns the game in this channel (admin only!)' ) @@ -53,7 +59,6 @@ async def play(ctx, arg): # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() filter_state= [i for i in range(len(state)) if state[i][0] == channel_id] if not filter_state: @@ -62,30 +67,54 @@ async def play(ctx, arg): i= filter_state[0] - if state[i][1] == queue and user.id not in state[i][4]: + if state[i][1] == "queue" and user.id not in state[i][4]: await ctx.send("Player hasn't joined yet! Join us with `$join`") return - if state[i][1] == queue and user.id!= state[i][4][0]: + if state[i][1] == "queue" and len(state[i][4]) 0 and state[i][2][-1] == user.id: + await ctx.send("No two consecutive moves per player!") + return + + for j in range(len(state[i][2])): + if (state[i][2][j] == user.id and + datetime.now() - datetime.strptime(state[i][3][j],format) < min_time_player): + await ctx.send("At most one move per player per day!") + return + + + if state[i][3] != [] and datetime.now()-datetime.strptime(state[i][3][-1],format) timedelta(minutes=5): + await ctx.send("You cannot edit this move!") + return + + legal_moves=[chr(col+ord('A')-1)+str(row) for col in range(1,21) if col!=9 for row in range(1,20)] + legal_moves+=[chr(col+ord('a')-1)+str(row) for col in range(1,21) if col!=9 for row in range(1,20)] + if arg not in legal_moves: + await ctx.send("I don't understand the move! Please input it in the format `$play Q16`") + return + + try: + sgfengine.play_move(str(channel_id), arg, user.name, True) + except ValueError as e: + await ctx.send(str(e)) + return + + file = discord.File(str(ctx.channel.id)+".png") + if state[i][1]=="queue": + next_player=(await bot.fetch_user(state[i][4][0])) + await ctx.send(file=file, content="{}'s turn! ⭐".format(next_player.name)) + else: + await ctx.send(file=file) + + with open("state.txt", "w") as f: f.write(repr(state)) + +@bot.command() +async def board(ctx): + channel_id= ctx.channel.id + user = ctx.author + + with open("state.txt") as f: state = ast.literal_eval(f.read()) + + filter_state= [i for i in range(len(state)) if state[i][0] == channel_id] + if not filter_state: + await ctx.send("No active game in this channel!") + return + + i= filter_state[0] + + file = discord.File(str(ctx.channel.id)+".png") + if state[i][1]=="queue": + next_player=(await bot.fetch_user(state[i][4][0])) + await ctx.send(file=file, content="{}'s turn! ⭐".format(next_player.name)) + else: + await ctx.send(file=file) @bot.command() async def join(ctx): @@ -104,7 +202,6 @@ async def join(ctx): # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() filter_state= [i for i in range(len(state)) if state[i][0] == channel_id] if not filter_state: @@ -126,7 +223,6 @@ async def join(ctx): await ctx.send("User joined!") with open("state.txt", "w") as f: f.write(repr(state)) - f.close() @bot.command() async def queue(ctx): @@ -134,7 +230,6 @@ async def queue(ctx): # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() filter_state= [i for i in range(len(state)) if state[i][0] == channel_id] if not filter_state: @@ -169,7 +264,6 @@ async def leave(ctx): # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() filter_state= [i for i in range(len(state)) if state[i][0] == channel_id] if not filter_state: @@ -191,7 +285,6 @@ async def leave(ctx): await ctx.send("User left :(") with open("state.txt", "w") as f: f.write(repr(state)) - f.close() @bot.command() async def newgame(ctx, arg): @@ -208,14 +301,13 @@ async def newgame(ctx, arg): # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() if ctx.channel.id in [ ch for (ch,_,_,_,_) in state]: await ctx.send("A game is already active in this channel!") return sgfengine.new_game(str(ctx.channel.id)) - state.append((ctx.channel.id, arg, None, None, [])) + state.append((ctx.channel.id, arg, [], [], [])) file = discord.File(str(ctx.channel.id)+".png") if arg=="queue": @@ -224,7 +316,6 @@ async def newgame(ctx, arg): await ctx.send(file=file, content="A new game has started! Play with `$play `") with open("state.txt", "w") as f: f.write(repr(state)) - f.close() @bot.command() async def resign(ctx, arg): @@ -240,7 +331,6 @@ async def resign(ctx, arg): return with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() now=datetime.now() file_name= "rengo_"+now.strftime("%Y_%m_%d_%H_%M_%S_")+ctx.channel.name+".sgf" #remove the hour minute and second later @@ -253,7 +343,6 @@ async def resign(ctx, arg): state = [s for s in state if s[0]!=channel_id] with open("state.txt", "w") as f: f.write(repr(state)) - f.close() async def background_task(): await bot.wait_until_ready() @@ -263,21 +352,37 @@ async def background_task(): while not bot.is_closed(): try: - print("ping") - await asyncio.sleep(1000) - # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) - f.close() + #print(state) #TODO find who has to move, skip players accordingly, notify if any has to move + for i in range(len(state)): + if state[i][3] == [] or state[i][1]!="queue": continue - with open("state.txt") as f: f.write(repr(state)) - f.close() + channel_id= state[i][0] + channel= bot.get_channel(channel_id) + + last_time= datetime.strptime(state[i][3][-1],format) + time_left= last_time + time_to_skip-datetime.now() + if time_left < time_to_skip * 2/3: # Probably remove? Depends on how passive aggressive it is + next_user = await bot.fetch_user(state[i][4][0]) + await channel.send("{}'s turn!".format(next_user.mention))#, time_left.total_seconds()/3600) ) + if time_left < timedelta(): + state[i][3][-1]= datetime.strftime(datetime.now(),format) + state[i][2][-1]= None + user_id= state[i][4][0] + state[i][4].pop(0) + state[i][4].append(user_id) + next_player=(await bot.fetch_user(state[i][4][0])) + await channel.send(content="{}'s turn! ⭐".format(next_player.mention)) + #Should I output the board too? + + with open("state.txt", "w") as f: f.write(repr(state)) + await asyncio.sleep(10) except ConnectionResetError: print("Connection error") bot.loop.create_task(background_task()) bot.run(token) - diff --git a/sgfengine.py b/sgfengine.py index 11a4165..62e1cba 100644 --- a/sgfengine.py +++ b/sgfengine.py @@ -16,7 +16,7 @@ def new_game(channel_id): # Could be an illegal move, or maybe I don't understand the message # outputs to .png -def play_move(channel_id, messagestr, player): +def play_move(channel_id, messagestr, player, overwrite=False): thecol= ord(messagestr[0].lower()) - ord('a') if thecol>8: thecol-=1 # Go boards don't have an I column!! @@ -29,6 +29,12 @@ def play_move(channel_id, messagestr, player): koban=None node= game.get_last_node() board, moves= sgf_moves.get_setup_and_moves(game) + if overwrite: + node2= node.parent + node.delete() + node= node2 + moves= moves[:-1] + for (colour, (row, col)) in moves: koban=board.play(row,col,colour) @@ -39,18 +45,16 @@ def play_move(channel_id, messagestr, player): board2= board.copy() try: - board2.play(therow, thecol, colour) + koban2=board2.play(therow, thecol, colour) except ValueError as e: raise ValueError("Illegal move1!") - if ascii_boards.render_board(board)== ascii_boards.render_board(board2): + if board2.get(therow,thecol) == None: raise ValueError("Illegal move2!") - #print(moves) - node2= node.new_child() node2.set(("B" if colour =='b' else "W"), (therow,thecol)) - if koban is not None: node2.set("SQ", [koban]) + if koban2 is not None: node2.set("SQ", [koban2]) node2.set("CR", [(therow, thecol)]) node2.set("C", player) # I think this would be fun for the review if node.has_property("CR"): node.unset("CR") diff --git a/state.txt b/state.txt index 1c59b50..614665e 100644 --- a/state.txt +++ b/state.txt @@ -1 +1 @@ -[(881537306073387091, 'queue', None, None, [477895596141707264])] \ No newline at end of file +[(870604751354613770, 'random', [477895596141707264], ['2021_08_30_19_03_29_444139'], [])] \ No newline at end of file