Beispiel #1
0
    def test_overwriting_transposition_dict(
            self, clean_tree_search_caches_before_tests):
        game = Game()
        best_moves = get_legal_abstract_moves(game.player_1, False)[0:1]

        store_in_transposition_dict(best_moves, 20, EvaluationType.exact,
                                    game.player_1, 2, False)

        # We should never overwrite an exact evaluation
        store_in_transposition_dict([], 50, EvaluationType.beta_cutoff,
                                    game.player_1, 3, False)

        # We should never overwrite an evaluation with higher depth
        store_in_transposition_dict([], 50, EvaluationType.beta_cutoff,
                                    game.player_1, 1, False)

        stored_moves, stored_score, _ = get_best_move_from_transposition_dict(
            game.player_1, 1, False, 0, 0)
        assert len(stored_moves) == 1
        assert stored_score == 20

        store_in_transposition_dict([], 30, EvaluationType.exact,
                                    game.player_1, 4, False)

        _, stored_score, _ = get_best_move_from_transposition_dict(
            game.player_1, 1, False, 0, 0)
        assert stored_score == 30
Beispiel #2
0
    def test_non_symmetric_movements(self):
        game = Game()

        PlacementMove(
            piece=game.player_1.get_piece_by_type(PieceType.two),
            tile=game.board.get_tile(0, 1),
            board=game.board,
        ).execute()

        # All tiles except 1 (the one occupied by player 1) should need to be considered
        assert len(get_legal_abstract_moves(
            game.player_2, True)) == 11 * len(game.player_2.pieces)

        # No after executing the movement, all possible movements need to be explored
        game.board.execute_board_movements(PLAYER_1_ID)
        assert len(get_legal_abstract_moves(
            game.player_2, True)) == 12 * len(game.player_2.pieces)
Beispiel #3
0
    def test_movement_order_sorting(self):
        game = Game()

        moves = get_legal_abstract_moves(game.player_1,
                                         filter_symmetric_moves=True)
        assert len(moves) > 0
        assert moves[0].piece_type == PieceType.four
        assert moves[-1].piece_type == PieceType.one
Beispiel #4
0
 def populate_moves_table(self):
     self.cursor.execute("SELECT COUNT(*) from moves;")
     if self.cursor.fetchone()[0] > 0:
         return
     game = Game()
     moves = get_legal_abstract_moves(game.player_1,
                                      filter_symmetric_moves=False)
     moves += get_legal_abstract_moves(game.player_2,
                                       filter_symmetric_moves=False)
     moves.append(AbstractMove(None, game.player_1))
     moves.append(AbstractMove(None, game.player_2))
     move_values = [[
         move.x, move.y,
         int(move.piece_type) if move.piece_type is not None else None,
         move.owner_id
     ] for move in moves]
     self.execute_values(QUERY_STORE_LEGAL_MOVES, move_values)
     self.database_connection.commit()
Beispiel #5
0
    def test_symmetric_movements_removal(self):
        game = Game()

        # With an empty board, there should only be 2 valid tiles on which to play
        assert len(get_legal_abstract_moves(
            game.player_1, True)) == 2 * len(game.player_1.pieces)

        PlacementMove(
            piece=game.player_1.get_piece_by_type(PieceType.two),
            tile=game.board.get_tile(0, 2),
            board=game.board,
        ).execute()
        PlacementMove(
            piece=game.player_2.get_piece_by_type(PieceType.four),
            tile=game.board.get_tile(4, 2),
            board=game.board,
        ).execute()
        game.board.execute_board_movements(PLAYER_1_ID)

        # Now both players should have 7 legal tiles at which to place their pieces
        # as the board now has x-axis symmetry
        assert len(get_legal_abstract_moves(
            game.player_1,
            True)) == 7 * len(game.player_1.get_available_pieces())
        assert len(get_legal_abstract_moves(
            game.player_2,
            True)) == 7 * len(game.player_2.get_available_pieces())

        game.board.execute_board_movements(PLAYER_2_ID)

        # The game still has x-axis symmetry
        assert len(get_legal_abstract_moves(
            game.player_1,
            True)) == 7 * len(game.player_1.get_available_pieces())
        assert len(get_legal_abstract_moves(
            game.player_2,
            True)) == 7 * len(game.player_2.get_available_pieces())
