Esempio n. 1
0
def range_no_params():
    """Range Calculations with no parameters."""
    dsc = DamageStatCalc()

    attacker = Pokemon(name="spinda", moves=["tackle"])
    defender = Pokemon(name="spinda", moves=["tackle"])
    move = generate_move(MOVE_DATA["tackle"])
    params = {}
    params["atk"] = {}
    params["def"] = {}
    params["hp"] = {}

    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 16
    assert dmg_range[1] == 20

    attacker = Pokemon(name="floatzel", moves=["watergun"])
    move = generate_move(MOVE_DATA['watergun'])
    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 21
    assert dmg_range[1] == 26

    defender = attacker
    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 10
    assert dmg_range[1] == 13
Esempio n. 2
0
def test_generate_move():
    """Test that the generate_move function properly generates a move."""
    # Test regular move is only a BaseMove
    tackle_move = generate_move(MOVE_DATA["tackle"])
    assert tackle_move.__class__.__bases__ == (BaseMove, )

    # Test a move that has Volatile Status is only SecondaryEffectMove
    uproar_move = generate_move(MOVE_DATA["uproar"])
    confuseray_move = generate_move(MOVE_DATA["confuseray"])

    assert uproar_move.__class__.__bases__ == (VolatileStatusMove, )
    assert confuseray_move.__class__.__bases__ == (VolatileStatusMove, )

    # Test Boosting Moves are only BoostingMove
    sd_move = generate_move(MOVE_DATA["swordsdance"])
    assert sd_move.__class__.__bases__ == (BoostingMove, )

    # Test OHKO moves
    sheercold_move = generate_move(MOVE_DATA["sheercold"])
    assert sheercold_move.__class__.__bases__ == (OHKOMove, )

    # Test Healing moves
    synthesis_move = generate_move(MOVE_DATA["synthesis"])
    assert synthesis_move.__class__.__bases__ == (HealingMove, )

    # Test Secondary Effect moves
    lowsweep_move = generate_move(MOVE_DATA["lowsweep"])
    pup_move = generate_move(MOVE_DATA["poweruppunch"])
    nuzzle_move = generate_move(MOVE_DATA["nuzzle"])
    assert lowsweep_move.__class__.__bases__ == (SecondaryEffectMove, )
    assert pup_move.__class__.__bases__ == (SecondaryEffectMove, )
    assert nuzzle_move.__class__.__bases__ == (SecondaryEffectMove, )
Esempio n. 3
0
def range_atk_params():
    """Range calculations with attack parameters."""
    dsc = DamageStatCalc()

    attacker = POKEMON_DATA["spinda"]
    defender = POKEMON_DATA["spinda"]
    move = generate_move(MOVE_DATA["tackle"])

    params = {}
    params["atk"] = {}
    params["atk"]["max_evs"] = True
    params["atk"]["positive_nature"] = True
    params["def"] = {}
    params["hp"] = {}

    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 25
    assert dmg_range[1] == 30

    attacker = POKEMON_DATA["exploud"]
    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 32
    assert dmg_range[1] == 39

    params["atk"]["positive_nature"] = False
    defender = POKEMON_DATA["floatzel"]
    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 26
    assert dmg_range[1] == 32
    def calc_move_outcomes(self, move_opt, player_flag=True):
        """
        For a move, generate possible outcomes.

        Considers Hit/Miss.

        Args:
            player_flag (bool): Whether to use player or opponent's gamestate.

        Returns:
            List of possible outcomes and their weights.

        """
        possible_outcomes = [move_opt + (True, )]
        outcome_weights = [1]
        if move_opt[0] == "ATTACK":
            if player_flag:
                chosen_move = self.game_state.gamestate["active"].moves[
                    move_opt[1]]
            else:
                chosen_move = generate_move(MOVE_DATA[move_opt[1]])

            acc = chosen_move["accuracy"]
            if not isinstance(acc, bool) and acc < 100:
                outcome_weights = [(1.0 * val) / 100
                                   for val in [acc, 100 - acc]]
                possible_outcomes = [
                    move_opt + (val == acc, ) for val in [acc, 100 - acc]
                ]

        return possible_outcomes, outcome_weights
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"] = generate_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)
Esempio n. 6
0
def range_status():
    """Test damage range with burn status."""
    dsc = DamageStatCalc()
    params = {}
    params["atk"] = {}
    params["def"] = {}
    params["hp"] = {}
    move = generate_move(MOVE_DATA["tackle"])

    attacker = Pokemon(name="spinda", moves=["tackle"])
    defender = Pokemon(name="spinda", moves=["tackle"])

    attacker.status = BRN_STATUS

    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 8
    assert dmg_range[1] == 10
