├── README.md └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # Albin 2 | 3 | Discord bot for playing blindfold chess written in Python. 4 | Albin takes the moves from chat and pushes them on the board without showing it. 5 | 6 | # TODO 7 | 8 | - Takeback command 9 | - Help command 10 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import chess 2 | import random 3 | import chess.engine 4 | import discord 5 | from discord.ext import commands 6 | 7 | from config import TOKEN 8 | 9 | bot = commands.Bot(command_prefix="&") 10 | 11 | isBoard = False 12 | game = [] 13 | 14 | 15 | @bot.command() 16 | async def start(ctx, user: discord.Member = None, variation: str = None): 17 | global board 18 | global botIsPlayer 19 | global isBoard 20 | global white_id 21 | global black_id 22 | 23 | botIsPlayer = False 24 | 25 | bot_id = await bot.application_info() 26 | if not user: 27 | await ctx.channel.send("Tag a user to play.") 28 | return 29 | 30 | elif user.id == bot_id.id: 31 | botIsPlayer = True 32 | 33 | white_id = ctx.author.id 34 | black_id = user.id 35 | 36 | isBoard = True 37 | 38 | if not variation: 39 | board = chess.Board() 40 | elif variation == "960" or variation == "chess960": 41 | r = random.randint(0, 959) 42 | board = chess.Board().from_chess960_pos(r) 43 | await ctx.channel.send("```" + str(board) + "```") 44 | else: 45 | await ctx.channel.send("Unknown chess variant.") 46 | return 47 | 48 | await ctx.channel.send("Board was created.") 49 | await ctx.channel.send(f"White: {ctx.message.author.mention}\nBlack: {user.mention}") 50 | 51 | 52 | @start.error 53 | async def start_error(ctx, error): 54 | if isinstance(error, commands.BadArgument): 55 | await ctx.channel.send("User not found.") 56 | 57 | 58 | @bot.command() 59 | async def move(ctx, arg): 60 | if isBoard: 61 | try: 62 | author_id = ctx.author.id 63 | 64 | if board.turn and author_id == white_id: # board.turn returns True if 65 | # white to move and false otherwise 66 | pass 67 | elif not board.turn and author_id == black_id: 68 | pass 69 | else: 70 | await ctx.channel.send("You can't move for other player.") 71 | return 72 | 73 | board.push_san(arg) 74 | 75 | except ValueError: 76 | await ctx.channel.send("Invalid move.") 77 | return 78 | 79 | if board.outcome(claim_draw=True) != None: 80 | s = str(board.outcome(claim_draw=True).termination) 81 | s = s.split(".") 82 | 83 | await ctx.channel.send(s[-1].replace("_", " ")) 84 | await end(ctx) 85 | 86 | elif board.is_check(): 87 | await ctx.channel.send("Check.") 88 | else: 89 | await ctx.message.add_reaction("✅") 90 | 91 | game.append(arg) 92 | 93 | if botIsPlayer: 94 | engine = chess.engine.SimpleEngine.popen_uci("stockfish") 95 | # 0.1 because maybe the player will have a tiny chance, if stockfish doesn't do the *perfect* moves 96 | limit = chess.engine.Limit(time=0.1) 97 | move = engine.play(board, limit) 98 | if move.move is None: 99 | if move.resigned: 100 | await ctx.channel.send("Engine resigned; Congratulations!") 101 | await end(ctx) 102 | return 103 | 104 | elif move.draw_offered: 105 | await ctx.channel.send("Engine offered you a draw, do you accept? (This isn't implemented yet :/)") 106 | await end(ctx) 107 | return 108 | 109 | move_normal_notation = board.san(move.move) 110 | await ctx.channel.send(move_normal_notation) 111 | board.push(move.move) 112 | game.append(move_normal_notation) 113 | 114 | if board.is_game_over(): 115 | await ctx.channel.send(board.outcome(claim_draw=True)) 116 | await end(ctx) 117 | elif board.is_check(): 118 | await ctx.channel.send("Check.") 119 | 120 | else: 121 | await ctx.channel.send("Board wasn't created. Use &start to create.") 122 | 123 | 124 | @bot.command() 125 | async def log(ctx): 126 | if len(game) == 0: 127 | await ctx.channel.send("No piece was moved yet.") 128 | return 129 | 130 | result = "" 131 | 132 | n = 1 133 | for i in range(len(game)): 134 | if i % 2: 135 | result += " " + game[i] + "\n" 136 | continue 137 | else: 138 | result += str(n) + ". " + game[i] 139 | 140 | n += 1 141 | 142 | await ctx.channel.send("```" + str(result) + "```") 143 | 144 | 145 | @bot.command() 146 | async def end(ctx): 147 | global board 148 | global game 149 | global botIsPlayer 150 | 151 | await ctx.channel.send("```" + str(board) + "```") 152 | await ctx.channel.send("Game ended. Board is reset.") 153 | 154 | game = [] 155 | board.reset() 156 | botIsPlayer = False 157 | 158 | 159 | bot.run(TOKEN) 160 | --------------------------------------------------------------------------------