Beispiel #6
0
    def test_non_symmetric_center_row(self):
        game = Game()

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

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

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

        # Game should not have y-axis symmetry because the center piece is facing east
        assert len(get_legal_abstract_moves(
            game.player_2, True)) == 11 * len(game.player_2.pieces)
Beispiel #7
0
    def test_blocked_moves_sorted_last(self):
        game = Game()
        game.starting_player = game.player_1

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

        one = game.player_2.get_piece_by_type(PieceType.one)
        one.set_movement_direction(Direction.south)
        game.board.get_tile(1, 1).place_piece(one)

        moves = get_legal_abstract_moves(game.player_1, False)
        first_piece_moves = moves[0:11]
        for move in first_piece_moves:
            assert move.piece_type == PieceType.four

        last_moves = moves[-3:]
        for move in last_moves:
            # The (2,0) tile is blocked by the opponent 5,
            # so this tile should be absolute bottom (except for our 4 and our 5)
            assert move.x == 2 and move.y == 0
Beispiel #8
0
    def test_using_valid_transposition_cache_cutoffs(
            self, clean_tree_search_caches_before_tests):
        game = Game()
        best_moves = get_legal_abstract_moves(game.player_1, False)[0:1]

        # We were searching a node with beta = 10 and found a move with value 35, which means the current node would
        # never happen in practice and we stored the lastly explored best value, 35
        store_in_transposition_dict(best_moves, 35, EvaluationType.beta_cutoff,
                                    game.player_1, 2, False)

        # Now we are exploring the same position at depth 2 but with a beta value of 50.
        # Since the node is a beta_cutoff, we know that the value is 35 or higher.
        # The beta player is only guaranteed a move of 50 or higher, which means this node could be a suitable candidate
        # We cannot reuse it, as it will not be discarded by beta cutoff and we don't know its real score
        _, stored_score, _ = get_best_move_from_transposition_dict(
            game.player_1, 2, False, 0, 50)
        assert stored_score is None

        # However, when exploring it with a beta value of 30, we can re-use this value as we will later discard it
        # in a beta cutoff, as the beta player is guaranteed a move that is better than "35 or higher"
        _, stored_score_2, _ = get_best_move_from_transposition_dict(
            game.player_1, 2, False, 0, 30)
        assert stored_score_2 is 35
Beispiel #9
0
FORMAT = "[%(asctime)s] [%(levelname)s] %(message)s"
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format=FORMAT)

game = Game()
strategy_fn = build_tree_search_strategy(depth=2)

# First, play a couple of normal games #
for x in range(0, 25):
    logger.info(f"Generating transposition cache for normal game {x}.")
    normal_game = Game()
    normal_game.play_game(strategy_fn, strategy_fn)
    persist_transposition_cache()

# Once "typical" games have been persisted, start mapping all possible first-turn positions
for move_p1 in get_legal_abstract_moves(game.player_1,
                                        filter_symmetric_moves=False):
    game_2 = Game()
    move_p1.execute(game_2.player_1)
    for move_p2 in get_legal_abstract_moves(game_2.player_2,
                                            filter_symmetric_moves=False):
        move_game = game_2.clone()
        logger.info(
            f"Generating transposition cache for starting move {move_p1!r} and follow up move {move_p2!r}."
        )
        move_p2.execute(move_game.player_2)
        move_game.board.execute_board_movements(move_game.starting_player.id)
        move_game.switch_starting_player()
        move_game.play_game(strategy_fn, strategy_fn)
        persist_transposition_cache()