from core import Trainer import random import copy import game_data as gd import pickle import matplotlib.pyplot as plt import numpy as np import os import pandas as pd np.seterr(divide='raise', invalid='raise') #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 RandomBattle(trainer,cpu_trainer): #print(f"You are in battle against {cpu_trainer.name}!") battle_active = True active_pokemon = trainer.get_active_pokemon() cpu_active_pokemon = cpu_trainer.get_active_pokemon() #print(f"Your active Pokemon is {active_pokemon.name} and the CPU's active Pokemon is {cpu_active_pokemon.name}.") did_player_won = False battle_turns_num = 0 player_hp_evolution = [] player_selected_moves = [] player_damage_done = [] cpu_hp_evolution = [] cpu_selected_moves = [] cpu_damage_done = [] while battle_active: 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.append(player_damage_done_value) player_hp_evolution.append(active_pokemon.get_current_hp()) cpu_hp_evolution.append(cpu_active_pokemon.get_current_hp()) battle_turns_num += 1 #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! You win!") did_player_won = True battle_active = False 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_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.append(cpu_damage_done_value) player_hp_evolution.append(active_pokemon.get_current_hp()) cpu_hp_evolution.append(cpu_active_pokemon.get_current_hp()) battle_turns_num += 1 #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 lose!") did_player_won=False battle_active = False break else: #print("You have other Pokemons that can battle!") continue except ValueError as e: print(e) #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] if len(cpu_damage_done) == 0: cpu_damage_done = [0] player_avg_damage_done = np.mean(player_damage_done) #average damage done per turn cpu_avg_damage_done = np.mean(cpu_damage_done) #average damage done per turn player_residual_hp_percent = trainer.get_active_pokemon().get_current_hp_percent() player_history = {'player_hp_evolution':player_hp_evolution, 'player_selected_moves':player_selected_moves, 'player_damage_done':player_damage_done, 'player_avg_damage_done':player_avg_damage_done } cpu_history = {'cpu_hp_evolution':cpu_hp_evolution, 'cpu_selected_moves':cpu_selected_moves, 'cpu_damage_done':cpu_damage_done, 'cpu_avg_damage_done':cpu_avg_damage_done } turn_history = {'player_history':player_history, 'cpu_history':cpu_history } battle_results = { 'player_pokemon':trainer.get_active_pokemon(), 'encountered_pokemon':cpu_trainer.get_active_pokemon(), 'result':did_player_won, 'battle_turns_num':battle_turns_num, 'player_residual_hp_percent':player_residual_hp_percent, 'turn_history':turn_history } 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"] player_type_label = ['player_type_' + t for t in all_pokemon_types] 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 dataset_column_labels = player_ActStats_labels + player_type_label + opponent_ActStats_labels + opponent_type_label + ['battle_result'] 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() 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 #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') # Split the dataset into features and target variable X = dataset.drop('battle_result', axis=1) y = dataset['battle_result'] # Split the data into training and testing sets X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Create a Random Forest Classifier model = RandomForestClassifier() param_dist = { 'n_estimators': randint(50, 500), 'max_depth': randint(1, 20), } rand_search = RandomizedSearchCV(model, param_distributions=param_dist, n_iter=5, cv=5).fit(X_train, y_train) # Train the model best_model = rand_search.best_estimator_ print(f"Best parameters found: {rand_search.best_params_}") # Make predictions on the test set y_pred = best_model.predict(X_test) # Evaluate the model accuracy = accuracy_score(y_test, y_pred) print(f"Model Accuracy: {accuracy:.2f}") print("Classification Report:") print(classification_report(y_test, y_pred)) 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] dot_data = export_graphviz(tree, feature_names=dataset_labels, filled = True, max_depth = 2, impurity = False, proportion = True) graph = graphviz.Source(dot_data) graph.render(outfile=f"plots/tree_{i}.pdf", format='pdf') 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() print("Generating dataset") dataset_column_labels = build_dataset() print("Training model") train_model(dataset_column_labels) print("Ended")