Exemple #1
0
    def test_obvious_first_movement(self, clean_tree_search_caches_before_tests):
        # Here we are going to set up a game where player 1 is about to win the game, except if
        # player 2 (who goes first) prevents it by placing a piece on the square player 1 needs to use
        # to win
        game = Game()
        game.starting_player = game.player_2

        five = game.player_1.get_piece_by_type(PieceType.five)
        five.set_movement_direction(Direction.east)
        game.board.get_tile(1, 1).place_piece(five)

        four = game.player_1.get_piece_by_type(PieceType.four)
        four.set_movement_direction(Direction.west)
        game.board.get_tile(3, 2).place_piece(four)

        opponent_four = game.player_2.get_piece_by_type(PieceType.four)
        opponent_four.set_movement_direction(Direction.west)
        game.board.get_tile(1, 3).place_piece(opponent_four)

        best_moves, _, eval_type = get_best_moves(game.player_2, game.player_1, is_first_move=True, depth=1)
        assert len(best_moves) == 1
        assert eval_type == EvaluationType.exact

        # Player 2 can only avoid losing this turn by playing anything on tile (2, 4)
        best_move = get_best_move(game.player_2, game.player_1, is_first_move=True, depth=1)

        assert best_move.x == 2
        assert best_move.y == 4

        # Should be true for any depth level (using 2 for test speed reasons)
        best_move_depth_2 = get_best_move(game.player_2, game.player_1, is_first_move=True, depth=2)

        assert best_move_depth_2.x == 2
        assert best_move_depth_2.y == 4
Exemple #2
0
    def test_using_inverted_alpha_beta_cutoffs(
            self, clean_tree_search_caches_before_tests):
        game = Game()

        # Player 1 pieces

        one_p1 = game.player_1.get_piece_by_type(PieceType.one)
        one_p1.set_movement_direction(Direction.east)
        game.board.get_tile(1, 3).place_piece(one_p1)

        four_p1 = game.player_1.get_piece_by_type(PieceType.four)
        four_p1.set_movement_direction(Direction.south)
        game.board.get_tile(3, 2).place_piece(four_p1)

        five_p1 = game.player_1.get_piece_by_type(PieceType.five)
        five_p1.set_movement_direction(Direction.south)
        game.board.get_tile(2, 2).place_piece(five_p1)

        # Player 2 pieces

        two_p2 = game.player_2.get_piece_by_type(PieceType.two)
        two_p2.set_movement_direction(Direction.west)
        game.board.get_tile(3, 1).place_piece(two_p2)

        three_p2 = game.player_2.get_piece_by_type(PieceType.three)
        three_p2.set_movement_direction(Direction.east)
        game.board.get_tile(2, 3).place_piece(three_p2)

        four_p2 = game.player_2.get_piece_by_type(PieceType.four)
        four_p2.set_movement_direction(Direction.east)
        game.board.get_tile(2, 1).place_piece(four_p2)

        five_p2 = game.player_2.get_piece_by_type(PieceType.five)
        five_p2.set_movement_direction(Direction.east)
        game.board.get_tile(1, 2).place_piece(five_p2)

        _, score, eval_type = get_best_moves(game.player_1,
                                             game.player_2,
                                             is_first_move=True,
                                             depth=2)

        assert eval_type == EvaluationType.exact
Exemple #3
0
    def test_transposition_does_not_use_wrong_alphabeta_cutoffs(
            self, clean_tree_search_caches_before_tests):
        game = Game()
        game.starting_player = game.player_2
        game.first_move_executed = True

        one_p1 = game.player_1.get_piece_by_type(PieceType.one)
        one_p1.set_movement_direction(Direction.south)
        game.board.get_tile(2, 3).place_piece(one_p1)

        one_p2 = game.player_2.get_piece_by_type(PieceType.one)
        one_p2.set_movement_direction(Direction.west)
        game.board.get_tile(3, 3).place_piece(one_p2)

        five_p2 = game.player_2.get_piece_by_type(PieceType.five)
        five_p2.set_movement_direction(Direction.west)
        game.board.get_tile(4, 2).place_piece(five_p2)

        best_move_p1_r1 = get_best_move(game.player_1,
                                        game.player_2,
                                        is_first_move=False,
                                        depth=2)

        assert best_move_p1_r1.piece_type != PieceType.four or best_move_p1_r1.x != 0 or best_move_p1_r1.y != 3

        # We ignore the correct movement and play a losing move (4 piece to (0, 3))

        four_p1 = game.player_1.get_piece_by_type(PieceType.four)
        four_p1.set_movement_direction(Direction.east)
        game.board.get_tile(0, 3).place_piece(four_p1)

        game.board.execute_board_movements(game.player_2.id)
        game.switch_starting_player()

        _, score, eval_type = get_best_moves(game.player_1,
                                             game.player_2,
                                             is_first_move=True,
                                             depth=2)

        assert score == -WIN_CONDITION_SCORE + DEPTH_PENALTY
        assert eval_type == EvaluationType.exact
