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
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, )
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)
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
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)
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
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
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 = {}