From 8aebbeb7116120da281f646daf5b78b460dfbdbd Mon Sep 17 00:00:00 2001 From: ReneCareenium Date: Thu, 2 Sep 2021 21:50:18 +0200 Subject: [PATCH] second version now with teams and some bugs fixead --- __pycache__/sgfengine.cpython-39.pyc | Bin 2252 -> 0 bytes rengobot.py | 196 ++++++++++++++++++--------- sgfengine.py | 13 +- 3 files changed, 140 insertions(+), 69 deletions(-) delete mode 100644 __pycache__/sgfengine.cpython-39.pyc diff --git a/__pycache__/sgfengine.cpython-39.pyc b/__pycache__/sgfengine.cpython-39.pyc deleted file mode 100644 index 4be26bac607300abd125e219bcbb074de5b64ddc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2252 zcmZt{O>Y}TbY^yTy}Pz!$4Qg4g~~mEYDzvD5JIS`5T%s>DS}p_bOl%&&%|-E-ZitE zMvi7VkiwOoINL|~2^=`^Gv>gBQ?7_pFYw+tEeY*d`)20(ea-uf>-CVpdZV=(hj986 zFK3qnm-pezZvhZQ(1b*|vxE*=#D-4f40*(dZsbxT9Kj!x$P=OPMCCD!D#8~5V0=OE zlTP&nWRR^6H%{K)G2V6-E0LQ3F5O}FNam&%=e@z8JNu(Yk^FR_9X@=w;mZ>M1({Gm zr?jL6(_qgsXg_8@UMZPo6GzhtpOPu1a z+9>U}SJQS9=S92YD+b)gzYh=X$3tm&Hj=4v57nTM@P4TVaWcrIaeGMy8_ROz<>S1N z!;WLZ-fo6_GNY8h~MdmP}ig)c@T>+&z)1qIRI@{8d3DLJMEIQ^Ke zkdkXIoPDmY!Oq`>I|PIZF39)nBG3sAG;ZNQkifOVgCMm_ui$&`#M9t*3{zQoAxxul zVe%$EhDngp*Of^yC1p^Ald7&v>6few%c^$NZ#vkkl{Fnq>PV|=WuU8&sui>YD5@Wl zuPy;~9lp7;p~K0Xp3@CopE{-g&F|XPe1~a&pQ*o!hVYN+cT@x*tvMx+$P=o#2va7i zWgR%@!Gh*dOE*CV@L3u24@kPkNzr&h$DdMCwkSDT1W7fN^ng4>>s#Np(r~tKkPQO- zE##jvLG~HwNPxbM^q_YEbkuZhvZ(8#YXAK~1Ym*HxWlFl-Z(GF0R_HakWMu8e4ko8 z-kP%)ox#lO8CNj5AevL;`vdeW;TdEhwqdb!|Tb*51Z45N&J~ zR}KBZ&_hGFkLEr~64{Rvi@$O8{CyJ$A4Kf_XA!%7&(PZn<*NVzrgrDDaWW;0n`DPl z8E23ds)m1Jy{akz6Yi*N*xeo%(4M%Gu}}>_NA()aG=m<1brUThE|D0gPJKE@IrU)Yv*0)ewmdjIpBKNv1)3L3FK9r_HXx^ZUg>HVrUlFx!J%1vEnFx{E<8wRC`r)6M8y^hO7}I#c7>__f`*@7ropx~Yw7dy z@V9I+sLSw-h~2xR+Hg>>0ytvV@7a{@)c%*Z>NTW!9RcPmq;8fKY7Ot_bH;c|VsJa2 zO&hg|)WOamk=+zV)qi=RHjo*o&Y7qJb3W*&cLAGY))5F32GgUjK=ABfc@MB1dbbnU z6pbn{l7@pMnUxrZ%odMw@q|Oj#!?~I8D&dj{R>)g B4D0{^ diff --git a/rengobot.py b/rengobot.py index f33c79a..7803acc 100644 --- a/rengobot.py +++ b/rengobot.py @@ -17,7 +17,7 @@ bot = commands.Bot(command_prefix='$', help_command=None) min_time_player= timedelta(seconds=1) # in random games, min time between same player plays (default days=1) time_to_skip= timedelta(days=1) # in queue games, how much time to wait for the next move -min_players = 3 +min_players = 2 # People who can start and resign games :O # Later we might replace this with checking for a role. @@ -27,12 +27,15 @@ admins=[ 756220448118669463, # Young Sun 477895596141707264 # René ] +white_stone= "<:white_stone:882731089548939314>" +black_stone= "<:black_stone:882730888453046342>" + with open("token.txt") as f: token = f.readlines()[0] # Get your own token and put it in token.txt format="%Y_%m_%d_%H_%M_%S_%f" -# The state is a list of tuples (channel_id, "queue"/"random", last_players, last_times, [player_id]) +# The state is a list of tuples (channel_id, "queue"/"random", last_players, last_times, [black_queue, white_queue]) @bot.command() async def help(ctx): @@ -57,6 +60,7 @@ async def help(ctx): async def play(ctx, arg): channel_id= ctx.channel.id user = ctx.author + guild= ctx.guild # lowest effort serialization with open("state.txt") as f: state = ast.literal_eval(f.read()) @@ -68,15 +72,18 @@ 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][0]+state[i][4][1]: await ctx.send("Player hasn't joined yet! Join us with `$join`") return - if state[i][1] == "queue" and len(state[i][4]) timedelta(minutes=5): + if len(state[i][2])==0 or state[i][2][-1] != user.id or datetime.now()-datetime.strptime(state[i][3][-1],format) > timedelta(minutes=5): await ctx.send("You cannot edit this move!") return @@ -161,15 +167,15 @@ async def edit(ctx, arg): #literally play but with less things return try: - sgfengine.play_move(str(channel_id), arg, user.name, True) + sgfengine.play_move(str(channel_id), arg, user.display_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)) + next_player=(await guild.fetch_member(state[i][4][colour][0])) + await ctx.send(file=file, content="{}'s turn! ⭐".format(next_player.display_name)) else: await ctx.send(file=file) @@ -179,6 +185,7 @@ async def edit(ctx, arg): #literally play but with less things async def board(ctx): channel_id= ctx.channel.id user = ctx.author + guild= ctx.guild with open("state.txt") as f: state = ast.literal_eval(f.read()) @@ -188,11 +195,17 @@ async def board(ctx): return i= filter_state[0] + colour= sgfengine.next_colour(str(channel_id)) + + os.system("sgf-render --style fancy -o "+str(channel_id)+".png -n last "+str(channel_id)+".sgf") 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)) + if len(state[i][4][colour]) > 0: + next_player=(await guild.fetch_member(state[i][4][colour][0])) + await ctx.send(file=file, content="{}'s turn! ⭐".format(next_player.display_name)) + else: + await ctx.send(file=file, content="Waiting for players to join!") else: await ctx.send(file=file) @@ -211,7 +224,7 @@ async def join(ctx): i= filter_state[0] - if user.id in state[i][4]: + if user.id in (state[i][4][0]+state[i][4][1]): await ctx.send("Player already in this game!") return @@ -219,45 +232,13 @@ async def join(ctx): await ctx.send("This game has no queue! No need to join, just `$play` whenever you want :P") return - state[i][4].append(user.id) + colour = 0 if len(state[i][4][0])<=len(state[i][4][1]) else 1 + state[i][4][colour].append(user.id) - await ctx.send("User joined!") + await ctx.send("User {} joined Team {}!".format(user.display_name, ("Black" if colour==0 else "White"))) with open("state.txt", "w") as f: f.write(repr(state)) -@bot.command() -async def queue(ctx): - channel_id= ctx.channel.id - - # lowest effort serialization - 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] - - if state[i][1] != "queue": - await ctx.send("This game has no queue! No need to join, just `$play` whenever you want :P") - return - - output= "Player list:\n" - for j, player_id in enumerate(state[i][4]): - player_name=(await bot.fetch_user(player_id)).name - output+=str(j+1)+". "+ player_name+"\n" - - if state[i][4]==[]: - output+="Nobody yet! Join us with `$join`" - - await ctx.send(output) - -@bot.command() -async def sgf(ctx): - file = discord.File(str(ctx.channel.id)+".sgf") - await ctx.send(file=file) - @bot.command() async def leave(ctx): channel_id= ctx.channel.id @@ -273,7 +254,7 @@ async def leave(ctx): i= filter_state[0] - if user.id not in state[i][4]: + if user.id not in (state[i][4][0]+state[i][4][1]): await ctx.send("Player not in this game!") return @@ -281,12 +262,91 @@ async def leave(ctx): await ctx.send("This game has no queue! No need to leave!") return - state[i][4].remove(user.id) #TODO What happens with this and the skipping? + colour = 0 if (user.id in state[i][4][0]) else 1 + state[i][4][colour].remove(user.id) - await ctx.send("User left :(") + await ctx.send("User {} left :(".format(user.display_name)) with open("state.txt", "w") as f: f.write(repr(state)) +@bot.command() +async def queue(ctx): + channel_id= ctx.channel.id + channel= bot.get_channel(channel_id) # thonk the order + guild = channel.guild + + # lowest effort serialization + 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] + colour= sgfengine.next_colour(str(channel_id)) + + if state[i][1] != "queue": + await ctx.send("This game has no queue! No need to join, just `$play` whenever you want :P") + return + + output= "Player list:\n" + if state[i][4][0]==[] and state[i][4][1] == []: + output+="Nobody yet! Join us with `$join`" + await ctx.send(output) + return + + if state[i][4][0] == []: + for j, player_id in enumerate(state[i][4][1]): + player_name=(await guild.fetch_member(player_id)).display_name + output+=white_stone+str(j+1).rjust(3)+". "+ player_name+"\n" + output+="\n Team Black needs more members!" + await ctx.send(output) + return + + if state[i][4][1] == []: + for j, player_id in enumerate(state[i][4][0]): + player_name=(await guild.fetch_member(player_id)).display_name + output+=black_stone+str(j+1).rjust(3)+". "+ player_name+"\n" + output+="\n Team White needs more members!" + await ctx.send(output) + return + + # Which team has more members? Or in case of a tie, which team goes first? + if len(state[i][4][colour]) > len(state[i][4][1-colour]): + last_player = state[i][4][colour][-1] + else: last_player= state[i][4][1-colour][-1] + + j=1 + pointers=[0,0] + while(True): + #print(channel_id, j, pointers, colour, state[i][0], state[i][4]) + output+= white_stone if ((colour+1) % 2 ==0) else black_stone + output+= str(j).rjust(3)+". " + + player_name= (await guild.fetch_member(state[i][4][colour][pointers[colour]])).display_name + output+= player_name+"\n" + + if state[i][4][colour][pointers[colour]] == last_player: break + + pointers[colour] = (pointers[colour]+1) % len(state[i][4][colour]) + colour=1-colour + + j+=1 + + if len(state[i][4][0])! $help for command list") + game=discord.Game("multiplayer Baduk! $help for command list") await bot.change_presence(status=discord.Status.online, activity=game) while not bot.is_closed(): @@ -366,20 +426,22 @@ async def background_task(): channel_id= state[i][0] channel= bot.get_channel(channel_id) + colour = sgfengine.next_colour(str(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 and time_left > time_to_skip*2/3-timedelta(seconds=10): # Probably remove? Depends on how passive aggressive it is - next_user = await bot.fetch_user(state[i][4][0]) + next_user = await guild.fetch_member(state[i][4][colour][0]) await channel.send("{}'s turn! Time is running up!".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])) + user_id= state[i][4][colour][0] + state[i][4][colour].pop(0) + state[i][4][colour].append(user_id) + next_player=(await guild.fetch_member(state[i][4][colour][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) diff --git a/sgfengine.py b/sgfengine.py index 62e1cba..b55650c 100644 --- a/sgfengine.py +++ b/sgfengine.py @@ -14,6 +14,15 @@ def new_game(channel_id): os.system("sgf-render --style fancy -o "+channel_id+".png -n last "+channel_id+".sgf") +#0 if black to play, 1 if white to play +def next_colour(channel_id): + with open(channel_id+".sgf","rb") as f: + game = sgf.Sgf_game.from_bytes(f.read()) + f.close() + + node= game.get_last_node() + return 1 if ("B" in node.properties()) else 0 + # Could be an illegal move, or maybe I don't understand the message # outputs to .png def play_move(channel_id, messagestr, player, overwrite=False): @@ -47,10 +56,10 @@ def play_move(channel_id, messagestr, player, overwrite=False): try: koban2=board2.play(therow, thecol, colour) except ValueError as e: - raise ValueError("Illegal move1!") + raise ValueError("Illegal move! There is a stone there.") if board2.get(therow,thecol) == None: - raise ValueError("Illegal move2!") + raise ValueError("Illegal move! No self-captures allowed.") node2= node.new_child() node2.set(("B" if colour =='b' else "W"), (therow,thecol))