Exemple #4
0
    def test_game_is_lost_on_depth_2_alternative(self, clean_tree_search_caches_before_tests):
        game = Game()
        game.starting_player = game.player_2

        three = game.player_1.get_piece_by_type(PieceType.three)
        three.set_movement_direction(Direction.east)
        game.board.get_tile(2, 1).place_piece(three)

        four = game.player_1.get_piece_by_type(PieceType.four)
        four.set_movement_direction(Direction.south)
        game.board.get_tile(2, 3).place_piece(four)

        five = game.player_1.get_piece_by_type(PieceType.five)
        five.set_movement_direction(Direction.south)
        game.board.get_tile(1, 2).place_piece(five)

        four_p2 = game.player_2.get_piece_by_type(PieceType.four)
        four_p2.set_movement_direction(Direction.west)
        game.board.get_tile(3, 2).place_piece(four_p2)

        five_p2 = game.player_2.get_piece_by_type(PieceType.five)
        five_p2.set_movement_direction(Direction.west)
        game.board.get_tile(2, 2).place_piece(five_p2)

        # At this position, player 2 is losing at depth 2. He can avoid losing this round by playing either:
        # 1) The 1 piece at (0, 3) or (1, 4)
        # 2) the 2 or 3 pieces at (3, 0) or (4, 1),
        # but next round he will be unable to block both squares where P1 can mate, and will not have the 4 in hand

        best_moves_p2_t1, score, eval_type = get_best_moves(game.player_2, game.player_1, is_first_move=True, depth=2)
        assert 6 >= len(best_moves_p2_t1) >= 1
        assert eval_type == EvaluationType.exact
        best_move_p2 = get_best_move(game.player_2, game.player_1, is_first_move=True, depth=2)

        assert (best_move_p2.x == 0 and best_move_p2.y == 3 and best_move_p2.piece_type == PieceType.one) or \
               (best_move_p2.x == 1 and best_move_p2.y == 4 and best_move_p2.piece_type == PieceType.one) or \
               (best_move_p2.x == 3 and best_move_p2.y == 0 and best_move_p2.piece_type in [PieceType.two, PieceType.three]) or \
               (best_move_p2.x == 4 and best_move_p2.y == 1 and best_move_p2.piece_type in [PieceType.two, PieceType.three])
        assert best_move_p2.score == -WIN_CONDITION_SCORE + DEPTH_PENALTY * 2
Exemple #5
0
    def test_game_is_lost_on_depth_2(self, clean_tree_search_caches_before_tests):
        game = Game()
        game.starting_player = game.player_1

        two = game.player_1.get_piece_by_type(PieceType.two)
        two.set_movement_direction(Direction.south)
        game.board.get_tile(2, 3).place_piece(two)

        three = game.player_1.get_piece_by_type(PieceType.three)
        three.set_movement_direction(Direction.east)
        game.board.get_tile(1, 2).place_piece(three)

        four = game.player_1.get_piece_by_type(PieceType.four)
        four.set_movement_direction(Direction.east)
        game.board.get_tile(0, 2).place_piece(four)

        one_p2 = game.player_2.get_piece_by_type(PieceType.one)
        one_p2.set_movement_direction(Direction.north)
        game.board.get_tile(2, 2).place_piece(one_p2)

        two_p2 = game.player_2.get_piece_by_type(PieceType.two)
        two_p2.set_movement_direction(Direction.west)
        game.board.get_tile(3, 2).place_piece(two_p2)

        # At this position, player 2 is completely losing at depth 2
        # He can avoid losing this round by playing the 4 piece at (2, 0), but next round he will
        # be unable to block both squares where P1 can mate, and will not have the 4 in hand

        best_moves_p2_t1, score_p2_t1, eval_type_p2_t1 = get_best_moves(game.player_2, game.player_1, is_first_move=False, depth=2)
        assert len(best_moves_p2_t1) == 1
        assert eval_type_p2_t1 == EvaluationType.exact
        best_move_p2 = get_best_move(game.player_2, game.player_1, is_first_move=False, depth=2)

        assert best_move_p2.x == 2 and best_move_p2.y == 0
        assert best_move_p2.piece_type == PieceType.four
        assert best_move_p2.score == score_p2_t1 == -WIN_CONDITION_SCORE + DEPTH_PENALTY * 2
