Example #1
0
 def test_stuck_state(self):
     """
     Purpose: Show generation of children when the current player cannot move
     """
     player_row1 = [(0, 0), (0, 2), (1, 1)]
     player_row2 = [(2, 0), (3, 1), (2, 2)]
     small_board = FishBoardModel.create_with_same_fish_amount(3, 3, 3)
     game_state = FishGameStateFactory.create_move_penguins_state(
         small_board, [(PlayerColor.RED, player_row1, 0),
                       (PlayerColor.BLACK, player_row2, 0)],
         check_penguin_amount=False
     )
     stuck_tree = FishGameTree(game_state)
     self.assertEqual(1, len(list(stuck_tree.generate_direct_children())))
     self.assertEqual(PlayerColor.BLACK, list(stuck_tree.generate_direct_children())[0].get_turn_color())
Example #2
0
 def test_not_move_state(self):
     """
     Purpose: Test trying to create a tree from a state that is not
              the player movement state
     """
     with self.assertRaises(ValueError):
         small_board = FishBoardModel.create_with_same_fish_amount(4, 3, 3)
         game_state = FishGameStateFactory.create_place_penguins_state(
             small_board, [(PlayerColor.RED, []), (PlayerColor.BLACK, [])]
         )
         FishGameTree(game_state)
Example #3
0
    def find_next_move(self, state: FishGameState) -> Action:
        """
        Purpose: Find best move for player whose turn it is in the current state
        after going N turns ahead for that player in the current turn, or as many possible.
        This assumes that a player using this will have at least 1 possible move on their
        turn. This holds because the referee would never ask for a move for a player who
        has a turn skipped. N turns ahead means that we will look at the current players moves for that many
        turns ahead. We could, and in some cases should look at their opponents move for that final turn where
        turn=N, but for this particular maximin evaluation we only have to look to the depth where the player
        we are checking has made their Nth move and not look at their opponents moves also.

        This is because the evaluation function only checks the amount of fish that the maximizing player has, which means that
        the opponents moves in the Nth round cannot impact the amount of fish that the strategizing player has,
        and thus is wasted computation. If we had another maximin evaluation function that took into account other
        factors that could change turn-by-turn, we would also checks the opponents moves in the Nth round.
        Signature: FishGameState Int -> Action
        :param state: The state for which we are looking for a move. This state includes the current
                      turn so we know which player to look for
        :return: The action representing a from and to position that is the player best
                 move according to the maximin algorithm
        """
        game_tree = FishGameTree(state=state)
        max_value = float("-inf")
        best_children = []
        for child in game_tree.generate_direct_children():
            # we have already gone one turn down, so
            # depth is amount of players there are * the number of turns - 1
            depth = (self.look_ahead_turns - 1) * len(state.get_player_order())
            minimax_value = FishBasicStrategy._find_maximin_value(
                child, False, depth, game_tree.get_turn_color())
            if minimax_value > max_value:
                best_children = [child]
                max_value = minimax_value
            elif minimax_value == max_value:
                best_children.append(child)
        if len(best_children) == 1:
            best_action = best_children[0].parent_action
        else:
            best_action = FishBasicStrategy._break_ties(
                [tree.get_parent_action() for tree in best_children])
        return best_action
Example #4
0
def run_xtree(move_response_query):
    """
    Purpose: Run the xtree test harness
    Signature: Move-Response-Query -> Void
    :param move_response_query: MoveResponseQuery object
    as specified by harness to print out test values
    :return: Prints out the Action that results from moving the first
    move and moving the second player to the neighbor of the first
    """
    # Convert JSON into state and our coordinates
    state_json = move_response_query['state']
    from_coord = move_response_query['from']
    to_coord = move_response_query['to']
    state = THHelper.create_state(state_json)
    # Save the to position of the action we are are gonna take and
    # find its neighbors
    to_posn_dh = THHelper.convert_to_double_height(to_coord[0], to_coord[1])
    from_posn_dh = THHelper.convert_to_double_height(from_coord[0],
                                                     from_coord[1])
    to_posn_neighbors = THHelper.find_coord_neighbors(to_posn_dh)
    # Take the move on the state_json
    tree = FishGameTree(state)
    new_state = tree.validate_and_apply_action((from_posn_dh, to_posn_dh))
    # Then we want to see if the new state_json which means
    # the next player(if it exists) has a move to a neighbor
    # of the to_pos
    new_tree = FishGameTree(new_state)
    final_actions = find_if_valid_move_from_state(new_tree, to_posn_neighbors)
    if final_actions:
        final_action = find_tiebreaker(final_actions)
        move_from_posn, move_to_posn = final_action
        move_from_org_posn = THHelper.convert_back_to_posn(
            move_from_posn[0], move_from_posn[1])
        move_to_org_posn = THHelper.convert_back_to_posn(
            move_to_posn[0], move_to_posn[1])
        final_action = [move_from_org_posn, move_to_org_posn]
        print(json.dumps(final_action))
    else:
        print(json.dumps(False))