Esempio n. 7
0
def range_hp_params():
    """Test calculations when using HP Parameters."""
    dsc = DamageStatCalc()

    attacker = POKEMON_DATA["spinda"]
    defender = POKEMON_DATA["spinda"]
    move = generate_move(MOVE_DATA["tackle"])

    params = {}
    params["atk"] = {}
    params["def"] = {}
    params["hp"] = {}
    params["hp"]["max_evs"] = True

    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 13
    assert dmg_range[1] == 16
    def determine_faster(self, my_gs, opp_gs, p_opt, o_opt):
        """
        Determine whether this player is faster.

        Args:
            my_gs (dict): This player's game state as a dictionary.
            opp_gs (dict): The opponent's game state as a dictionary.
            p_opt (tuple): The player's choice for this turn.
            o_opt (tuple): The opponent's choice for this turn.

        Returns:
            Boolean whether or not this player is faster than the opponent.

        """
        p_poke = my_gs["active"]
        o_poke_name = opp_gs["data"]["active"]["name"]

        p_move = p_poke.moves[p_opt[1]]
        o_move = generate_move(MOVE_DATA[o_opt[1]])

        # Same priority is decided by speed
        if p_move["priority"] == o_move["priority"]:
            speed_pairs = self.game_state.opp_gamestate["investment"][
                o_poke_name]["spe"]
            min_opp_spe, max_opp_spe = speed_pairs

            # Factor in status
            opp_modifier = 1
            if opp_gs["data"]["active"]["status"] == PAR_STATUS:
                opp_modifier = opp_modifier * 0.5

            # Factor in Boosts
            opp_modifier = opp_modifier * calc_boost_factor(
                opp_gs["data"]["active"], "spe")

            # Assume that any speed is possible, which isn't exactly correct
            return p_poke.effective_stat(
                "spe") > opp_modifier * (min_opp_spe + max_opp_spe) / 2

        # Moves of different priority will always go in priority order
        return p_move["priority"] > o_move["priority"]
    def defending_dmg_range(self, my_gs, opp_gs, o_opt):
        """
        Calculate the (weighted) damage range when attacked.

        Args:
            my_gs (dict): This player's (potential) game state.
            opp_gs (dict): The opponent's game state as a dictionary.
            o_opt (tuple): The opponent's choice for this turn.

        Returns:
            Expected damage range for an opponent's attack.
                Damage is calculated with each possible investment as equally likely.

        """
        p_poke = my_gs["active"]
        o_move = generate_move(MOVE_DATA[o_opt[1]])
        o_poke_name = opp_gs["data"]["active"]["name"]
        o_poke = POKEMON_DATA[o_poke_name]
        o_poke["status"] = opp_gs["data"]["active"]["status"]
        params = self.game_state.opp_gamestate["investment"][o_poke_name]

        # We do not handle status moves at this point in time.
        if o_move["category"] == "Status":
            return [0, 0]

        dmg_range = None
        param_combs = def_param_combinations(p_poke, params, o_move)
        for param_comb in param_combs:
            dmg_val = self.dmg_stat_calc.calculate_range(
                o_move, o_poke, p_poke, param_comb)
            if not dmg_range:
                dmg_range = [0, 0]

            dmg_range[0] += dmg_val[0]
            dmg_range[1] += dmg_val[1]

        # Each combination is weighted equally
        dmg_range[0] = dmg_range[0] / len(param_combs)
        dmg_range[1] = dmg_range[1] / len(param_combs)

        return dmg_range
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"] = generate_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)
Esempio n. 11
0
def range_def_params():
    """Test calculations when using defense parameters."""
    dsc = DamageStatCalc()

    attacker = POKEMON_DATA["spinda"]
    defender = POKEMON_DATA["spinda"]
    move = generate_move(MOVE_DATA["tackle"])

    params = {}
    params["atk"] = {}
    params["def"] = {}
    params["def"]["max_evs"] = True
    params["def"]["positive_nature"] = True
    params["hp"] = {}

    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 10
    assert dmg_range[1] == 13

    params["def"]["positive_nature"] = False
    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 11
    assert dmg_range[1] == 14