Exemple #6
0
    def test_black_widow(self, clean_tree_search_caches_before_tests):
        # The following situation is hard to see for a human, but completely winning for player 2
        # He only has piece 5 in hand, and as long as he keeps it for the next move (makes it not enter the board)
        # He will be able to use it next round to win the game, no reply from player 1 possible.

        game = Game()
        game.starting_player = game.player_1

        # Player 1 pieces
        player_1_one = game.player_1.get_piece_by_type(PieceType.one)
        player_1_one.set_movement_direction(Direction.east)
        game.board.get_tile(2, 3).place_piece(player_1_one)

        player_1_two = game.player_1.get_piece_by_type(PieceType.two)
        player_1_two.set_movement_direction(Direction.west)
        game.board.get_tile(3, 2).place_piece(player_1_two)

        player_1_four = game.player_1.get_piece_by_type(PieceType.four)
        player_1_four.set_movement_direction(Direction.east)
        game.board.get_tile(1, 3).place_piece(player_1_four)

        player_1_five = game.player_1.get_piece_by_type(PieceType.five)
        player_1_five.set_movement_direction(Direction.east)
        game.board.get_tile(1, 1).place_piece(player_1_five)

        player_1_three = game.player_1.get_piece_by_type(PieceType.three)
        player_1_three.set_movement_direction(Direction.east)
        game.board.get_tile(0, 3).place_piece(player_1_three)

        # Player 2 pieces
        player_2_one = game.player_2.get_piece_by_type(PieceType.one)
        player_2_one.set_movement_direction(Direction.north)
        game.board.get_tile(2, 1).place_piece(player_2_one)

        player_2_two = game.player_2.get_piece_by_type(PieceType.two)
        player_2_two.set_movement_direction(Direction.west)
        game.board.get_tile(3, 1).place_piece(player_2_two)

        player_2_three = game.player_2.get_piece_by_type(PieceType.three)
        player_2_three.set_movement_direction(Direction.west)
        game.board.get_tile(3, 3).place_piece(player_2_three)

        player_2_four = game.player_2.get_piece_by_type(PieceType.four)
        player_2_four.set_movement_direction(Direction.east)
        game.board.get_tile(2, 2).place_piece(player_2_four)

        best_moves_player_2_round_1, player_2_round_1_score, _ = get_best_moves(game.player_2, game.player_1, is_first_move=False, depth=2)

        for candidate_move in best_moves_player_2_round_1:
            clone_game = game.clone()

            candidate_move.execute(clone_game.player_2)

            clone_game.board.execute_board_movements(starting_player_id=clone_game.player_1.id)
            clone_game.switch_starting_player()

            # Play the second round
            clone_move_p2_r2 = get_best_move(clone_game.player_2, clone_game.player_1, is_first_move=True, depth=2)
            clone_move_p2_r2.execute(clone_game.player_2)
            clone_move_p1_r2 = get_best_move(clone_game.player_1, clone_game.player_2, is_first_move=False, depth=2)
            clone_move_p1_r2.execute(clone_game.player_1)

            clone_game.board.execute_board_movements(starting_player_id=clone_game.player_2.id)
            assert clone_game.board.get_game_result(clone_game.player_2.id, clone_game.player_1.id) == GameResult.win

            assert clone_move_p1_r2.score == -WIN_CONDITION_SCORE + DEPTH_PENALTY
            assert clone_move_p2_r2.score == WIN_CONDITION_SCORE - DEPTH_PENALTY

        # Play the first round
        best_move_player_2_round_1 = get_best_move(game.player_2, game.player_1, is_first_move=False, depth=2)
        best_move_player_2_round_1.execute(game.player_2)

        game.board.execute_board_movements(starting_player_id=game.player_1.id)
        game.switch_starting_player()

        best_moves_player_2_round_2, _, _ = get_best_moves(game.player_2, game.player_1, is_first_move=True, depth=2)

        # Play the second round
        best_move_player_2_round_2 = get_best_move(game.player_2, game.player_1, is_first_move=True, depth=2)
        best_move_player_2_round_2.execute(game.player_2)
        best_move_player_1_round_2 = get_best_move(game.player_1, game.player_2, is_first_move=False, depth=2)
        best_move_player_1_round_2.execute(game.player_1)

        game.board.execute_board_movements(starting_player_id=game.player_2.id)

        # On the first round, player 2 must play the 5 piece (forced, only piece in hand) on any tile where it cannot
        # enter the board, as he needs it to mate in the next round.
        tiles_where_5_would_enter = [(0, 2), (1, 4), (0, 3), (2, 0)]
        for candidate_move in best_moves_player_2_round_1:
            assert candidate_move.piece_type == PieceType.five
            assert (candidate_move.x, candidate_move.y) not in tiles_where_5_would_enter
        assert best_move_player_2_round_1.piece_type == PieceType.five
        assert (best_move_player_2_round_1.x, best_move_player_2_round_1.y) not in tiles_where_5_would_enter

        # On the second round, player 2 should play the 5 on (2, 4), as its the winning move
        assert len(best_moves_player_2_round_2) == 1
        assert best_move_player_2_round_2.piece_type == PieceType.five
        assert best_move_player_2_round_2.x == 2 and best_move_player_2_round_2.y == 4

        # Both players should know at any moment they have already won/lost
        assert best_move_player_2_round_1.score == player_2_round_1_score == WIN_CONDITION_SCORE - DEPTH_PENALTY * 2
        assert best_move_player_1_round_2.score == -WIN_CONDITION_SCORE + DEPTH_PENALTY
        assert best_move_player_2_round_2.score == WIN_CONDITION_SCORE - DEPTH_PENALTY

        assert game.board.get_game_result(game.player_2.id, game.player_1.id) == GameResult.win