import random import math import copy import game_data as gd class Pokemon: def __init__(self, input_series): self.national_pokedex_number = input_series['national_pokedex_number'] self.name = input_series['name'] self.level = random.randint(1,20) self.types = input_series['types'] self.baseStats = input_series['baseStats'] self.moves_dict = {} moves = input_series['moves'] if len(moves) > 4: raise ValueError("A Pokemon can only have 4 moves.") else: for move in moves: #check if the move is in the available_moves dataframe if move not in gd.available_moves_df.index: raise ValueError(f"{move} is not an available move.") else: self.moves_dict[move] = copy.deepcopy(gd.available_moves_df.loc[move]) self.ActStats = self._compute_ActStats() self.curr_hp = self.ActStats['hp'] def _compute_ActStats(self): ActStats = {} for baseKey in self.baseStats.keys(): if baseKey == 'hp': ActStats[baseKey] = math.floor(self.baseStats[baseKey] * 2 * self.level / 100) + self.level + 10 else: ActStats[baseKey] = math.floor(self.baseStats[baseKey] * 2 * self.level / 100) + 5 return ActStats def _get_baseStats(self): return self.baseStats def get_current_hp(self): return self.curr_hp def get_current_hp_percent(self): return self.curr_hp / self.baseStats['hp'] def can_battle(self): condition1 = self.curr_hp > 0 condition2 = False for move in self.moves_dict: move_usage_avail = True #disregard pp in random battle #move_usage_avail = self.moves_dict[move]['pp'] > 0 if move_usage_avail: condition2 = True break else: condition2 = False return condition1 and condition2 def learnMove(self, move): if len(self.moves_dict) >= 4: raise ValueError("A Pokemon can only have 4 moves.") else: self.moves_dict[move] = copy.deepcopy(gd.available_moves_df[move]) def _compute_effect(self, move, opponent): #check the move type (attack) and the defending #pokemon type (defend) and look at effectiveness list. #this should be done for all type of the defending pokemon attack_move_type = move['type'] defending_types = opponent.get_types() effect = 1 for def_t in defending_types: for effectiveness_dataset in gd.type_effectiveness: if effectiveness_dataset['attack'] == attack_move_type and effectiveness_dataset['defend'] == def_t: effect = effect * effectiveness_dataset['effectiveness'] break return effect def _useMove(self, move, opponent): if random.random() < move['accuracy']: if move['category'] == 'physical': attack = self.ActStats['attack'] defense = opponent.ActStats['defense'] assert defense>0 else: attack = self.ActStats['special'] defense = opponent.ActStats['special'] assert defense>0 stability = 1.5 if move['type'] in self.types else 1 effect = self._compute_effect(move,opponent) critical = 2 if random.random() < self.ActStats['speed']/512 else 1 luck = random.uniform(0.85, 1.0) modifier = stability * effect * critical * luck damage = math.floor(((2*self.level+10)/250 * (attack/defense) * move['power'] + 2) * modifier) opponent.curr_hp -= damage if opponent.curr_hp < 0: opponent.curr_hp = 0 #print(f"{self.name} used {move['name']} and dealt {damage} damage to {opponent.name}!") else: damage = 0 #print(f"{self.name}'s attack missed!") return damage def useMove(self, move_name, opponent): if move_name not in self.moves_dict: raise ValueError(f"{self.name} does not know the move {move_name}.") else: return self._useMove(self.moves_dict[move_name], opponent) def print_move(self,move): print(f"Move: {self.moves_dict[move]['name']}") print(f"Type: {self.moves_dict[move]['type']}") print(f"Category: {self.moves_dict[move]['category']}") print(f"Power: {self.moves_dict[move]['power']}") print(f"Accuracy: {self.moves_dict[move]['accuracy']}") print(f"PP: {self.moves_dict[move]['pp']}") def get_moves(self): return list(self.moves_dict.keys()) def get_types(self): return self.types def checkout_move(self, move_name): if move_name not in self.moves_dict: raise ValueError(f"{self.name} does not know the move {move_name}.") else: self.print_move(move_name) def _status(self): print(f"Pokemon: {self.name}") print(f"Level: {self.level}") print(f"Types: {self.types}") print(f"Base Stats: {self.baseStats}") print(f"Current HP: {self.curr_hp}") print(f"Moves: {self.moves_dict}") def print_pokemon_info(self): self._status() class Trainer: def __init__(self, name, starter_name, items = {"potions": 0, "pokeball": 0}, is_CPU = False): self.name = name self.starter = starter_name self.active_pokemon = Pokemon(gd.pokemons_df.loc[starter_name]) self.pokemon_list = [] self.add_pokemon(self.active_pokemon) self.items = items self.is_CPU = is_CPU def add_pokemon(self, pokemon): if len(self.pokemon_list) >= 6: raise ValueError("A trainer can only have 6 Pokemon.") else: self.pokemon_list.append(pokemon) def get_pokemon_list(self): return [pok.name for pok in self.pokemon_list] def get_pokemon(self, name): for pokemon in self.pokemon_list: if pokemon.name == name: return pokemon raise ValueError(f"{name} is not in the trainer's Pokemon list.") def can_battle(self): for pokemon in self.pokemon_list: if pokemon.can_battle(): return True return False def active_pokemon_can_battle(self): return self.active_pokemon.can_battle() def _change_active_pokemon(self,new_pokemon): self.active_pokemon = new_pokemon def get_active_pokemon(self): return self.active_pokemon def change_active_pokemon(self,pokemon_name): try: new_pokemon = self.get_pokemon(pokemon_name) self._change_active_pokemon(new_pokemon) print("Pokemon change successfull!") except ValueError as e: raise e def print_items(self): print(f"1) Potions: {self.items['potions']}") print(f"2) Pokeballs: {self.items['pokeball']}") def use_item(self, item_name): if item_name not in self.items: raise ValueError(f"{item_name} is not an available item.") else: if self.items[item_name] > 0: self.items[item_name] -= 1 #use the item print(f"Used {item_name}. Remaining: {self.items[item_name]}") if item_name == "potions": self.active_pokemon.curr_hp += 20 if self.active_pokemon.curr_hp > self.active_pokemon.baseStats['hp']: self.active_pokemon.curr_hp = self.active_pokemon.baseStats['hp'] print(f"{self.active_pokemon.name} has been healed by 20 HP!") return False elif item_name == "pokeball": return True else: raise ValueError(f"You have no {item_name} left.") def update_pokemon_list(self, pokemon): pass def _status(self): print('='*30) print(f"Trainer name: {self.name}") print(f"Starter Pokemon: {self.starter}") print(f"Pokemon List: {[pok.name for pok in self.pokemon_list]}") print("Active Pokemon info:") self.active_pokemon.print_pokemon_info() print("Detailed info of Pokemons in your list:") print("-"*30) for pokemon in self.pokemon_list: pokemon.print_pokemon_info() print("-"*30) print(f"Items: {self.items}") print('='*30) def print_trainer_info(self): self._status()