class TestStockfish(unittest.TestCase): def setUp(self): self.stockfish = Stockfish() def test_get_best_move(self): best_move = self.stockfish.get_best_move() self.assertIn(best_move, ( 'e2e4', 'g1f3', )) self.stockfish.set_position(['e2e4', 'e7e6']) best_move = self.stockfish.get_best_move() self.assertIn(best_move, ( 'd2d4', 'g1f3', )) # mate self.stockfish.set_position(['f2f3', 'e7e5', 'g2g4', 'd8h4']) self.assertFalse(self.stockfish.get_best_move()) def test_is_move_correct(self): self.assertFalse(self.stockfish.is_move_correct('e2e1')) self.assertTrue(self.stockfish.is_move_correct('a2a3')) self.stockfish.set_position(['e2e4', 'e7e6']) self.assertFalse(self.stockfish.is_move_correct('e2e1')) self.assertTrue(self.stockfish.is_move_correct('a2a3'))
class TestStockfish(unittest.TestCase): def setUp(self): self.stockfish = Stockfish() def test_get_best_move(self): best_move = self.stockfish.get_best_move() self.assertIn(best_move, ( 'e2e4', 'g1f3', 'b1c3', )) self.stockfish.set_position(['e2e4', 'e7e6']) best_move = self.stockfish.get_best_move() self.assertIn(best_move, ( 'd2d4', 'g1f3', )) # mate self.stockfish.set_position(['f2f3', 'e7e5', 'g2g4', 'd8h4']) self.assertFalse(self.stockfish.get_best_move()) def test_set_fen_position(self): self.stockfish.set_fen_position( "7r/1pr1kppb/2n1p2p/2NpP2P/5PP1/1P6/P6K/R1R2B2 w - - 1 27") self.assertTrue(self.stockfish.is_move_correct('f4f5')) self.assertFalse(self.stockfish.is_move_correct('a1c1')) def test_is_move_correct(self): self.assertFalse(self.stockfish.is_move_correct('e2e1')) self.assertTrue(self.stockfish.is_move_correct('a2a3')) self.stockfish.set_position(['e2e4', 'e7e6']) self.assertFalse(self.stockfish.is_move_correct('e2e1')) self.assertTrue(self.stockfish.is_move_correct('a2a3'))
class TestStockfish(unittest.TestCase): def setUp(self): self.stockfish = Stockfish() def test_get_best_move(self): best_move = self.stockfish.get_best_move() self.assertIn(best_move, ('e2e4', 'g1f3',)) self.stockfish.set_position(['e2e4', 'e7e6']) best_move = self.stockfish.get_best_move() self.assertIn(best_move, ('d2d4', 'g1f3',)) # mate self.stockfish.set_position(['f2f3', 'e7e5', 'g2g4', 'd8h4']) self.assertFalse(self.stockfish.get_best_move()) def test_is_move_correct(self): self.assertFalse(self.stockfish.is_move_correct('e2e1')) self.assertTrue(self.stockfish.is_move_correct('a2a3')) self.stockfish.set_position(['e2e4', 'e7e6']) self.assertFalse(self.stockfish.is_move_correct('e2e1')) self.assertTrue(self.stockfish.is_move_correct('a2a3'))
class ChessGame: def __init__(self, fen): """ constructor taking initial FEN position""" self.d_engine = Stockfish() self.setFen(fen) def setFen(self, fen): """ set board in fen notation """ self.d_engine.set_fen(fen) def move(self, algMove): """ move in long algebraic notation, send to stockfish """ if self.d_engine.is_move_correct(algMove): print("correct") def bestMove(self): return self.d_engine.get_best_move()
class Game: def __init__(self): self.engine = Stockfish() self.board = Board() def sync_engine(self): self.engine.set_position(self.board.move_list) def get_best_move(self) -> str: return self.engine.get_best_move() def is_move_correct(self, move: str) -> bool: return self.engine.is_move_correct(move) def make_move(self, move) -> bool: if self.is_move_correct(move): self.board.make_move(move) self.sync_engine() return True else: return False def make_best_move(self): self.make_move(self.get_best_move())
""" Before running do pip install stockfish to get the stockfish python interface """ from stockfish import Stockfish stockfish = Stockfish() stockfish.set_position(['e2e4', 'e7e6']) stockfish.depth = 20 print(stockfish.get_best_move()) print(stockfish.is_move_correct('a2a3'))
def get_move_from_board_states(board_state_before, board_state_after, previous_moves, chess_engine: Stockfish): """ Determine the move given the state before the move and the state after the move. """ # Determine the sids in the prev state that differ in the current state changed_before = [ sid for sid in board_state_before if sid not in board_state_after or board_state_after[sid] != board_state_before[sid] ] # Determine the sids in the current state that differ from the prev state changed_after = [ sid for sid in board_state_after if sid not in board_state_before or board_state_after[sid] != board_state_before[sid] ] # Check for promoted pieces promoted_piece = None pieces_before = ''.join( sorted([ p for p in board_state_before.values() if p in Board.get_white_pieces() ])) pieces_after = ''.join( sorted([ p for p in board_state_after.values() if p in Board.get_white_pieces() ])) if pieces_before != pieces_after: differing_pieces = pieces_before + pieces_after for p in pieces_before: if p in pieces_after: differing_pieces.replace(p, '', 2) log.info( f"Found differing pieces between states: {differing_pieces}") potential_piece = differing_pieces.replace('p', '') if len(differing_pieces ) != 2 or 'p' not in differing_pieces or len( potential_piece) != 1: raise InvalidMove('Piece promotion is invalid.') promoted_piece = potential_piece # If no change raise exception if len(changed_before) == 0 or len(changed_after) == 0: raise NoMoveFound() # If more than one sid was changed in the after board state this could be a castling move if len(changed_after) != 1: # Construct a set of all the pieces involved in the move pieces_moved = set([board_state_before[k] for k in changed_before] + [board_state_after[k] for k in changed_after]) # Check if the move is a castling move is_castling_move = len(pieces_moved) == 2 and ( ('r' in pieces_moved and 'k' in pieces_moved) or ('R' in pieces_moved and 'K' in pieces_moved)) # If it is a castling move, filter out board state changes that are not related to the king if is_castling_move: log.info('Castling move detected.') board_state_before = { k: board_state_before[k] for k in board_state_before if (board_state_before[k] == 'k' or board_state_before[k] == 'K') } board_state_after = { k: board_state_after[k] for k in board_state_after if (board_state_after[k] == 'k' or board_state_after[k] == 'K') } changed_before = [ sid for sid in board_state_before if sid not in board_state_after or board_state_after[sid] != board_state_before[sid] ] changed_after = [ sid for sid in board_state_after if sid not in board_state_before or board_state_after[sid] != board_state_before[sid] ] else: raise InvalidMove('Move affected too many sids.') # Get the move end sid e_sid = changed_after[0] piece_moved = board_state_after[ e_sid] if promoted_piece is None else promoted_piece # Find the move start sid s_sid = None for sid in changed_before: if board_state_before[sid] == piece_moved: s_sid = sid if s_sid is None: raise InvalidMove('Could not find move start.') move = f"{s_sid}{e_sid}{promoted_piece if promoted_piece is not None else ''}" # Verify that the move is valid chess_engine.set_position(previous_moves) if not chess_engine.is_move_correct(move): raise InvalidMove(f"Move {move} is invalid.") return move
class Game: # handles game logic + interacts with stockfish def __init__(self): abs_path = os.path.dirname(__file__) # local path rel_path = 'stockfish-10-win/stockfish-10-win/stockfish_x86-64-modern.exe' self.sf = Stockfish(os.path.join( abs_path, rel_path)) # pull the engine from compiled file self.m_skill_level = 0 # skill level of engine self.m_cur_turn = 0 # 0 for player turn, 1 for computer turn self.m_in_game = False # see if we're still in game self.m_move_his = [] # move history to update position in stockfish self.num_moves = 0 # number of moves (extraneous, may remove) self.set_skill_level( self.request_skill_level()) # request a skill level for engine self.choose_side(self.request_side()) # request a starting side self.m_starting_side = 0 self.m_board = Board(self.m_cur_turn) def set_skill_level(self, d_level): # function to set a skill level self.sf.set_skill_level(d_level) self.m_skill_level = d_level def choose_side(self, d_side): # function to set a starting side self.m_cur_turn = d_side self.m_starting_side = d_side def check_for_mate( self, val ): # function periodically checks if there is a checkmate and ends game if true # we can check for checkmate by looking at what stockfish thinks is the best move # if stockfish says only 1 move is the best move, we're in check # if no move is the best move, we're in checkmate. moves = self.sf.get_best_move() mate = 0 print(moves) if moves == None: mate = 1 return mate def check_for_castle(self, start, end): r_a = 0 r_b = 0 if start == 60: # black side print('black king \n') if (start - end) == -2: # castled 'h' rook r_a = 63 r_b = 61 else: # castled 'a' rook r_a = 56 r_b = 59 elif start == 4: # white side if (start - end) == -2: # h rook r_a = 7 r_b = 5 else: r_a = 0 r_b = 3 self.m_board.change_pos(r_a, r_b) return 1 def updateBoard(self, move): # first two characters of move are start, last two are end start_pos = move[:2] ind_pos_s = (ord(start_pos[0]) - 97) + ((int(start_pos[1]) - 1) * 8) end_pos = move[2:4] ind_pos_e = (ord(end_pos[0]) - 97) + ((int(end_pos[1]) - 1) * 8) # check for special moves occuring # check if a king in its original position has moved. print('Index: ' + str(ind_pos_s) + '\n' + 'Type: ' + str(self.m_board.get_piece_at(ind_pos_s)) + '\n\n\n\n') if (self.m_board.get_piece_at(ind_pos_s) == 1): print('castling!') self.check_for_castle(ind_pos_s, ind_pos_e) self.m_board.change_pos(ind_pos_s, ind_pos_e) return 1 def endGame(self, ): # if mate is triggered, loser will be whoever's current turn it is win = 0 w_side = 0 if self.m_cur_turn == 0: # white lost w_side = 1 if self.m_cur_turn != self.m_starting_side: win = 1 else: # black lost if self.m_cur_turn != self.m_starting_side: win = 1 if win: print('you won!') else: print('you lost!') return 1 def request_side(self, ): # prompts user for choosing a starting side side_set = False side = "white" count = 0 c_side = 0 while not side_set and count < 5: count = count + 1 side = input('Which side? (type in white, black or random): ') if side == 'white' or side == 'black' or side == 'random': if side == 'black': # if black, computer goes first c_side = 1 elif side == 'white': # if white, player goes first c_side = 0 else: if (random.randint(0, 999) / 100) > 5: c_side = 1 else: c_side = 0 side_set = True elif count == 5: print("Setting to default side of white.") c_side = 0 side_set = True else: print("Invalid input" + "\n") return c_side def request_skill_level( self, ): # prompts user for inputting a skill level for engine level_set = False count = 0 while not level_set and count < 5: count = count + 1 skill = input( 'What skill level? (integers between 1 and 21 inclusive): ') if skill.isdigit() and int(skill) > 0 and int(skill) < 22: level_set = True elif count == 5: print("Setting to default of skill level 5.") skill = 5 else: print("Invalid input" + "\n") return skill def run(self, ): print('Game is now starting.') in_game = True # start game self.m_board.print_board_state() while in_game: # check if currently in checkmate (to end game) mate = self.check_for_mate() if mate: # game is now over break turn = "your" if self.m_cur_turn == 0 else "the computer's" print("It is now " + turn + " turn." + "\n") if self.m_cur_turn == 0: move_done = 0 while not move_done: move = input('Enter your move: ' + '\n').strip() # first check if valid move format (letter + number + letter + number) valid = re.search(rule, move) if valid and self.sf.is_move_correct(move): # valid move syntax and able to be made self.m_move_his.append(move) self.sf.set_position(self.m_move_his) print("You entered the move: " + move + '\n') move_done = 1 else: print("Invalid move, please enter a different move." + '\n') self.m_cur_turn = 1 else: # computer's turn move = self.sf.get_best_move() print("The computer moves: " + move + '\n') self.m_move_his.append(move) self.sf.set_position(self.m_move_his) self.m_cur_turn = 0 self.num_moves = self.num_moves + 1 self.updateBoard(move) self.m_board.print_board_state() self.endGame()