def test_opp_gamestate():
    """Test that opponent's gamestate is updated properly."""
    spinda = Pokemon(name="spinda", moves=["tackle"])

    ppgs1 = PokemonPlayerGameState()
    ppgs2 = PokemonPlayerGameState()

    gamestate = {}
    gamestate["team"] = []
    gamestate["active"] = spinda

    opp_gamestate = anonymize_gamestate_helper(gamestate)

    # Update the gamestate
    ppgs1.update_gamestate(gamestate, opp_gamestate)
    ppgs2.update_gamestate(gamestate, opp_gamestate)

    # Gamestate updating happens properly.
    assert ppgs1.opp_gamestate["data"]
    assert not ppgs1.opp_gamestate["data"]["team"]
    assert ppgs1.opp_gamestate["data"]["active"]["name"] == "spinda"

    turn_info = {}
    turn_info["type"] = "ATTACK"
    turn_info["attacker"] = "player2"
    turn_info["move"] = spinda.moves[0]
    turn_info["pct_damage"] = 28
    turn_info["def_poke"] = "spinda"
    turn_info["atk_poke"] = "spinda"
    turn_info = [turn_info]

    # Give new info
    ppgs1.new_info(turn_info, "player1")
    # New info is stored properly
    assert len(ppgs1.opp_gamestate["moves"]["spinda"]) == 1
    def __init__(self, team):
        """Initialize the agent."""
        if not team:
            raise AttributeError("Team must have at least one pokemon")

        super().__init__(type="PokemonAgent")
        self.team = team
        self.game_state = PokemonPlayerGameState()
        self.dmg_stat_calc = DamageStatCalc()
def basic_test():
    """Test initializing and accessing of attributes."""
    ppgs = PokemonPlayerGameState()

    # Attribute lookup
    assert not ppgs.test_attr

    # Setting values in that dictionary
    ppgs.test_attr["test"] = 7
    assert ppgs.test_attr
    assert ppgs.test_attr["test"] == 7
def test_infer_speed_investment():
    """Test how we infer speed."""
    magikarp = Pokemon(name="magikarp", moves=["tackle"])
    spinda = Pokemon(name="spinda", moves=["tackle"])

    ppgs1 = PokemonPlayerGameState()
    ppgs2 = PokemonPlayerGameState()

    gamestate = {}
    gamestate["team"] = []
    gamestate["active"] = magikarp
    opp_gamestate_dict = {}
    opp_gamestate_dict["team"] = []
    opp_gamestate_dict["active"] = spinda
    opp_gamestate = anonymize_gamestate_helper(opp_gamestate_dict)
    ppgs1.update_gamestate(gamestate, opp_gamestate)
    ppgs2.update_gamestate(opp_gamestate_dict,
                           anonymize_gamestate_helper(gamestate))

    new_info = {}
    new_info["type"] = "ATTACK"
    new_info["move"] = MOVE_DATA["tackle"]
    new_info["attacker"] = "player2"
    new_info["defender"] = "player1"
    new_info["pct_damage"] = 27
    new_info["damage"] = 46
    new_info["atk_poke"] = "spinda"
    new_info["def_poke"] = "magikarp"
    new_info = [new_info] * 2

    test_infer_speed_faster(ppgs2, new_info)
    test_infer_speed_slower(ppgs1, new_info)
def test_init_opp_gamestate():
    """Test that initializing an opponent's gamestate works."""
    ppgs = PokemonPlayerGameState()

    # Initialize opponent's gamestate
    spinda = Pokemon(name="spinda", moves=["tackle"])
    gamestate = {}
    gamestate["team"] = []
    gamestate["active"] = spinda
    ppgs.init_opp_gamestate(gamestate["team"], gamestate["active"])

    # Assert that only investment was calculated
    assert ppgs.opp_gamestate["investment"]
    assert not ppgs.opp_gamestate["data"]
    assert not ppgs.opp_gamestate["moves"]

    # Assert that values were filled in properly
    assert ppgs.opp_gamestate["investment"]["spinda"]["hp"]
    assert [int(x) for x in ppgs.opp_gamestate["investment"]["spinda"]["spe"]
            ] == [140, 240]
def test_reset_gamestates():
    """Test that resetting a gamestate works."""
    ppgs = PokemonPlayerGameState()

    # Set the values in the gamestate
    ppgs.gamestate["doot"] = 7
    ppgs.opp_gamestate["data"]["pew"] = 71

    assert ppgs.gamestate
    assert ppgs.opp_gamestate["data"]
    assert not ppgs.opp_gamestate["moves"]
    assert not ppgs.opp_gamestate["investment"]

    # Now reset them
    ppgs.reset_gamestates()
    assert not ppgs.gamestate
    assert not ppgs.opp_gamestate["data"]
    assert not ppgs.opp_gamestate["moves"]
    assert not ppgs.opp_gamestate["investment"]