Esempio n. 12
0
def range_boosts():
    """Make sure that boosts impact the range."""
    # Setup
    dsc = DamageStatCalc()
    params = {}
    params["atk"] = {}
    params["def"] = {}
    params["hp"] = {}
    move = generate_move(MOVE_DATA["tackle"])

    attacker = Pokemon(name="spinda", moves=["tackle"])
    defender = Pokemon(name="spinda", moves=["tackle"])

    # With attacking boosts
    attacker.boosts["atk"] = 1
    dmg_range = dsc.calculate_range(move, attacker, defender, params)

    assert dmg_range[0] == 24
    assert dmg_range[1] == 29

    defender.boosts["def"] = -1
    dmg_range = dsc.calculate_range(move, attacker, defender, params)
    assert dmg_range[0] == 36
    assert dmg_range[1] == 44
def test_infer_miss(player_gs):
    """Infer on a miss."""
    new_info = [{
        'type': 'ATTACK',
        'move': generate_move(MOVE_DATA["hydropump"]),
        'critical_hit': False,
        'damage': 0,
        'pct_damage': 0.0,
        'attacker': "player2",
        'defender': "player1",
        'atk_poke': 'spinda',
        'def_poke': 'magikarp',
        'move_hits': False
    }]

    original_investment = player_gs.opp_gamestate["investment"]
    player_gs.new_info(new_info, "player1")

    # Got new move info
    assert "spinda" in player_gs.opp_gamestate["moves"]
    assert player_gs.opp_gamestate["moves"]["spinda"]

    # Did not do any inference on investment
    assert player_gs.opp_gamestate["investment"] == original_investment
Esempio n. 14
0
    def __init__(self, **kwargs):
        """
        Initialize a pokemon.

        Make a new instance of species <name> with moves <moves>
        at level <level> with nature <quirky>

        Args:
            name (str): String corresponding to value in config.POKEMON_DATA
            moves (list): List of moves corresponding to moves in config.MOVE_DATA
            level (int): Level of pokemon to be used in calculations
            nature (str): Pokemon nature to be used to modify stat values.
            evs (dict): Dictionary of key/value pairs with EVs for each stat.
                Key should be stat code, value should be number of EVs.

        """
        name = kwargs["name"]
        moves = kwargs["moves"]
        level = kwargs.get("level", 100)
        nature = kwargs.get("nature", "quirky")
        evs = kwargs.get("evs", {})

        # Validate pokemon chosen
        if name not in POKEMON_DATA:
            raise AttributeError("Invalid pokemon chosen: {}.".format(name))

        # Validate moves
        if not moves:
            raise AttributeError("Moves must be provided.")
        for move in moves:
            if move not in MOVE_DATA:
                raise AttributeError("Invalid move chosen: {}.".format(move))

        # Validate level
        if level not in range(1, 101):
            raise AttributeError("Level must be between 1 and 100")

        # Validate nature
        if nature not in NATURES:
            raise AttributeError("Invalid nature chosen: {}".format(nature))

        # Validate EVs
        for stat in evs:
            if evs[stat] < 0:
                raise AttributeError("EVs cannot be less than 0.")
            if evs[stat] > 255:
                raise AttributeError("EVs cannot exceed 255.")
            if not isinstance(evs[stat], int):
                raise AttributeError("EVs must be integer values.")

        self.name = name
        self.level = level
        self.moves = []
        for move in moves:
            self.moves.append(generate_move(MOVE_DATA[move]))
        self.types = POKEMON_DATA[self.name]["types"]
        self.base_stats = POKEMON_DATA[self.name]["baseStats"]
        self.dex_num = POKEMON_DATA[self.name]["num"]
        self.status = None
        self.status_turns = 0
        self.evs = evs
        self.increase_stat = None
        self.set_stats(nature, evs)
        self.boosts = default_boosts()
        self.volatile_status = {}