class LocalGameMoveActionsTest(unittest.TestCase): STARTING_WHITE_PAWN_CAPTURES = [ Move(A2, B3), Move(B2, A3), Move(B2, C3), Move(C2, B3), Move(C2, D3), Move(D2, C3), Move(D2, E3), Move(E2, D3), Move(E2, F3), Move(F2, E3), Move(F2, G3), Move(G2, F3), Move(G2, H3), Move(H2, G3), ] BLACK_STARTING_PAWN_CAPTURES = [ Move(A7, B6), Move(B7, A6), Move(B7, C6), Move(C7, B6), Move(C7, D6), Move(D7, C6), Move(D7, E6), Move(E7, D6), Move(E7, F6), Move(F7, E6), Move(F7, G6), Move(G7, F6), Move(G7, H6), Move(H7, G6), ] def setUp(self): self.game = LocalGame() def test_starting_pawn_capture_moves(self): move_actions = self.game.move_actions() for move in self.STARTING_WHITE_PAWN_CAPTURES: self.assertIn(move, move_actions) self.game.board.turn = BLACK move_actions = self.game.move_actions() for move in self.BLACK_STARTING_PAWN_CAPTURES: self.assertIn(move, move_actions) def test_pass(self): self.assertNotIn(None, self.game.move_actions()) self.assertNotIn(Move.null(), self.game.move_actions()) def test_superset_fuzz(self, max_turns=500): turn = 1 while not self.game.board.is_game_over() and turn < max_turns: truth_moves = set(self.game.board.generate_pseudo_legal_moves()) recon_moves = set(self.game.move_actions()) self.assertTrue(recon_moves.issuperset(truth_moves)) self.game.board.push(random.sample(truth_moves, 1)[0]) turn += 1
def playback(game_history: GameHistory, player: Player, color: Color): game = LocalGame() opponent_name = game_history.get_white_player_name() if color == chess.WHITE: opponent_name = game_history.get_black_player_name() player.handle_game_start(color, game.board.copy(), opponent_name) game.start() turn = game_history.first_turn() while not game.is_over() and turn < game_history.last_turn(): opt_capture_square = game.opponent_move_results() if game.turn == color: player.handle_opponent_move_result(opt_capture_square is not None, opt_capture_square) sense_actions = game.sense_actions() move_actions = game.move_actions() sense = game_history.sense(turn) player_sense = player.choose_sense(sense_actions, move_actions, game.get_seconds_left()) if game.turn == color and sense != player_sense: print( 'Warning: Sense action did not match history on turn {}. Using the sense action from history.' .format(turn)) sense_result = game.sense(sense) if game.turn == color: player.handle_sense_result(sense_result) move = game_history.requested_move(turn) player_move = player.choose_move(move_actions, game.get_seconds_left()) if game.turn == color and move != player_move: print( 'Warning: Move action did not match history on turn {}. Using the move action from history.' .format(turn)) requested_move, taken_move, opt_enemy_capture_square = game.move(move) if game.turn == color: player.handle_move_result(requested_move, taken_move, opt_enemy_capture_square is not None, opt_enemy_capture_square) game.end_turn() turn = turn.next game.end() winner_color = game.get_winner_color() win_reason = game.get_win_reason() game_history = game.get_game_history() player.handle_game_end(winner_color, win_reason, game_history)
class LocalGameMoveTest(unittest.TestCase): def setUp(self): self.game = LocalGame() def test_legal_kingside_castle(self): """ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . . K . . R """ self.game.board.set_board_fen('8/8/8/8/8/8/8/R3K2R') self.game.board.set_castling_fen('KQkq') req, taken, opt_capture = self.game.move(Move(E1, G1)) self.assertEqual(req, taken) self.assertIsNone(opt_capture) self.assertEqual(self.game.board.board_fen(), '8/8/8/8/8/8/8/R4RK1') def test_legal_queenside_castle(self): """ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . . K . . R """ self.game.board.set_board_fen('8/8/8/8/8/8/8/R3K2R') self.game.board.set_castling_fen('KQkq') req, taken, opt_capture = self.game.move(Move(E1, C1)) self.assertEqual(req, taken) self.assertIsNone(opt_capture) self.assertEqual(self.game.board.board_fen(), '8/8/8/8/8/8/8/2KR3R') def test_queenside_castle_piece_between(self): """ r . P . k b n r r . . P k b n r r P . . k b n r p p . p p p p p p p . p p p p p p p . p p p p p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . P P . P P P P P P P . P P P P P P P . P P P P P R . p . K B N R R . . p K B N R R p . . K B N R """ for fen in ['r1P1kbnr/pp1ppppp/8/8/8/8/PP1PPPPP/R1p1KBNR', 'r2Pkbnr/pp1ppppp/8/8/8/8/PP1PPPPP/R2pKBNR', 'rP2kbnr/pp1ppppp/8/8/8/8/PP1PPPPP/Rp2KBNR']: self.game.board.set_board_fen(fen) self.game.board.turn = WHITE self.game.turn = WHITE req, tak, opt_capture = self.game.move(Move(E1, C1)) self.assertEqual(tak, None) self.game.board.turn = BLACK self.game.turn = BLACK req, tak, opt_capture = self.game.move(Move(E8, C8)) self.assertEqual(tak, None) def test_kingside_castle_piece_between(self): """ r n b q k P . r r n b q k . P r p p p p p . p p p p p p p . p p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . P P P P P . P P P P P P P . P P R N B Q K p . R R N B Q K . p R :return: """ for fen in ['rnbqkP1r/ppppp1pp/8/8/8/8/PPPPP1PP/RNBQKp1R', 'rnbqk1Pr/ppppp1pp/8/8/8/8/PPPPP1PP/RNBQK1pR']: self.game.board.set_board_fen(fen) self.game.board.turn = WHITE self.game.turn = WHITE req, tak, opt_capture = self.game.move(Move(E1, G1)) self.assertEqual(tak, None) self.game.board.turn = BLACK self.game.turn = BLACK req, tak, opt_capture = self.game.move(Move(E8, G8)) self.assertEqual(tak, None) def test_queenside_castle_no_rights(self): """ r . . . k . . r p p p p p p p p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . P P P P P P P P R . . . K . . R """ self.game.board.set_board_fen('r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R') self.game.board.turn = WHITE self.game.turn = WHITE for castling_fen in ['-', 'k', 'q', 'kq', 'Kk', 'Kq', 'Kkq']: self.game.board.set_castling_fen(castling_fen) with self.assertRaises(ValueError): self.game.move(Move(E1, C1)) self.game.board.turn = BLACK self.game.turn = BLACK for castling_fen in ['-', 'K', 'Q', 'KQ', 'Kk', 'Qk', 'KQk']: self.game.board.set_castling_fen(castling_fen) with self.assertRaises(ValueError): self.game.move(Move(E8, C8)) def test_kingside_castle_no_rights(self): """ r . . . k . . r p p p p p p p p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . P P P P P P P P R . . . K . . R """ self.game.board.set_board_fen('r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R') self.game.board.turn = WHITE self.game.turn = WHITE for castling_fen in ['-', 'k', 'q', 'kq', 'Qk', 'Qq', 'Qkq']: self.game.board.set_castling_fen(castling_fen) with self.assertRaises(ValueError): self.game.move(Move(E1, G1)) self.game.board.turn = BLACK self.game.turn = BLACK for castling_fen in ['-', 'K', 'Q', 'KQ', 'Kq', 'Qq', 'KQq']: self.game.board.set_castling_fen(castling_fen) with self.assertRaises(ValueError): self.game.move(Move(E8, G8)) def test_castling_into_check(self): """ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . q . . . . . . . . . . . . . . . . . . . . . K . . R """ self.game.board.set_board_fen('8/8/8/8/6q1/8/8/4K2R') self.assertFalse(self.game.board.is_check()) move = Move(E1, G1) req, taken, opt_capture = self.game.move(move) self.assertEqual(req, taken) self.assertIsNone(opt_capture) self.game.board.turn = WHITE self.assertTrue(self.game.board.is_check()) def test_castling_out_of_check(self): """ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . q . . . K . . R """ self.game.board.set_board_fen('8/8/8/8/8/8/8/q3K2R') self.assertTrue(self.game.board.is_check()) move = Move(E1, G1) req, taken, opt_capture = self.game.move(move) self.assertEqual(req, taken) self.assertIsNone(opt_capture) self.game.board.turn = WHITE self.assertFalse(self.game.board.is_check()) def test_castling_stay_in_check(self): """ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . q . . . . . . . . . . . . . K . . R """ self.game.board.set_board_fen('8/8/8/8/8/6q1/8/4K2R') self.assertTrue(self.game.board.is_check()) move = Move(E1, G1) req, taken, opt_capture = self.game.move(move) self.assertEqual(req, taken) self.assertIsNone(opt_capture) self.game.board.turn = WHITE self.assertTrue(self.game.board.is_check()) def test_en_passant_white(self): """ r n b q k b n r p . p p p p p p . . . . . . . . . . . . . . . . P p . . . . . . . . . . . . . . . P P P P P P P R N B Q K B N R """ # test that en passant captures result in the correct capture square self.game.board.set_board_fen('rnbqkbnr/p1pppppp/8/8/1p6/8/PPPPPPPP/RNBQKBNR') req, taken, opt_capture = self.game.move(Move(A2, A4)) self.assertEqual(req, taken) self.assertIsNone(opt_capture) req, taken, opt_capture = self.game.move(Move(B4, A3)) self.assertEqual(req, taken) self.assertIsNotNone(opt_capture) self.assertEqual(opt_capture, A4) def test_en_passant_black(self): """ r n b q k b n r p p p p p . p p . . . . . . . . . . . . . p P . . . . . . . . . . . . . . . . . P P P P P P . P R N B Q K B N R """ # test that en passant captures result in the correct capture square self.game.board.set_board_fen('rnbqkbnr/pppppppp/8/6P1/8/8/PPPPPP1P/RNBQKBNR') self.game.turn = BLACK self.game.board.turn = BLACK req, taken, opt_capture = self.game.move(Move(F7, F5)) self.assertEqual(req, taken) self.assertIsNone(opt_capture) req, taken, opt_capture = self.game.move(Move(G5, F6)) self.assertEqual(req, taken) self.assertIsNotNone(opt_capture) self.assertEqual(opt_capture, F5) def test_move_opponent_piece(self): # test moving opponent pieces b = Board() b.turn = BLACK for move in b.generate_pseudo_legal_moves(): with self.assertRaises(ValueError): self.game.move(move) def test_move_no_piece(self): # test a move from a square with no piece for from_square in SquareSet(BB_RANK_3 | BB_RANK_4 | BB_RANK_5 | BB_RANK_6): for to_square in SQUARES: with self.assertRaises(ValueError): m = Move(from_square, to_square) self.game.move(m) def test_move_illegal(self): for from_square in SquareSet(BB_RANK_1 | BB_RANK_2): for to_square in SQUARES: move = Move(from_square, to_square) if move not in self.game.move_actions(): with self.assertRaises(ValueError): self.game.move(move) def test_sliding_straight_capture(self): """ . . . . . . . . . . . p . . . . . . . . . . . . . p . R . p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . """ result_by_move = { Move(D5, C5): (Move(D5, C5), None), Move(D5, B5): (Move(D5, B5), B5), Move(D5, A5): (Move(D5, B5), B5), Move(D5, D6): (Move(D5, D6), None), Move(D5, D7): (Move(D5, D7), D7), Move(D5, D8): (Move(D5, D7), D7), Move(D5, E5): (Move(D5, E5), None), Move(D5, F5): (Move(D5, F5), F5), Move(D5, G5): (Move(D5, F5), F5), Move(D5, H5): (Move(D5, F5), F5), Move(D5, D4): (Move(D5, D4), None), Move(D5, D3): (Move(D5, D3), None), Move(D5, D2): (Move(D5, D2), None), Move(D5, D1): (Move(D5, D1), None), } for expected_req, (expected_taken, expected_capture) in result_by_move.items(): self.game.board.set_board_fen('8/3p4/8/1p1R1p2/8/8/8/8') self.game.board.turn = WHITE self.game.turn = WHITE req, taken, opt_capture = self.game.move(expected_req) self.assertEqual(req, expected_req) self.assertEqual(taken, expected_taken) self.assertEqual(opt_capture, expected_capture) def test_sliding_straight_into_ally(self): """ . . . . . . . . . . . p . . . . . . . . . . . . . p . R . p . . . . . . . . . . . . . . . . . . . . . P . . . . . . . . . . . . """ for move in [Move(D5, D2), Move(D5, D1)]: self.game.board.set_board_fen('8/3p4/8/1p1R1p2/8/8/3P4/8') self.game.board.turn = WHITE self.game.turn = WHITE with self.assertRaises(ValueError): req, taken, opt_capture = self.game.move(move) def test_sliding_diagonal_capture(self): """ p . . . . . p . . . . . . . . . . . . . . . . . . . . X . . . . . . . . . . . . . . . . . . . . p . . . . . p . . . . . . . . . """ result_by_move = { Move(D5, C6): (Move(D5, C6), None), Move(D5, B7): (Move(D5, B7), None), Move(D5, A8): (Move(D5, A8), A8), Move(D5, E6): (Move(D5, E6), None), Move(D5, F7): (Move(D5, F7), None), Move(D5, G8): (Move(D5, G8), G8), Move(D5, E4): (Move(D5, E4), None), Move(D5, F3): (Move(D5, F3), None), Move(D5, G2): (Move(D5, G2), G2), Move(D5, H1): (Move(D5, G2), G2), Move(D5, C4): (Move(D5, C4), None), Move(D5, B3): (Move(D5, B3), None), Move(D5, A2): (Move(D5, A2), A2), } for expected_req, (expected_taken, expected_capture) in result_by_move.items(): self.game.board.set_board_fen('p5p1/8/8/3B4/8/8/p5p1/8') self.game.board.turn = WHITE self.game.turn = WHITE req, taken, opt_capture = self.game.move(expected_req) self.assertEqual(req, expected_req) self.assertEqual(taken, expected_taken) self.assertEqual(opt_capture, expected_capture) def test_sliding_diagonal_into_ally(self): """ p . . . . . p . . . . . . . . . . . . . . . . . . . . X . . . . . . . . . . . . . . . . . . . . p . . . . . P . . . . . . . . . """ for move in [Move(D5, G2), Move(D5, H1)]: self.game.board.set_board_fen('p5p1/8/8/3B4/8/8/p5P1/8') self.game.board.turn = WHITE self.game.turn = WHITE with self.assertRaises(ValueError): req, taken, opt_capture = self.game.move(move) def test_pawn_auto_promotion(self): """ . . . . . . . . . . . P . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . """ self.game.board.set_board_fen('8/3P4/8/8/8/8/8/8') req, taken, opt_capture = self.game.move(Move(D7, D8)) self.assertEqual(Move(D7, D8), req) self.assertNotEqual(req, taken) self.assertEqual(req.to_square, taken.to_square) self.assertEqual(req.from_square, taken.from_square) self.assertIsNone(req.promotion) self.assertEqual(taken.promotion, QUEEN) def test_pass(self): req, taken, opt_capture = self.game.move(None) self.assertEqual(req, None) self.assertEqual(taken, None) self.assertIsNone(opt_capture) self.game.board.turn = BLACK req, taken, opt_capture = self.game.move(None) self.assertEqual(req, None) self.assertEqual(taken, None) self.assertIsNone(opt_capture) self.game.board.turn = WHITE self.game.board.remove_piece_at(0) req, taken, opt_capture = self.game.move(None) self.assertEqual(req, None) self.assertEqual(taken, None) self.assertIsNone(opt_capture) def test_legal_fuzz(self, max_turns=500): board = Board() turn = 1 while not board.is_game_over() and turn < max_turns: move = random.choice(list(board.generate_pseudo_legal_moves()) + [None]) req, taken, opt_square = self.game.move(move) self.assertEqual(req, taken) if move is not None and board.is_capture(move): self.assertIsNotNone(opt_square) board.push(move if move is not None else Move.null()) self.assertEqual(self.game.board, board) turn += 1