Creating a bot¶
To create a reconchess bot, extend the reconchess.Player
base class and implement the abstract methods that it has. In
order to use the reconchess scripts, the main python file you pass into the scripts must contain exactly 1 sub class of
reconchess.Player
.
For more information on the API see the reconchess.Player
section on the reconchess API page.
Example bot: Random bot¶
The random bot takes random actions each turn, for both sensing and moving. It only really implements the
reconchess.Player.choose_sense()
and reconchess.Player.choose_move()
methods.
import random
from reconchess import *
class RandomBot(Player):
def handle_game_start(self, color: Color, board: chess.Board, opponent_name: str):
pass
def handle_opponent_move_result(self, captured_my_piece: bool, capture_square: Optional[Square]):
pass
def choose_sense(self, sense_actions: List[Square], move_actions: List[chess.Move], seconds_left: float) -> \
Optional[Square]:
return random.choice(sense_actions)
def handle_sense_result(self, sense_result: List[Tuple[Square, Optional[chess.Piece]]]):
pass
def choose_move(self, move_actions: List[chess.Move], seconds_left: float) -> Optional[chess.Move]:
return random.choice(move_actions + [None])
def handle_move_result(self, requested_move: Optional[chess.Move], taken_move: Optional[chess.Move],
captured_opponent_piece: bool, capture_square: Optional[Square]):
pass
def handle_game_end(self, winner_color: Optional[Color], win_reason: Optional[WinReason],
game_history: GameHistory):
pass
Example bot: Trout bot¶
The trout bot is a baseline that you can test your bot against. It keeps track of a single chess.Board
and uses the Stockfish engine to make a move. When it gets information back from the game,
it naively applies that information to its chess.Board
.
NOTE You will need to download Stockfish and create an environment variable called STOCKFISH_EXECUTABLE that has the path to the Stockfish executable to use TroutBot.
import chess.engine
import random
from reconchess import *
import os
STOCKFISH_ENV_VAR = 'STOCKFISH_EXECUTABLE'
class TroutBot(Player):
"""
TroutBot uses the Stockfish chess engine to choose moves. In order to run TroutBot you'll need to download
Stockfish from https://stockfishchess.org/download/ and create an environment variable called STOCKFISH_EXECUTABLE
that is the path to the downloaded Stockfish executable.
"""
def __init__(self):
self.board = None
self.color = None
self.my_piece_captured_square = None
# make sure stockfish environment variable exists
if STOCKFISH_ENV_VAR not in os.environ:
raise KeyError(
'TroutBot requires an environment variable called "{}" pointing to the Stockfish executable'.format(
STOCKFISH_ENV_VAR))
# make sure there is actually a file
stockfish_path = os.environ[STOCKFISH_ENV_VAR]
if not os.path.exists(stockfish_path):
raise ValueError('No stockfish executable found at "{}"'.format(stockfish_path))
# initialize the stockfish engine
self.engine = chess.engine.SimpleEngine.popen_uci(stockfish_path, setpgrp=True)
def handle_game_start(self, color: Color, board: chess.Board, opponent_name: str):
self.board = board
self.color = color
def handle_opponent_move_result(self, captured_my_piece: bool, capture_square: Optional[Square]):
# if the opponent captured our piece, remove it from our board.
self.my_piece_captured_square = capture_square
if captured_my_piece:
self.board.remove_piece_at(capture_square)
def choose_sense(self, sense_actions: List[Square], move_actions: List[chess.Move], seconds_left: float) -> \
Optional[Square]:
# if our piece was just captured, sense where it was captured
if self.my_piece_captured_square:
return self.my_piece_captured_square
# if we might capture a piece when we move, sense where the capture will occur
future_move = self.choose_move(move_actions, seconds_left)
if future_move is not None and self.board.piece_at(future_move.to_square) is not None:
return future_move.to_square
# otherwise, just randomly choose a sense action, but don't sense on a square where our pieces are located
for square, piece in self.board.piece_map().items():
if piece.color == self.color:
sense_actions.remove(square)
return random.choice(sense_actions)
def handle_sense_result(self, sense_result: List[Tuple[Square, Optional[chess.Piece]]]):
# add the pieces in the sense result to our board
for square, piece in sense_result:
self.board.set_piece_at(square, piece)
def choose_move(self, move_actions: List[chess.Move], seconds_left: float) -> Optional[chess.Move]:
# if we might be able to take the king, try to
enemy_king_square = self.board.king(not self.color)
if enemy_king_square:
# if there are any ally pieces that can take king, execute one of those moves
enemy_king_attackers = self.board.attackers(self.color, enemy_king_square)
if enemy_king_attackers:
attacker_square = enemy_king_attackers.pop()
return chess.Move(attacker_square, enemy_king_square)
# otherwise, try to move with the stockfish chess engine
try:
self.board.turn = self.color
self.board.clear_stack()
result = self.engine.play(self.board, chess.engine.Limit(time=0.5))
return result.move
except chess.engine.EngineTerminatedError:
print('Stockfish Engine died')
except chess.engine.EngineError:
print('Stockfish Engine bad state at "{}"'.format(self.board.fen()))
# if all else fails, pass
return None
def handle_move_result(self, requested_move: Optional[chess.Move], taken_move: Optional[chess.Move],
captured_opponent_piece: bool, capture_square: Optional[Square]):
# if a move was executed, apply it to our board
if taken_move is not None:
self.board.push(taken_move)
def handle_game_end(self, winner_color: Optional[Color], win_reason: Optional[WinReason],
game_history: GameHistory):
try:
# if the engine is already terminated then this call will throw an exception
self.engine.quit()
except chess.engine.EngineTerminatedError:
pass