final corrections before submission

This commit is contained in:
Edoardo Vergerio
2026-09-06 17:42:44 +02:00
parent 268fda0a6b
commit f046d06e21
15 changed files with 487 additions and 248 deletions
+4 -1
View File
@@ -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
+13 -34
View File
@@ -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()
+17 -18
View File
@@ -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__":
+1 -1
View File
@@ -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
+3 -4
View File
@@ -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()
-4
View File
@@ -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()
+4 -4
View File
@@ -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
+7 -4
View File
@@ -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}...")
+2 -3
View File
@@ -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.
#in the main code I decided to keep using dictionaries
#for the type effectiveness
+5 -4
View File
@@ -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)
make_plots(Ngames, Nbattles)
print("Finished!")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+431 -171
View File
@@ -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()