From f046d06e21879debd662cc29e22b3c15008a027c Mon Sep 17 00:00:00 2001 From: Edoardo Vergerio Date: Sun, 6 Sep 2026 17:42:44 +0200 Subject: [PATCH] final corrections before submission --- .gitignore | 5 +- HW1/core.py | 47 +- HW1/hw1.py | 35 +- HW2/core.py | 2 +- HW2/hw2.py | 7 +- HW3/core.py | 4 - HW3/game_data.py | 8 +- HW3/hw3.py | 11 +- HW4/game_data.py | 5 +- HW4/hw4.py | 9 +- .../average_damage_per_turn_bulbasaur.pdf | Bin 11925 -> 12428 bytes .../average_damage_per_turn_charmander.pdf | Bin 13070 -> 12442 bytes .../average_damage_per_turn_squirtle.pdf | Bin 12966 -> 12519 bytes HW4/plots/pokemon_types_distribution.pdf | Bin 21287 -> 21257 bytes HW5/hw5.py | 602 +++++++++++++----- 15 files changed, 487 insertions(+), 248 deletions(-) diff --git a/.gitignore b/.gitignore index 00048f7..d357198 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ +HW3/results/* HW3/results +HW3/plots HW4/results +HW4/plots HW5/results HW5/plots HW5/plots/* HW5/dataset.csv -HW5/trained_model.joblib +HW5/trained_model.joblib \ No newline at end of file diff --git a/HW1/core.py b/HW1/core.py index 7bc0191..b20f237 100644 --- a/HW1/core.py +++ b/HW1/core.py @@ -19,11 +19,8 @@ class Pokemon: raise ValueError(f"{move} is not an available move.") else: self.moves_dict[move] = copy.deepcopy(gd.available_moves[move]) - print(gd.available_moves[move]['PP']) - print("TYPE", type(gd.available_moves[move])) self.curr_hp = self.baseStats['hp'] - def _get_baseStats(self): return self.baseStats @@ -49,6 +46,7 @@ class Pokemon: self.moves_dict[move] = gd.available_moves[move] def _useMove(self, move, opponent): + win_flag = False if move['PP'] > 0: if random.random() < move['accuracy']: if move['category'] == 'physical': @@ -64,29 +62,32 @@ class Pokemon: 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: + if opponent.curr_hp < 0 or opponent.curr_hp == 0: opponent.curr_hp = 0 + win_flag = True + print(f"{opponent.name} has fainted!") print(f"{self.name} used {move['name']} and dealt {damage} damage to {opponent.name}!") + print("CPU's Pokemon current HP: ", opponent.curr_hp) move['PP'] = move['PP'] - 1 else: print(f"{self.name}'s attack missed!") move['PP'] = move['PP'] - 1 else: print(f"{self.name} has no PP left for {move['name']}!") - pass + return win_flag 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}.") + raise ValueError(f"{self.name} does not know the move {move_name}.") else: - self._useMove(self.moves_dict[move_name], opponent) + 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"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"Accuracy: {self.moves_dict[move]['accuracy']}") print(f"PP: {self.moves_dict[move]['PP']}") def get_moves(self): @@ -111,7 +112,7 @@ class Pokemon: class Trainer: - def __init__(self, name, starter_name, items = {"potions": 0, "pokeball": 0}, is_CPU = False): + def __init__(self, name, starter_name, items = {}, is_CPU = False): self.name = name if is_CPU: if starter_name not in gd.available_wild_pokemons: @@ -171,28 +172,6 @@ class Trainer: 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 @@ -203,7 +182,7 @@ class Trainer: 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("Detailed info of Pokemons in the list:") print("-"*30) for pokemon in self.pokemon_list: pokemon.print_pokemon_info() diff --git a/HW1/hw1.py b/HW1/hw1.py index 2f6df94..d1494bd 100644 --- a/HW1/hw1.py +++ b/HW1/hw1.py @@ -1,8 +1,5 @@ import random -import math - import game_data as gd - from core import Trainer def Main(): @@ -19,11 +16,11 @@ def Main(): print(e) print("Please try again...") continue - trainer.print_trainer_info() + #trainer.print_trainer_info() - cpu_trainer = Trainer(name="CPU", starter_name=random.choice(gd.available_starter_names)) + cpu_trainer = Trainer(name="CPU", starter_name=random.choice(gd.available_wild_pokemon_names), is_CPU=True) print(f"CPU has chosen {cpu_trainer.get_active_pokemon().name} as their starter Pokemon!") - cpu_trainer.print_trainer_info() + #cpu_trainer.print_trainer_info() continue_playing = True while continue_playing: selected_pokemon_combat_flag = False @@ -42,7 +39,6 @@ def Main(): print(f"Choose a move for {player_pokemon.name} to use against {cpu_trainer.get_active_pokemon().name}:\n", player_pokemon.get_moves()) player_move_name = input("Choose:") try: - #print the move selected by the player print("Your selected move: ") player_pokemon.checkout_move(player_move_name) selected_combat_move_flag = True @@ -50,21 +46,24 @@ def Main(): print(e) print("Please try again...") - #make a combat + #combat print('='*30) print("BATTLE RESULTS:") print("Your move:") - player_pokemon.useMove(player_move_name, cpu_trainer.get_active_pokemon()) - player_pokemon.checkout_move(player_move_name) - print('-'*30) - print("CPU's pokemon") - cpu_trainer.get_active_pokemon().print_pokemon_info() - print('='*30) - - print("Continue playing? (yes/no)") - response = input().strip().lower() - if response != "yes": + opponent_fainted = player_pokemon.useMove(player_move_name, cpu_trainer.get_active_pokemon()) + #player_pokemon.checkout_move(player_move_name) + #print('-'*30) + #print("CPU's pokemon") + #cpu_trainer.get_active_pokemon().print_pokemon_info() + #print('='*30) + if opponent_fainted: + print("You won the battle!") continue_playing = False + else: + print("Continue playing? (yes/no)") + response = input().strip().lower() + if response != "yes": + continue_playing = False if __name__ == "__main__": diff --git a/HW2/core.py b/HW2/core.py index d263b82..ea38a82 100644 --- a/HW2/core.py +++ b/HW2/core.py @@ -178,7 +178,7 @@ class Trainer: 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 + self.items[item_name] -= 1 print(f"Used {item_name}. Remaining: {self.items[item_name]}") if item_name == "potions": self.active_pokemon.curr_hp += 20 diff --git a/HW2/hw2.py b/HW2/hw2.py index 4a0aced..08b2c66 100644 --- a/HW2/hw2.py +++ b/HW2/hw2.py @@ -160,7 +160,7 @@ def Main(): print("Adding potions and pokeballs to your inventory...") trainer.items = {"potions": 10, "pokeball": 10} print("Your character is ready!") - trainer.print_trainer_info() + #trainer.print_trainer_info() player_created_flag = True print(f"Welcome {trainer.name}! Your adventure begins now!") except ValueError as e: @@ -201,9 +201,9 @@ def Main(): battle_prob = random.uniform(0, 1) print(f"Battle probability: {battle_prob:.2f}") if battle_prob < 0.8: - print("A wild Pokemon appeared!") cpu_trainer = Trainer(name='CPU',starter_name=gd.available_wild_pokemon_names[random.randint(0,len(gd.available_wild_pokemon_names)-1)],is_CPU=True) - print(cpu_trainer.get_active_pokemon().print_pokemon_info()) + print(f"A wild {cpu_trainer.get_active_pokemon().name} appeared!") + #print(cpu_trainer.get_active_pokemon().print_pokemon_info()) game_status = 'battle' Battle(trainer,cpu_trainer) else: @@ -224,7 +224,6 @@ def Main(): else: print("You exited the Pokemon Center.") game_status = 'story' - # Add Pokemon Center logic here elif game_status == 'pokemon_store': print("You are in the Pokemon Store. You can buy items here.") restore_items_flag = input("Type 'yes' to restore your items: ").strip().lower() diff --git a/HW3/core.py b/HW3/core.py index 6ed7e09..56865be 100644 --- a/HW3/core.py +++ b/HW3/core.py @@ -21,7 +21,6 @@ class Pokemon: self.moves_dict[move] = copy.deepcopy(gd.available_moves[move]) self.curr_hp = self.baseStats['hp'] - def _get_baseStats(self): return self.baseStats @@ -51,9 +50,6 @@ class Pokemon: self.moves_dict[move] = copy.deepcopy(gd.available_moves[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() diff --git a/HW3/game_data.py b/HW3/game_data.py index 9984353..80bd04c 100644 --- a/HW3/game_data.py +++ b/HW3/game_data.py @@ -1,9 +1,9 @@ import json import random -#load moves from json file -#eclude moves with power equal to null, -# do not consider the effect,effects or changes fields + +#exclude moves with power equal to null, +#do not consider the effect,effects or changes fields available_moves = dict() keys_to_skip = ['effect','effects','changes'] with open('moves.json', 'r') as file: @@ -33,7 +33,7 @@ with open('pokemons.json', 'r') as file: for m in available_moves.keys(): if available_moves[m]['type'] in current_pokemon_types: compatible_moves.append(m) - #select two random moves + #select two (compatible) random moves selected_moves = [random.choice(compatible_moves) for _ in range(2)] pokemons_dict[p['name']]['moves'] = selected_moves diff --git a/HW3/hw3.py b/HW3/hw3.py index af69bb9..ffbce59 100644 --- a/HW3/hw3.py +++ b/HW3/hw3.py @@ -1,3 +1,4 @@ +import os from core import Trainer import random @@ -68,11 +69,7 @@ def Main(starter_choice,Nbattles): try: starter_name = starter_choice trainer = Trainer(name='edo', starter_name=starter_name) - #print(f"{trainer.name} has chosen {trainer.starter} as their starter Pokemon!") - #print("Adding potions and pokeballs to your inventory...") trainer.items = {"potions": 10, "pokeball": 10} - #print("Your character is ready!") - #print(f"Welcome {trainer.name}! Your adventure begins now!") except ValueError as e: print(e) print("Something went wrong!") @@ -98,6 +95,8 @@ def Main(starter_choice,Nbattles): def make_plots(Ngames,Nbattles): #SIMPLE PLOT + if os.path.exists('plots') == False: + os.makedirs('plots') cumulative_victories = np.zeros((Ngames,Nbattles)) plt.figure(figsize=(10, 6)) for starter_choice in gd.available_starters: @@ -171,6 +170,10 @@ def make_plots(Ngames,Nbattles): if __name__ == "__main__": Ngames = 500 Nbattles = 100 + if os.path.exists('results') == False: + os.makedirs('results') + if os.path.exists('plots') == False: + os.makedirs('plots') for starter_choice in gd.available_starters: for i in range(Ngames): print(f"Generating data for {starter_choice}, game {i}...") diff --git a/HW4/game_data.py b/HW4/game_data.py index 3393341..01d6935 100644 --- a/HW4/game_data.py +++ b/HW4/game_data.py @@ -56,6 +56,5 @@ with open('type_effectiveness.json', 'r') as file: type_effectiveness.append(json.loads(line)) type_effectiveness_df = pd.DataFrame.from_dict(type_effectiveness) #NOTE: from a practical point of view, since type_effectiveness is accessed many times -#in the main code, using pandas DataFrame is not efficient. I decided to keep using dictionaries -#for the type effectiveness. The justification arises especially if we consider -#how long data generation will take. \ No newline at end of file +#in the main code I decided to keep using dictionaries +#for the type effectiveness \ No newline at end of file diff --git a/HW4/hw4.py b/HW4/hw4.py index 017e8e2..c615879 100644 --- a/HW4/hw4.py +++ b/HW4/hw4.py @@ -7,7 +7,6 @@ import pickle import matplotlib.pyplot as plt import numpy as np import os -np.seterr(divide='raise', invalid='raise') #if the results folder does not exist, create it if not os.path.exists('results'): @@ -220,17 +219,19 @@ def make_plots(Ngames,Nbattles): plt.savefig('plots/pokemon_types_distribution.pdf') - if __name__ == "__main__": Ngames = 1000 Nbattles = 150 + print("Generating data") for i in range(Ngames): + #automatic player creation (choose a random starter pokemon) starter_choice = random.choice(gd.available_starters) - #print(f"Generating data for {starter_choice}, game {i}...") + print(f"Generating data for {starter_choice}, game {i}...") results = Main(starter_choice = starter_choice, Nbattles=Nbattles) pickle_out = open(f"results/{starter_choice}_game_{i}_{Nbattles}_battles.pickle",'wb') pickle.dump(results,pickle_out) pickle_out.close() print("Generating plots") - make_plots(Ngames, Nbattles) \ No newline at end of file + make_plots(Ngames, Nbattles) + print("Finished!") \ No newline at end of file diff --git a/HW4/plots/average_damage_per_turn_bulbasaur.pdf b/HW4/plots/average_damage_per_turn_bulbasaur.pdf index ca043259fd549690076ff3aabf147005650e3e40..1ae1b2f69571fcd46bdba08a4f39502b7a7b57b7 100644 GIT binary patch delta 3570 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 O5-+@Sr_mu};r{}=Nu9|6 delta 3139 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 ZB>NzXeUwEHf}j8r8V5ljD6V#p{{Ssh!Vv%f diff --git a/HW4/plots/average_damage_per_turn_charmander.pdf b/HW4/plots/average_damage_per_turn_charmander.pdf index 44b2a1f518834e285dc4dd317f568688d1484ade..ce389d372f27ec20026bae0ccbe0774f3281f7ef 100644 GIT binary patch delta 3501 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 U#*dlAVzsnD9wjA{W2QX+1>pM>Z~y=R diff --git a/HW4/plots/average_damage_per_turn_squirtle.pdf b/HW4/plots/average_damage_per_turn_squirtle.pdf index d3515bcbb6c24ae0b60ba366ffd5b7654a084a3a..f7a6b68a82c0d0338ae89a6a9139fc7898557ed6 100644 GIT binary patch delta 3299 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 delta 3656 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 I+-?`>Kba%7`v3p{ diff --git a/HW4/plots/pokemon_types_distribution.pdf b/HW4/plots/pokemon_types_distribution.pdf index e9fbad28e87b5674920af01720aa4928b2e7d1d3..8e458d7b7ae3b7f3346977e5fd9c4a78cdad63fb 100644 GIT binary patch delta 6527 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 delta 6506 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 diff --git a/HW5/hw5.py b/HW5/hw5.py index 94c3761..51a3707 100644 --- a/HW5/hw5.py +++ b/HW5/hw5.py @@ -1,22 +1,199 @@ from core import Trainer - +import game_data as gd import random import copy -import game_data as gd +import numpy as np import pickle import joblib -import matplotlib.pyplot as plt -import numpy as np import os import pandas as pd +from sklearn.model_selection import train_test_split +from sklearn.ensemble import RandomForestClassifier +from sklearn.metrics import accuracy_score, classification_report, confusion_matrix +from sklearn.model_selection import RandomizedSearchCV +from scipy.stats import randint +from sklearn.tree import export_graphviz +import graphviz -np.seterr(divide='raise', invalid='raise') +game_status = 'story' +game_active_flag = True +if os.path.exists('trained_model.joblib'): + recommender_sys_avail = True +else: + recommender_sys_avail = False -#if the results folder does not exist, create it -if not os.path.exists('results'): - os.makedirs('results') -if not os.path.exists('plots'): - os.makedirs('plots') +def recommend(player_pokemon, opponent_pokemon): + global recommender_sys_avail + if recommender_sys_avail: + model = joblib.load('trained_model.joblib') + + # Prepare input data + all_pokemon_types = list(gd.available_types) + player_ActStats = list(player_pokemon.ActStats.values()) + player_pokemon_type = player_pokemon.types + player_pokemon_type_encoded = [1 if t in player_pokemon_type else 0 for t in all_pokemon_types] + opponent_ActStats = list(opponent_pokemon.ActStats.values()) + opponent_pokemon_type = opponent_pokemon.types + opponent_pokemon_type_encoded = [1 if t in opponent_pokemon_type else 0 for t in all_pokemon_types] + + input_data = player_ActStats + player_pokemon_type_encoded + opponent_ActStats + opponent_pokemon_type_encoded + labels = build_dataset()[:-1] # Exclude the last label which is 'battle_result' + df_to_eval = pd.DataFrame([input_data], columns=labels) + + prediction_proba = model.predict_proba(df_to_eval) + return prediction_proba[0][1] # Returns 1 if prediction is a win, 0 otherwise, and the probability of winning + else: + raise ValueError("Recommender system is not available. Play NewRandomBattle once!") +def Battle(trainer,cpu_trainer): + global game_status + global game_active_flag + print(f"You are in battle against CPU's {cpu_trainer.get_active_pokemon().name}!") + battle_active = True + + while battle_active: + player_battle_action_selected = False + while not player_battle_action_selected: + print("Type the number of one of the following actions:") + print(f"1) Attack - [Your active Pokemon - {trainer.get_active_pokemon().name}]") + print("2) Change pokemon") + print("3) Use item") + print("4) Consult the Oracle") + print("5) Run away") + print('6) Check status') + + try: + action = int(input("Choose an action (1-6): ").strip()) + player_battle_action_selected = True + except ValueError: + print("Invalid input. Please enter a number between 1 and 6.") + continue + + if action == 1: + #check if the player can sustain a battle: first check the active pokemon, + #then check the pokemons in the list + if not trainer.active_pokemon_can_battle(): + print("Your active pokemon cannot battle!") + if not trainer.can_battle(): + print("You have no more pokemon that can battle! You lost the battle!") + battle_active = False + game_status = 'pokemon_center' + break + else: + print("You have other pokemon that can battle! Please change your active pokemon!") + continue + + selected_combat_move_flag = False + while not selected_combat_move_flag: + active_pokemon = trainer.get_active_pokemon() + cpu_active_pokemon = cpu_trainer.get_active_pokemon() + print(f"Choose a move for {active_pokemon.name} to use against {cpu_active_pokemon.name}:\n", active_pokemon.get_moves()) + player_move_name = input("Choose:") + try: + active_pokemon.useMove(player_move_name,cpu_active_pokemon) + selected_combat_move_flag = True + #check if the opponent has been defeated + if not cpu_trainer.active_pokemon_can_battle(): + print(f"{cpu_trainer.get_active_pokemon().name} has been defeated!") + battle_active = False + game_status = 'story' + break + + #if the opponent has not been defeated, then its the CPU turn to play + cpu_active_pokemon_selected_move_name = random.choice(cpu_active_pokemon.get_moves()) + print(f"CPU's {cpu_active_pokemon.name} is using {cpu_active_pokemon_selected_move_name} against your {active_pokemon.name}!") + cpu_active_pokemon.useMove(cpu_active_pokemon_selected_move_name, active_pokemon) + + #update the player pokemon list + trainer.update_pokemon_list(active_pokemon) + #check if the player has been defeated + if not trainer.active_pokemon_can_battle(): + print(f"Your {active_pokemon.name} has been defeated!") + if not trainer.can_battle(): + print("You have no more Pokemons that can battle! You lost!") + battle_active = False + game_status = 'pokemon_center' + break + else: + print("You have other Pokemons that can battle!") + continue + except ValueError as e: + print(e) + print("Please try again...") + + elif action == 2: + print('='*30) + print("Change active pokemon") + print('-'*30) + change_pokemon_success = False + while not change_pokemon_success: + try: + print("Select the name of a pokemon in your list:") + print(trainer.get_pokemon_list()) + chosen_new_pokemon_name = input('Choose...') + trainer.change_active_pokemon(chosen_new_pokemon_name) + change_pokemon_success = True + except ValueError as e: + print(e) + + elif action == 3: + print("Select the item index to use:") + trainer.print_items() + item_selected_flag = False + while not item_selected_flag: + try: + avail_items = list(trainer.items.keys()) + item_index = int(input("Choose an item index (1-2): ").strip()) + item_selected_flag = True + is_pokeball = trainer.use_item(item_name=avail_items[item_index - 1]) + if is_pokeball: + catch_prob = 1 - (cpu_trainer.get_active_pokemon().get_current_hp() / cpu_trainer.get_active_pokemon().baseStats['hp']) + random_value = random.uniform(0, 1) + if random_value < catch_prob: + print(f"You caught {cpu_trainer.get_active_pokemon().name}!") + trainer.add_pokemon(cpu_trainer.get_active_pokemon()) + battle_active = False + game_status = 'story' + else: + print(f"{cpu_trainer.get_active_pokemon().name} escaped your pokeball!") + except ValueError: + print("Please enter a number between 1 and 2.") + continue + + elif action == 4: + global recommender_sys_avail + if recommender_sys_avail: + try: + for pokemon in trainer.pokemon_list: + win_prob = recommend(player_pokemon=pokemon, opponent_pokemon=cpu_trainer.get_active_pokemon()) + print(f"The Oracle predicts {pokemon.name} to have {win_prob:.2f} of chances to win against {cpu_trainer.get_active_pokemon().name}.") + except ValueError as e: + print(e) + + elif action == 5: + runaway_prob = 0.6 + random_value = random.uniform(0, 1) + if random_value < runaway_prob: + print("You successfully ran away from the battle!") + battle_active = False + game_status = 'story' + else: + print("You could not run away from the battle!") + + elif action == 6: + print('='*30) + print("BATTLE INFO") + print('-'*30) + print("Your items:") + trainer.print_items() + print("Your pokemon list info:") + for pokemon in trainer.pokemon_list: + pokemon.print_pokemon_info() + print("-"*30) + print("CPU active pokemon info:") + cpu_trainer.get_active_pokemon().print_pokemon_info() + print('='*30) + else: + print("Invalid action. Please choose again.") def RandomBattle(trainer,cpu_trainer): #print(f"You are in battle against {cpu_trainer.name}!") @@ -34,13 +211,20 @@ def RandomBattle(trainer,cpu_trainer): cpu_hp_evolution = [] cpu_selected_moves = [] cpu_damage_done = [] + zero_damage_counter = 0 + zero_damage_max_turns = 10 while battle_active: + #print(f"Starting battle turn {battle_turns_num + 1}") player_move_name = random.choice(active_pokemon.get_moves()) player_selected_moves.append(player_move_name) #print(f"{active_pokemon.name} selected {player_move_name} to use against {cpu_active_pokemon.name}:\n") try: - player_damage_done_value = active_pokemon.useMove(player_move_name,cpu_active_pokemon) - + player_damage_done_value = active_pokemon.useMove(player_move_name,cpu_active_pokemon,game_status='new_random_battle') + if player_damage_done_value == 0: + zero_damage_counter += 1 + else: + zero_damage_counter = 0 + #print(f"Player did {player_damage_done_value} damage with {player_move_name}") player_damage_done.append(player_damage_done_value) player_hp_evolution.append(active_pokemon.get_current_hp()) cpu_hp_evolution.append(cpu_active_pokemon.get_current_hp()) @@ -58,7 +242,11 @@ def RandomBattle(trainer,cpu_trainer): #print(f"CPU's {cpu_active_pokemon.name} is using {cpu_active_pokemon_selected_move_name} against your {active_pokemon.name}!") cpu_selected_moves.append(cpu_active_pokemon_selected_move_name) - cpu_damage_done_value = cpu_active_pokemon.useMove(cpu_active_pokemon_selected_move_name, active_pokemon) + cpu_damage_done_value = cpu_active_pokemon.useMove(cpu_active_pokemon_selected_move_name, active_pokemon,game_status='new_random_battle') + if cpu_damage_done_value == 0: + zero_damage_counter += 1 + else: + zero_damage_counter = 0 cpu_damage_done.append(cpu_damage_done_value) player_hp_evolution.append(active_pokemon.get_current_hp()) @@ -67,7 +255,7 @@ def RandomBattle(trainer,cpu_trainer): #update the player pokemon list trainer.update_pokemon_list(active_pokemon) - #check if the player has been defeated + if not trainer.active_pokemon_can_battle(): #print(f"Your {active_pokemon.name} has been defeated!") if not trainer.can_battle(): @@ -76,12 +264,16 @@ def RandomBattle(trainer,cpu_trainer): battle_active = False break else: - #print("You have other Pokemons that can battle!") + print("You have other Pokemons that can battle!") continue - + if zero_damage_counter >= zero_damage_max_turns: + print("Zero damage detected for too many consecutive turns. Ending battle.") + battle_active = False + did_player_won = False + break except ValueError as e: print(e) - + flag_1 = True #manage those cases for which the player wins in the first turn and the cpu does not play any turn if len(player_damage_done) == 0: player_damage_done = [0] @@ -116,112 +308,6 @@ def RandomBattle(trainer,cpu_trainer): } return battle_results -def Main(starter_choice,Nbattles): - try: - starter_name = starter_choice - trainer = Trainer(name='edo', starter_name=starter_name) - #print(f"{trainer.name} has chosen {trainer.starter} as their starter Pokemon!") - #print("Adding potions and pokeballs to your inventory...") - trainer.items = {"potions": 10, "pokeball": 10} - #print("Your character is ready!") - #print(f"Welcome {trainer.name}! Your adventure begins now!") - except ValueError as e: - print(e) - print("Something went wrong!") - - game_active_flag = True - while game_active_flag: - all_battle_results = [] - for i in range(0,Nbattles): - #print("You entered the Random Battle mode") - #print("Battle #",i) - cpu_trainer = Trainer(name='CPU',starter_name=gd.pokemons_df.index[random.randint(0,len(gd.pokemons_df.index)-1)],is_CPU=True) - battle_results = RandomBattle(trainer,cpu_trainer) - all_battle_results.append(battle_results) - heal_flag = 'yes' - if heal_flag == 'yes': - for pokemon in trainer.pokemon_list: - pokemon.curr_hp = pokemon.baseStats['hp'] - #print("All your Pokemon have been healed!") - - game_active_flag = False - #save all battle results - return all_battle_results - -def make_plots(Ngames,Nbattles): - files_to_analyze = os.listdir('results/') - - avg_damage_per_level = dict() - for file_name in files_to_analyze: - pickle_in = open(f"results/{file_name}",'rb') - all_battle_results = pickle.load(pickle_in) - pickle_in.close() - for battle_result in all_battle_results: - player_pokemon = battle_result['player_pokemon'] - player_pokemon_name = player_pokemon.name - player_pokemon_level = player_pokemon.level - player_avg_damage_done = battle_result['turn_history']['player_history']['player_avg_damage_done'] - - if player_pokemon_name not in avg_damage_per_level: - avg_damage_per_level[player_pokemon_name] = {} - if player_pokemon_level not in avg_damage_per_level[player_pokemon_name]: - avg_damage_per_level[player_pokemon_name][player_pokemon_level] = [] - avg_damage_per_level[player_pokemon_name][player_pokemon_level].append(player_avg_damage_done) - for pokemon_name in avg_damage_per_level: - for level in avg_damage_per_level[pokemon_name]: - avg_damage_per_level[pokemon_name][level] = np.mean(avg_damage_per_level[pokemon_name][level]) - - for pokemon_name in avg_damage_per_level: - levels = list(avg_damage_per_level[pokemon_name].keys()) - avg_damages = list(avg_damage_per_level[pokemon_name].values()) - plt.figure(figsize=(12, 6)) - plt.bar(levels, avg_damages) - plt.xlabel('Pokemon Level') - plt.ylabel('Average Damage Done') - plt.title(f'Average Damage Done per battle turn for {pokemon_name}') - plt.xticks(rotation=45) - plt.tight_layout() - plt.savefig(f'plots/average_damage_per_turn_{pokemon_name}.pdf') - - #pie chart - available_pokemon_type_distribution = {} - for pokemon_name in gd.pokemons_dict: - pokemon_types = gd.pokemons_dict[pokemon_name]['types'] - for pokemon_type in pokemon_types: - if pokemon_type not in available_pokemon_type_distribution: - available_pokemon_type_distribution[pokemon_type] = 0 - available_pokemon_type_distribution[pokemon_type] += 1 - - encountered_pokemon_types = {} - for file_name in files_to_analyze: - pickle_in = open(f"results/{file_name}",'rb') - all_battle_results = pickle.load(pickle_in) - pickle_in.close() - for battle_result in all_battle_results: - encountered_pokemon = battle_result['encountered_pokemon'] - encountered_pokemon_types_list = encountered_pokemon.types - for pokemon_type in encountered_pokemon_types_list: - if pokemon_type not in encountered_pokemon_types: - encountered_pokemon_types[pokemon_type] = 0 - encountered_pokemon_types[pokemon_type] += 1 - - quantities = list(encountered_pokemon_types.values()) - labels = list(encountered_pokemon_types.keys()) - plt.figure(figsize=(12, 6)) - plt.subplot(1, 2, 1) - plt.pie(quantities, labels=labels, autopct='%1.1f%%', startangle=140) - plt.title('Distribution of Encountered Pokemon Types') - plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle - - plt.subplot(1, 2, 2) - quantities_base = list(available_pokemon_type_distribution.values()) - labels_base = list(available_pokemon_type_distribution.keys()) - plt.pie(quantities_base, labels=labels_base, autopct='%1.1f%%', startangle=140) - plt.title('Distribution of Available Pokemon Types') - plt.axis('equal') - - plt.savefig('plots/pokemon_types_distribution.pdf') - def build_dataset(): all_pokemon_types = list(gd.available_types) player_ActStats_labels = ["player_hp", "player_attack", "player_defense", "player_speed", "player_special"] @@ -229,39 +315,48 @@ def build_dataset(): opponent_ActStats_labels = ["opponent_hp", "opponent_attack", "opponent_defense", "opponent_speed", "opponent_special"] opponent_type_label = ['opponent_type_' + t for t in all_pokemon_types] - #define a padas dataframe to store the dataset + #define a pandas dataframe to store the dataset dataset_column_labels = player_ActStats_labels + player_type_label + opponent_ActStats_labels + opponent_type_label + ['battle_result'] + + if os.path.exists('dataset.csv'): + return dataset_column_labels + dataset = pd.DataFrame(columns=dataset_column_labels) - files_to_analyze = os.listdir('results/') - dataset_row = [] - for file_name in files_to_analyze: - pickle_in = open(f"results/{file_name}",'rb') - all_battle_results = pickle.load(pickle_in) - pickle_in.close() + dataset_rows = [] + num_of_files = len(files_to_analyze) + for i, file_name in enumerate(files_to_analyze): + print(f"Processing file {i + 1}/{num_of_files}: {file_name}") + + with open(f"results/{file_name}", "rb") as pickle_in: + all_battle_results = pickle.load(pickle_in) + for battle_result in all_battle_results: - player_pokemon_ActStats = list(battle_result['player_pokemon'].ActStats.values()) - player_pokemon_type = battle_result['player_pokemon'].types - player_pokemon_type_encoded = [1 if t in player_pokemon_type else 0 for t in all_pokemon_types] - opponent_pokemon = list(battle_result['encountered_pokemon'].ActStats.values()) - opponent_pokemon_type = battle_result['encountered_pokemon'].types - opponent_pokemon_type_encoded = [1 if t in opponent_pokemon_type else 0 for t in all_pokemon_types] - battle_result_integer = 1 if battle_result['result'] else 0 - dataset_row = player_pokemon_ActStats + player_pokemon_type_encoded + opponent_pokemon + opponent_pokemon_type_encoded + [battle_result_integer] - #append the row to the dataset - dataset.loc[len(dataset)] = dataset_row + player_pokemon = battle_result["player_pokemon"] + opponent_pokemon = battle_result["encountered_pokemon"] + + player_stats = list(player_pokemon.ActStats.values()) + player_types = [ + int(pokemon_type in player_pokemon.types) + for pokemon_type in all_pokemon_types + ] + opponent_stats = list(opponent_pokemon.ActStats.values()) + opponent_types = [ + int(pokemon_type in opponent_pokemon.types) + for pokemon_type in all_pokemon_types + ] + result = int(battle_result["result"]) + + dataset_rows.append( + player_stats + player_types + opponent_stats + opponent_types + [result] + ) + + dataset = pd.DataFrame(dataset_rows, columns=dataset_column_labels) + dataset.to_csv("dataset.csv", index=False) - #save the dataset to a csv file - dataset.to_csv('dataset.csv', index=False) return dataset_column_labels def train_model(dataset_column_labels): - from sklearn.model_selection import train_test_split - from sklearn.ensemble import RandomForestClassifier - from sklearn.metrics import accuracy_score, classification_report, confusion_matrix - from sklearn.model_selection import RandomizedSearchCV - from scipy.stats import randint - dataset_labels = dataset_column_labels[:-1] # Load the dataset dataset = pd.read_csv('dataset.csv') @@ -276,13 +371,14 @@ def train_model(dataset_column_labels): # Create a Random Forest Classifier model = RandomForestClassifier() param_dist = { - 'n_estimators': randint(50, 500), - 'max_depth': randint(1, 20), + 'n_estimators': randint(30, 300), + 'max_depth': randint(2, 30), } + #RandomizedSearchCV is used for parameter tuning. rand_search = RandomizedSearchCV(model, - param_distributions=param_dist, - n_iter=5, cv=5).fit(X_train, y_train) + param_distributions=param_dist, + n_iter=7, cv=5, n_jobs=3).fit(X_train, y_train) # Train the model best_model = rand_search.best_estimator_ @@ -299,8 +395,6 @@ def train_model(dataset_column_labels): print("Confusion Matrix:") print(confusion_matrix(y_test, y_pred)) - from sklearn.tree import export_graphviz - import graphviz # Visualize the first three estimators for i in range(3): tree = best_model.estimators_[i] @@ -313,23 +407,189 @@ def train_model(dataset_column_labels): graph = graphviz.Source(dot_data) graph.render(outfile=f"plots/tree_{i}.pdf", format='pdf') - return best_model + # Save the trained model + joblib.dump(best_model, 'trained_model.joblib') + +def MainRandomBattle(starter_choice,Nbattles): + try: + starter_name = starter_choice + trainer = Trainer(name='trainer', starter_name=starter_name) + trainer.items = {"potions": 10, "pokeball": 10} + except ValueError as e: + print(e) + print("Something went wrong!") + + game_active_flag = True + while game_active_flag: + all_battle_results = [] + for i in range(0,Nbattles): + cpu_trainer = Trainer(name='CPU',starter_name=gd.pokemons_df.index[random.randint(0,len(gd.pokemons_df.index)-1)],is_CPU=True) + battle_results = RandomBattle(trainer,cpu_trainer) + all_battle_results.append(battle_results) + heal_flag = 'yes' + if heal_flag == 'yes': + for pokemon in trainer.pokemon_list: + pokemon.curr_hp = pokemon.baseStats['hp'] + + game_active_flag = False + #save all battle results + return all_battle_results + +def Main(): + global game_status + global game_active_flag + player_created_flag = False + while not player_created_flag: + try: + trainer_name = input("Enter your trainer name: ") + print("Available starter Pokemon: ", gd.available_starter_names) + starter_name = input("Choose your starter Pokemon: ") + trainer = Trainer(name=trainer_name, starter_name=starter_name) + print(f"{trainer.name} has chosen {trainer.starter} as their starter Pokemon!") + print("Adding potions and pokeballs to your inventory...") + trainer.items = {"potions": 10, "pokeball": 10} + print("Your character is ready!") + #trainer.print_trainer_info() + player_created_flag = True + print(f"Welcome {trainer.name}! Your adventure begins now!") + except ValueError as e: + print(e) + print("Please try again...") + continue + + while game_active_flag: + if game_status == 'story': + print("Type the number of one of the following actions:") + print("1) Explore ") + print("2) Pokemon Center") + print("3) Pokemon Store") + print("4) Show trainer data") + print("5) NewRandomBattle") + print("6) Exit") + + try: + action = int(input("Choose an action (1-6): ").strip()) + except ValueError: + print("Invalid input. Please enter a number between 1 and 6.") + continue + if action == 1: + game_status = 'explore' + elif action == 2: + game_status = 'pokemon_center' + elif action == 3: + game_status = 'pokemon_store' + elif action == 4: + print("Showing trainer data:") + trainer.print_trainer_info() + elif action == 5: + confirm=input("Are you sure you want to start a new random battle? This will generate data and train the model. Type 'yes' to continue: ") + if confirm.lower() == 'yes': + print("Starting a new random battle!") + game_status = 'new_random_battle' + else: + print("Cancelled starting a new random battle.") + elif action == 6: + print("Exiting the game. Goodbye!") + game_active_flag = False + else: + print("Invalid action. Please choose again.") + + elif game_status == 'explore': + print("You are exploring") + battle_prob = random.uniform(0, 1) + #print(f"Battle probability: {battle_prob:.2f}") + if battle_prob < 0.8: + cpu_trainer = Trainer(name='CPU',starter_name=gd.pokemons_df.index[random.randint(0,len(gd.pokemons_df.index)-1)],is_CPU=True) + print(f"A wild {cpu_trainer.get_active_pokemon().name} appeared!") + + #print(cpu_trainer.get_active_pokemon().print_pokemon_info()) + game_status = 'battle' + #before entering the battle, we predict the win probability of + #each of the player's pokemon against the opponent's pokemon + try: + win_probs = [] + for pokemon in trainer.pokemon_list: + win_prob = recommend(player_pokemon=pokemon, opponent_pokemon=cpu_trainer.get_active_pokemon()) + win_probs.append(win_prob) + print(f"The Oracle predicts {pokemon.name} to have {win_prob:.2f} of chances to win.") + #if all the win probabilities are below 0.5, we suggest the player to run away + if all(prob < 0.5 for prob in win_probs): + print("The Oracle suggests to RUN AWAY!") + else: + if len(win_probs) > 1: + #find the pokemon with the highest win probability + best_pokemon_index = win_probs.index(max(win_probs)) + best_pokemon = trainer.pokemon_list[best_pokemon_index] + print(f"The Oracle suggests to use {best_pokemon.name}!") + except ValueError as e: + print("HINT: Unlock access to the Oracle by playing NewRandomBattle!") + + Battle(trainer,cpu_trainer) + else: + print('-'*30) + print("No wild Pokemon appeared.") + game_status = 'story' + + elif game_status == 'pokemon_center': + print("You are at the Pokemon Center. You can heal your Pokemon here.") + heal_flag = input("Type 'yes' to heal your Pokemons or any other key to exit: ").strip().lower() + if heal_flag == 'yes': + for pokemon in trainer.pokemon_list: + pokemon.curr_hp = pokemon.baseStats['hp'] + for move in pokemon.moves_dict.keys(): + pokemon.moves_dict[move]['pp'] = copy.deepcopy(gd.available_moves_df.loc[move]['pp']) + print("All your Pokemon have been healed!") + game_status = 'story' + else: + print("You exited the Pokemon Center.") + game_status = 'story' + # Add Pokemon Center logic here + + elif game_status == 'pokemon_store': + print("You are in the Pokemon Store. You can buy items here.") + restore_items_flag = input("Type 'yes' to restore your items: ").strip().lower() + if restore_items_flag == 'yes': + trainer.items = {"potions": 10, "pokeball": 10} + print("Your items have been restored!") + game_status = 'story' + else: + print("You exited the Pokemon Store.") + game_status = 'story' + + elif game_status == 'new_random_battle': + Ngames = 1000 + Nbattles = 500 + print("Generating data...") + #generate battle data only if the dataset.csv does not exist + if not os.path.exists('dataset.csv'): + for i in range(Ngames): + starter_choice = gd.pokemons_df.index[random.randint(0,len(gd.pokemons_df.index)-1)] + print(f"Generating data for {starter_choice}, game {i}...") + results = MainRandomBattle(starter_choice=starter_choice, Nbattles=Nbattles) + pickle_out = open(f"results/{starter_choice}_game_{i}_{Nbattles}_battles.pickle",'wb') + pickle.dump(results,pickle_out) + pickle_out.close() + + print("Setting up the dataset...") + dataset_column_labels = build_dataset() + try: + print("Training model...") + train_model(dataset_column_labels) + print("Model has been trained! Ending NewRandomBattle") + global recommender_sys_avail + recommender_sys_avail = True + except Exception as e: + print(f"An error occurred during model training: {e}") + print("Ending NewRandomBattle without training the model.") + game_status = 'story' + + else: + print("Unknown game status. Try again.") if __name__ == "__main__": - Ngames = 10 - Nbattles = 5 - print("Generating data") - for i in range(Ngames): - starter_choice = gd.pokemons_df.index[random.randint(0,len(gd.pokemons_df.index)-1)] - #print(f"Generating data for {starter_choice}, game {i}...") - results = Main(starter_choice = starter_choice, Nbattles=Nbattles) - pickle_out = open(f"results/{starter_choice}_game_{i}_{Nbattles}_battles.pickle",'wb') - pickle.dump(results,pickle_out) - pickle_out.close() + if not os.path.exists('results'): + os.makedirs('results') + if not os.path.exists('plots'): + os.makedirs('plots') - print("Generating dataset") - dataset_column_labels = build_dataset() - print("Training model") - best_model = train_model(dataset_column_labels) - joblib.dump(best_model, 'trained_model.joblib') - print("Ended") + Main() \ No newline at end of file