-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from alan-turing-institute/visualisation
Visualisation using sprites
- Loading branch information
Showing
157 changed files
with
3,847 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Poke Stats | ||
|
||
This directory includes the statistical analysis tools necessary for analysing | ||
the outcomes of the fights. | ||
|
||
So far this includes: | ||
|
||
- Benchmarking the method via clustering the results of 1v1 fights against base | ||
stats, looking to recreate the tier list. | ||
- This can be achieved by running 1v1_fights.py, the base stats are found in | ||
poke_base_stats.pkl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import os | ||
import pickle | ||
import re | ||
|
||
# Directory path where the files are located | ||
directory = "/Users/lfrance/OneDrive - The Alan Turing Institute/002_Projects/Pokemon/poke-images/pokesprite/pokemon-gen8/regular" | ||
|
||
pickle_file = "poke_base_stats.pkl" | ||
|
||
with open(pickle_file, "rb") as file: | ||
pokemon_dict = pickle.load(file) | ||
# Get a list of all filenames in the directory | ||
filenames = os.listdir(directory) | ||
|
||
print(pokemon_dict.keys()) | ||
|
||
pokemon_names = list(pokemon_dict.keys()) | ||
for filename in filenames: | ||
file_path = os.path.join(directory, filename) | ||
|
||
# Extract the Pokémon name from the filename | ||
pokemon_name = filename[:-4] # Remove the ".png" extension | ||
|
||
# Check if the Pokémon name is not present in the dictionary | ||
if pokemon_name not in pokemon_names: | ||
# Delete the file | ||
os.remove(file_path) | ||
print(f"Deleted file: {filename}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
# 1v1 fights for the pokemon for stats. | ||
# Author: Edmund Dable-Heath | ||
""" | ||
Setting each of the 150 pokemon to fight each other pokemon until either a cutoff is | ||
reached or the variation of the win ratio over a history of s fights falls below a | ||
threshold. | ||
""" | ||
|
||
# Imports include reference to potential scripts | ||
# import pop_gen | ||
# import fight_sim | ||
from __future__ import annotations | ||
|
||
import pickle | ||
from itertools import combinations_with_replacement as cwr | ||
|
||
import numpy as np | ||
|
||
# Actual code ========================================================================== | ||
|
||
# def gen_pop() -> list(poke_objects): | ||
# """Generate the population of 151 pokemon objects ready to fight. | ||
|
||
# NB: This can be done more explicity here, i.e. implement the rejection sampling | ||
# in this script, but I will simplify away from that for now. | ||
# """ | ||
# return pop_gen.gen_pop | ||
|
||
# # def duel_to_convergence( | ||
# poke_1: poke_object, | ||
# poke_2: poke_object, | ||
# var_threshold: float, | ||
# look_back: int, | ||
# cutoff: int, | ||
# ) -> tuple(float, float): | ||
# """DUEL TO THE DEATH! (or at least a converged win ratio.....) | ||
|
||
# For two pokemon, duel until the win ration converges or cutoff is reached. | ||
# Return ratios scaled by number of fights. | ||
|
||
# TODO: make lookback function of number of fights? | ||
|
||
# Args: | ||
# poke_1 (poke_object): pokemon_1 (GLADIATOR, READY!) | ||
# poke_2 (poke_object): pokemon_2 (GLADIATOR, READY!) | ||
# var_threshold (float): threshold for convergence of win ratio variance. | ||
# look_back: how man previous ratio values to consider. | ||
# cutoff: max iters to run this. | ||
|
||
# Returns: | ||
# (poke_wins_1, poke_wins_2) tuple(float, float): | ||
# poke_wins_i = (no wins of poke_i) / (no of duels) | ||
# """ | ||
# count = 0 | ||
# variance = 100 | ||
# poke_win_1 = 1 # Start with one win each to avoid /0 errors.... | ||
# poke_win_2 = 1 | ||
# ratios = [] | ||
# while (count < cutoff) or (variance > var_threshold): | ||
# count += 1 | ||
# if duel(poke_1, poke_2): | ||
# poke_win_1 += 1 | ||
# else: | ||
# poke_win_2 += 1 | ||
# ratios.append(poke_win_1 / poke_win_2) | ||
# if len(ratios) < look_back: | ||
# variance = np.var(ratios) | ||
# else: | ||
# variance = np.var(ratios[-look_back:]) | ||
# return poke_win_1 / count, poke_win_2 / count | ||
|
||
# def duel(poke_1: poke_object, poke_2: poke_object) -> bool: | ||
# """Simulate a fight between a pair of pokemon, with a result of True if poke_1 | ||
# wins. | ||
|
||
# Args: | ||
# poke_1 (poke_object): pokemon_1 (IN THE RED CORNER) | ||
# poke_2 (poke_object): pokemon_2 (IN THE BLUE CORNER) | ||
|
||
# Returns: | ||
# bool: True if pokemon 1 is victorious. | ||
# """ | ||
# winner = fight_sim.fight(poke_1, poke_2) | ||
# return winner == poke_1 | ||
|
||
|
||
# def main(**kwargs): | ||
# # Generate a population | ||
# poke_pop = gen_pop | ||
|
||
# # Instantiate results matrix (we know n = 151) | ||
# res_arr = np.zeros(151, 151) | ||
|
||
# # Duel to convergence to get stats | ||
# for dueling_partners_id in cwr(list(range(len(poke_pop))), 2): | ||
# i, j = dueling_partners_id | ||
# poke_ratio_1, poke_ratio_2 = duel_to_convergence( | ||
# poke_pop[i], | ||
# poke_pop[j], | ||
# kwargs["var_threshold"], | ||
# kwargs["look_back"], | ||
# kwargs["cutoff"], | ||
# ) | ||
# if i == j: | ||
# res_arr[i, i] += poke_ratio_1 | ||
# res_arr[i, j] += poke_ratio_1 | ||
# res_arr[j, i] += poke_ratio_2 | ||
|
||
# # Average over to get stats | ||
# avg_win_ratio = np.mean(res_arr, axis=0) | ||
# return avg_win_ratio | ||
|
||
# Dummy Code =========================================================================== | ||
|
||
|
||
def dummy_gen_pop() -> list(str): | ||
"""Create a dummy list of pokemon.""" | ||
with open("poke_base_stats.pkl", "rb") as f: | ||
p_dict = pickle.load(f) | ||
return p_dict | ||
|
||
|
||
def dummy_duel(poke_1: str, poke_2: str) -> bool: | ||
"""Dummy duel method""" | ||
rng = np.random.default_rng(seed=int("".join(str(ord(c)) for c in poke_1 + poke_2))) | ||
prob = rng.random(1)[0] | ||
# print(prob) | ||
# print(1 - prob) | ||
return rng.choice(a=[True, False], p=[prob, 1 - prob]) | ||
|
||
|
||
def dummy_duel_to_convergence( | ||
poke_1: str, | ||
poke_2: str, | ||
var_threshold: float, | ||
look_back: int, | ||
cutoff: int, | ||
) -> tuple(float, float): | ||
"""DUEL TO THE DEATH! (or at least a converged win ratio.....) | ||
For two pokemon, duel until the win ration converges or cutoff is reached. | ||
Return ratios scaled by number of fights. | ||
TODO: make lookback function of number of fights? | ||
Args: | ||
poke_1 (poke_object): pokemon_1 (GLADIATOR, READY!) | ||
poke_2 (poke_object): pokemon_2 (GLADIATOR, READY!) | ||
var_threshold (float): threshold for convergence of win ratio variance. | ||
look_back: how man previous ratio values to consider. | ||
cutoff: max iters to run this. | ||
Returns: | ||
(poke_wins_1, poke_wins_2) tuple(float, float): | ||
poke_wins_i = (no wins of poke_i) / (no of duels) | ||
""" | ||
count = 0 | ||
variance = 100 | ||
poke_win_1 = 1 # Start with one win each to avoid /0 errors.... | ||
poke_win_2 = 1 | ||
ratios = [] | ||
# print(cutoff) | ||
while count < cutoff and variance > var_threshold: # or variance | ||
# print(count) | ||
count += 1 | ||
if dummy_duel(poke_1, poke_2): | ||
poke_win_1 += 1 | ||
else: | ||
poke_win_2 += 1 | ||
ratios.append(poke_win_1 / poke_win_2) | ||
if count > 2: | ||
if len(ratios) < look_back: | ||
variance = np.var(ratios) | ||
else: | ||
variance = np.var(ratios[-look_back:]) | ||
print(variance) | ||
return poke_win_1 / count, poke_win_2 / count | ||
|
||
|
||
def main(**kwargs): | ||
# Generate a population | ||
poke_dict = dummy_gen_pop() | ||
poke_names = list(poke_dict.keys()) | ||
|
||
# Instantiate results matrix (we know n = 151) | ||
res_arr = np.zeros((151, 151)) | ||
|
||
# Duel to convergence to get stats | ||
for dueling_partners_id in cwr(list(range(len(poke_names))), 2): | ||
i, j = dueling_partners_id | ||
print(f"IN THE RED CORNER: {poke_names[i]}") | ||
print(f"IN THE BLUE CORNER: {poke_names[j]}") | ||
poke_ratio_1, poke_ratio_2 = dummy_duel_to_convergence( | ||
poke_names[i], | ||
poke_names[j], | ||
kwargs["var_threshold"], | ||
kwargs["look_back"], | ||
kwargs["cutoff"], | ||
) | ||
if i == j: | ||
res_arr[i, i] += poke_ratio_1 | ||
res_arr[i, j] += poke_ratio_1 | ||
res_arr[j, i] += poke_ratio_2 | ||
|
||
# Average over to get stats | ||
avg_win_ratio = np.mean(res_arr, axis=0) | ||
for i in range(len(poke_names)): | ||
poke_dict[poke_names[i]]["average_win_ratio"] = avg_win_ratio[i] | ||
# print(poke_dict) | ||
print("for Magikarp:") | ||
print(poke_dict["magikarp"]) | ||
return poke_dict | ||
|
||
|
||
if __name__ == "__main__": | ||
# Pars hardcoded in for now as should be single run. | ||
pars = {"var_threshold": 1e-14, "look_back": 2, "cutoff": 100} | ||
|
||
main(**pars) |
Binary file not shown.
Oops, something went wrong.