def min_max_search(self, game: ChessGame, depth: int, maximize_color: Color, parent_max: int, parent_min: int, first_move: bool): """ Use minmax search with alpha beta prune to calculate the next best move """ board = game.board game_status = game.check_game_status() if not depth or game_status != 'Continue': if (game_status == "BlackLoss" and maximize_color == Color.WHITE) or (game_status == "WhiteLoss" and maximize_color == Color.BLACK): return math.inf ret = self.game_evaluation(maximize_color) return ret else: turn = game.turn if turn == maximize_color: max_score = parent_min for row in range(8): for col in range(8): piece = board[row][col] if piece.color == turn: moves = piece.get_checked_moves()["moves"] src = (row, col) for tar in moves: game.update(src, tar, 'Queen', is_ai=True) child_score = self.min_max_search( game, depth - 1, maximize_color, parent_max, max_score, False) game.undo() if child_score > max_score: if first_move: self.best_move = [src, tar] max_score = child_score if max_score > parent_max: return parent_max return max_score else: min_score = parent_max for row in range(8): for col in range(8): piece = board[row][col] if piece.color == turn: moves = piece.get_checked_moves()["moves"] src = (row, col) for tar in moves: game.update(src, tar, 'Queen', is_ai=True) child_score = self.min_max_search( game, depth - 1, maximize_color, min_score, parent_min, False) game.undo() if child_score < min_score: if first_move: self.best_move = [src, tar] min_score = child_score if min_score < parent_min: return parent_min return min_score
class TestUpDate(unittest.TestCase): def setUp(self) -> None: self.game = ChessGame(fen="start") def test_update_start_move_black(self): random_num_1 = random.randint(0, 7) random_num_2 = random.randint(0, 7) for row in range(6, 8): for col in range(8): self.assertFalse(self.game.update((row, col), (random_num_1, random_num_2), "Queen")) def test_update_ended_without_continue(self): self.game.half_move_clock = 49 self.game.full_move_clock = 100 self.game.count = 200 self.game.en_passant_target_notation = "-" self.game.castling_notation = "-" self.game.turn = Color.WHITE empty = Empty(None, Color.EMPTY, -1, -1) for row in range(8): for col in range(8): self.game.board[row][col] = empty self.game.board[7][3] = King(self.game, Color.BLACK, 7, 3) self.game.board[0][5] = King(self.game, Color.WHITE, 0, 5) self.assertTrue(self.game.update((0, 5), (0, 4), "Queen")) self.assertFalse(self.game.update((7, 3), (7, 2), "Queen")) def test_update_move_pawn(self): for col in range(8): self.assertTrue(self.game.update((1, col), (3, col), "Queen")) self.assertEqual(self.game.en_passant_target_notation, chr(ord('a') + col) + "3") self.game.turn = Color.WHITE for col in range(8): self.assertTrue(self.game.update((3, col), (4, col), "Queen")) self.game.turn = Color.WHITE def test_update_move_white(self): random_num_1 = random.randint(0, 7) random_num_2 = random.randint(0, 7) self.assertFalse(self.game.update((0, 0), (random_num_1, random_num_2), "Queen")) self.game.turn = Color.WHITE self.assertTrue(self.game.update((0, 1), (2, 0), "Queen")) self.game.turn = Color.WHITE self.assertTrue(self.game.update((2, 0), (0, 1), "Queen")) self.game.turn = Color.WHITE self.assertFalse(self.game.update((0, 2), (random_num_1, random_num_2), "Queen")) self.game.turn = Color.WHITE self.assertFalse(self.game.update((0, 3), (random_num_1, random_num_2), "Queen")) self.game.turn = Color.WHITE self.assertFalse(self.game.update((0, 4), (random_num_1, random_num_2), "Queen")) self.game.turn = Color.WHITE self.assertFalse(self.game.update((0, 5), (random_num_1, random_num_2), "Queen")) self.game.turn = Color.WHITE self.assertTrue(self.game.update((0, 6), (2, 7), "Queen")) self.game.turn = Color.WHITE self.assertTrue(self.game.update((2, 7), (0, 6), "Queen")) self.game.turn = Color.WHITE self.assertFalse(self.game.update((0, 7), (random_num_1, random_num_2), "Queen")) def test_update_kings_coordinate(self): self.game.update((1, 4), (3, 4), "Queen") self.game.update((6, 4), (4, 4), "Queen") self.game.update((0, 4), (1, 4), "Queen") self.game.update((7, 4), (6, 4), "Queen") self.assertEqual(self.game.kings_coordinate[0], (1, 4)) self.assertEqual(self.game.kings_coordinate[1], (6, 4)) def test_update_history(self): self.game.update((1, 0), (3, 0), "Queen") self.game.update((6, 0), (4, 0), "Queen") self.game.update((0, 0), (2, 0), "Queen") self.game.update((7, 0), (5, 0), "Queen") self.game.update((1, 7), (3, 7), "Queen") self.game.update((6, 7), (4, 7), "Queen") self.game.update((0, 7), (2, 7), "Queen") self.game.update((7, 7), (5, 7), "Queen") self.assertEqual(self.game.history[0]["fen"], "rnbqkbnr/pppppppp/8/8/P7/8/1PPPPPPP/RNBQKBNR b KQkq a3 0 1") self.assertEqual(self.game.history[0]["movement"], {"src": "a2", "tar": "a4"}) self.assertEqual(self.game.history[1]["fen"], "rnbqkbnr/1ppppppp/8/p7/P7/8/1PPPPPPP/RNBQKBNR w KQkq a6 0 2") self.assertEqual(self.game.history[1]["movement"], {"src": "a7", "tar": "a5"}) self.assertEqual(self.game.history[2]["fen"], "rnbqkbnr/1ppppppp/8/p7/P7/R7/1PPPPPPP/1NBQKBNR b Kkq - 1 2") self.assertEqual(self.game.history[2]["movement"], {"src": "a1", "tar": "a3"}) self.assertEqual(self.game.history[3]["fen"], "1nbqkbnr/1ppppppp/r7/p7/P7/R7/1PPPPPPP/1NBQKBNR w Kk - 2 3") self.assertEqual(self.game.history[3]["movement"], {"src": "a8", "tar": "a6"}) self.assertEqual(self.game.history[4]["fen"], "1nbqkbnr/1ppppppp/r7/p7/P6P/R7/1PPPPPP1/1NBQKBNR b Kk h3 0 3") self.assertEqual(self.game.history[4]["movement"], {"src": "h2", "tar": "h4"}) self.assertEqual(self.game.history[5]["fen"], "1nbqkbnr/1pppppp1/r7/p6p/P6P/R7/1PPPPPP1/1NBQKBNR w Kk h6 0 4") self.assertEqual(self.game.history[5]["movement"], {"src": "h7", "tar": "h5"}) self.assertEqual(self.game.history[6]["fen"], "1nbqkbnr/1pppppp1/r7/p6p/P6P/R6R/1PPPPPP1/1NBQKBN1 b k - 1 4") self.assertEqual(self.game.history[6]["movement"], {"src": "h1", "tar": "h3"}) self.assertEqual(self.game.history[7]["fen"], "1nbqkbn1/1pppppp1/r6r/p6p/P6P/R6R/1PPPPPP1/1NBQKBN1 w - - 2 5") self.assertEqual(self.game.history[7]["movement"], {"src": "h8", "tar": "h6"})