Example #5
0
 def create_small_tree(rows=4, columns=4):
     """
     Purpose: Create a small tree to be used in tests
     Signature: Int Int -> FishGameTree
     :param rows: Amount of rows for the board
     :param columns: Amount of columns for the board
     """
     small_board = FishBoardModel.create_with_same_fish_amount(rows, columns, 3)
     game_state = FishGameStateFactory.create_move_penguins_state(
         small_board, [(PlayerColor.RED, GameTreeTest.penguins_first_player, 5),
                       (PlayerColor.BLACK, GameTreeTest.penguins_second_player, 0)]
     )
     tree_node = FishGameTree(game_state)
     return tree_node
Example #6
0
    def _find_maximin_value(tree: FishGameTree, maximizing_player: bool,
                            depth: int,
                            maximizing_player_color: PlayerColor) -> int:
        """
        Purpose: Find the maximin value for a certain game tree to a certain depth.
                 This maximizes the amount of fish when a the player whose turn it is is playing
                 and minimizes the amount of fish with its moves when all other players are playing,
        Signature: FishGameTree Bool Int PlayerColor -> Int
        :param tree: The tree which we are searching for the best moves on for a certain player
        :param maximizing_player: Whether it is the maximizing players turn
        :param depth: How many nodes down in the tree we have to go before returning the value
        :param maximizing_player_color: The color of the player who is trying to maximize their value
        :return: Returns an integer representing the maximum value that a player is guaranteed to get
                 from that position in the tree
        """
        # if we have gone N turns or hit an end game state
        if depth == 0 or tree.is_end_game_state():
            return tree.get_state().get_fish_for_player(
                maximizing_player_color)

        if maximizing_player:
            compare_val = float("-inf")
            compare_func = max
        else:
            compare_val = float("inf")
            compare_func = min

        for child in tree.generate_direct_children():
            maximizing_player = child.get_turn_color(
            ) == maximizing_player_color
            compare_val = compare_func(
                compare_val,
                FishBasicStrategy._find_maximin_value(child, maximizing_player,
                                                      depth - 1,
                                                      maximizing_player_color))
        return compare_val
    def test_run_movement_turn_skip(self):
        """
        Test running a movement turn where someone is skipped
        """
        # player that throws an exception when trying to place it
        ref = self.set_up_game_phases(3, 3, num_players=4, run_placement_phase=True)

        # Set player color to black, they can not go and will be skipped
        ref.current_game_state.set_turn(PlayerColor.BLACK)
        ref.current_game_tree = FishGameTree(copy.deepcopy(ref.current_game_state))

        initial_pengs = ref.current_game_state.get_penguins_for_player(player_color=PlayerColor.BLACK)
        ref._run_movement_turn()
        # Player 4 can't go and is skipped so they have no penguins
        moved_pengs = ref.current_game_state.get_penguins_for_player(player_color=PlayerColor.BLACK)
        self.assertEqual(initial_pengs, moved_pengs)
        self.assertEqual(0, ref.current_game_state.get_fish_for_player(player_color=PlayerColor.BLACK))
Example #8
0
 def create_multiple_players(rows=5, columns=5):
     """
     Purpose: Create a larger board with multiple players to be used for testing
     Signature: Int Int -> FishGameTree
     :param rows: Amount of rows for the board
     :param columns: Amount of columns for the board
     """
     small_board = FishBoardModel.create_with_same_fish_amount(rows, columns, 3)
     game_state = FishGameStateFactory.create_move_penguins_state(
         small_board, [(PlayerColor.RED, GameTreeTest.penguins_four_player[1], 0),
                       (PlayerColor.BLACK, GameTreeTest.penguins_four_player[0], 0),
                       (PlayerColor.BROWN, GameTreeTest.penguins_four_player[2], 0),
                       (PlayerColor.WHITE, GameTreeTest.penguins_four_player[3], 0),
                       ]
     )
     tree_node = FishGameTree(game_state)
     return tree_node
 def test_find_if_valid_move(self):
     """
     Purpose: Test finding valid move from a tree
     """
     state = {
         "players": [{
             "color": "red",
             "score": 10,
             "places": [[0, 0]]
         }, {
             "color": "black",
             "score": 1,
             "places": [[0, 2], [0, 1]]
         }],
         "board": [[4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [3, 3, 3, 3]]
     }
     state = THHelper.create_state(state)
     state.move_penguin(PlayerColor.RED, (0, 0), (3, 3))
     tree = FishGameTree(state)
     neighbors = THHelper.find_coord_neighbors([3, 3])
     self.assertListEqual([((4, 0), (3, 1)), ((2, 0), (3, 1))],
                          xtree.find_if_valid_move_from_state(
                              tree, neighbors))