class PokemonAgent(BaseAgent):
    """
    Class for a pokemon player.

    Attributes:
        team (list): The team of pokemon this agent uses.
        gamestate (dict): This player's internal representation of a game.
        opp_gamestate (dict): This player's knowledge about the opponent's team in this game.
        dmg_stat_calc (DamageStatCalc): The class to do estimate damage using Damage Stats.

    """
    def __init__(self, team):
        """Initialize the agent."""
        if not team:
            raise AttributeError("Team must have at least one pokemon")

        super().__init__(type="PokemonAgent")
        self.team = team
        self.game_state = PokemonPlayerGameState()
        self.dmg_stat_calc = DamageStatCalc()

    def reset_gamestates(self):
        """Reset gamestate values for a new battle."""
        self.game_state.reset_gamestates()

    def init_opp_gamestate(self, opp_team, opp_active):
        """
        Initialize the investment data for the opponent's team.

        Args:
            opp_team (list): List with the opponent's Pokemon.
            opp_active (Pokemon): Opponent's active Pokemon.

        """
        self.game_state.init_opp_gamestate(opp_team, opp_active)

    def update_gamestate(self, my_gamestate, opp_gamestate):
        """
        Update internal gamestate for self.

        Args:
            my_gamestate (dict): PokemonEngine representation of player's position.
                Should have "active" and "team" keys.
            opp_gamestate (dict): PokemonEngine representation of opponent's position.
                Only % HP should be viewable, and has "active" and "team" keys.

        """
        self.game_state.update_gamestate(my_gamestate, opp_gamestate)

    def make_move(self):
        """
        Make a move.

        Either use random move or switch to random pokemon.

        Returns:
            Tuple with move type (ATTACK or SWITCH and the position.

        """
        response = ()
        active_can_switch, moves = self.game_state.gamestate[
            "active"].possible_moves()
        can_switch = len(
            self.game_state.gamestate["team"]) > 0 and active_can_switch

        if can_switch and random() < 0.5:
            switch = uniform(0, len(self.game_state.gamestate["team"]))
            switch = int(switch)
            response = "SWITCH", switch
        else:
            move_ind = uniform(0, len(moves))
            move_ind = int(move_ind)
            response = moves[move_ind]

        return response

    def switch_faint(self):
        """
        Choose switch-in after pokemon has fainted.

        For now pick a random pokemon.

        Returns:
            Position of the next pokemon to switch to.

        """
        choice = uniform(0, len(self.game_state.gamestate["team"]))
        choice = int(choice)
        return choice

    def battle_position(self):
        """
        Calculate the battle position function.

        Returns:
            This player's current % HP divided by the
                Opponent's current % HP.

        """
        self_component = self.calc_position()
        opp_component = self.calc_opp_position()

        return self_component / opp_component

    def calc_position(self):
        """
        Calculate the value for self's battle position.

        Returns:
            This player's remaining % HP.

        """
        return calc_position_helper(self.game_state.gamestate)

    def calc_opp_position(self):
        """
        Calculate the opponent's battle position.

        Returns:
            The opponent's remaining % HP.

        """
        return calc_opp_position_helper(self.game_state.opp_gamestate)

    def new_info(self, raw_turn_info):
        """
        Get new info for opponent's game_state.

        Assumes Species Clause is in effect.

        Args:
            turn_info (list): What happened on that turn, who took what damage.
            my_id (str): Name corresponding to the "attacker" or "defender"
                values of this dict. To know which values the method
                should be looking at in turn_info.
        """
        self.game_state.new_info(raw_turn_info, self.id)
def test_infer_investment():
    """Make sure investment is properly inferred."""
    magikarp = Pokemon(name="magikarp", moves=["tackle"])
    spinda = Pokemon(name="spinda", moves=["tackle"])

    ppgs1 = PokemonPlayerGameState()
    ppgs2 = PokemonPlayerGameState()

    # Set the gamestate
    gamestate = {}
    gamestate["team"] = []
    gamestate["active"] = magikarp
    opp_gamestate_dict = {}
    opp_gamestate_dict["team"] = []
    opp_gamestate_dict["active"] = spinda
    opp_gamestate = anonymize_gamestate_helper(opp_gamestate_dict)
    ppgs1.update_gamestate(gamestate, opp_gamestate)
    ppgs2.update_gamestate(opp_gamestate_dict,
                           anonymize_gamestate_helper(gamestate))

    # Set the new info
    new_info = {}
    new_info["type"] = "ATTACK"
    new_info["move"] = MOVE_DATA["tackle"]
    new_info["attacker"] = "player1"
    new_info["defender"] = "player2"
    new_info["pct_damage"] = 27
    new_info["damage"] = 46
    new_info["atk_poke"] = "spinda"
    new_info["def_poke"] = "magikarp"
    new_info = [new_info]

    test_infer_defending(ppgs2, new_info)
    test_infer_attacking(ppgs1, new_info)
    test_infer_speed_investment()

    # Test with misses
    ppgs1 = PokemonPlayerGameState()
    ppgs1.update_gamestate(gamestate, opp_gamestate)
    test_infer_miss(ppgs1)