From a59119c0c434ac7919d5130dccafa887910fdccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Mon, 20 Nov 2023 14:04:52 +0100 Subject: [PATCH] Also analyse endgames of cheating strategy --- endgames.py | 14 +++-- games.py | 8 ++- get_sheet.py | 151 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 113 insertions(+), 60 deletions(-) diff --git a/endgames.py b/endgames.py index e8aca18..f7da8d0 100644 --- a/endgames.py +++ b/endgames.py @@ -4,6 +4,7 @@ import subprocess from typing import Dict from games import GAMES_PATH +from bots import ensure_cheat_game_exists from pathlib import Path DATA_FILE = Path('endgame-data.json') @@ -71,23 +72,28 @@ def full_analyze_game(filename: str): def full_analyze_game_cached(game_id: int, cheat: bool = False): - cached = DATA.get('all', {}).get(str(game_id), None) + key = 'all' if not cheat else 'all-cheat' + if cheat: + ensure_cheat_game_exists(game_id) + cached = DATA.get(key, {}).get(str(game_id), None) if cached is not None: return cached result = full_analyze_game(str(GAMES_PATH / str(game_id)) + '-cheat' if cheat else '') - key = 'all' if not cheat else 'all-cheat' if key not in DATA.keys(): DATA[key] = {} DATA[key][game_id] = result save_cache() return result + def analyze_game_cached(game_id: int, cheat: bool = False): - cached = DATA.get('normal', {}).get(str(game_id), None) + key = 'normal' if not cheat else 'normal-cheat' + if cheat: + ensure_cheat_game_exists(game_id) + cached = DATA.get(key, {}).get(str(game_id), None) if cached is not None: return cached result = analyze_game(str(GAMES_PATH / str(game_id)) + '-cheat' if cheat else '') - key = 'normal' if not cheat else 'normal-cheat' if key not in DATA.keys(): DATA[key] = {} DATA[key][game_id] = result diff --git a/games.py b/games.py index 1411d6a..3b33107 100644 --- a/games.py +++ b/games.py @@ -11,12 +11,16 @@ if not GAMES_PATH.exists(): GAMES_PATH.mkdir(parents=True) -def get_game_json(game_id: int) -> Dict: - filename = GAMES_PATH / str(game_id) +def get_game_json(game_id: int, cheat: bool = False) -> Dict: + filename = GAMES_PATH / (str(game_id) + ('-cheat' if cheat else '')) if filename.exists(): with open(filename, 'r') as f: return json.load(f) + if cheat: + print('Failed to load replay of cheating game with id {}'.format(game_id)) + return {} + game = get("export/" + str(game_id)) with open(filename, 'w') as f: f.write(json.dumps(game, indent=2)) diff --git a/get_sheet.py b/get_sheet.py index e8ae92f..faa60d1 100644 --- a/get_sheet.py +++ b/get_sheet.py @@ -3,14 +3,16 @@ import requests_cache import json import csv import pandas +from typing import List +from pathlib import Path from hanabi.database import global_db_connection_manager -from hanabi.live.site_api import get +from hanabi.live.hanab_live import parse_json_game, HanabLiveGameState +from hanabi.live.compress import link from bdr import describe_game from endgames import analyze_game_cached, full_analyze_game_cached from games import get_game_json -from bots import run_cheating_strategy # Init db connection global_db_connection_manager.read_config() @@ -18,6 +20,10 @@ global_db_connection_manager.connect() session = requests_cache.CachedSession('.hanab-live.cache',expire_after=30000) +OUT_PATH = Path('out') +if not OUT_PATH.exists(): + OUT_PATH.mkdir(parents=True) + player_mapping = { 'RamaNoVarjan': 'Ramanujan', 'purplejoe2': 'PurpleJoe', @@ -104,19 +110,21 @@ def analyze_games(games): retval[game_id] = bdrs, termination return retval -def analyze_endgames(games): + +def analyze_endgames(games, cheat): retval = {} - for game_id in games.keys(): - print('Analysing endgames of game {}'.format(game_id)) - result = analyze_game_cached(game_id) + for game_id in games: + print('Analysing endgames {} of game {}'.format('with cheating' if cheat else '', game_id)) + result = analyze_game_cached(game_id, cheat) retval[game_id] = result return retval -def full_analyze_endgames(games): + +def full_analyze_endgames(games, cheat): retval = {} - for game_id in games.keys(): - print('Analysing all endgames of game {}'.format(game_id)) - result = full_analyze_game_cached(game_id) + for game_id in games: + print('Analysing all endgames {} of game {}'.format('with cheating' if cheat else '', game_id)) + result = full_analyze_game_cached(game_id, cheat) retval[game_id] = result return retval @@ -147,10 +155,72 @@ def lookup_val(endgame_dict, clue_modifier) -> str: return retval -if __name__ == "__main__": +def make_endgame_tables(ids: List[int], cheat: bool): + endgames = analyze_endgames(ids, cheat) + + postfix = '-cheat' if cheat else '' + main_fname = OUT_PATH / ('endgames' + postfix + '.csv') + + fieldnames = ['Game ID'] + [str(i) for i in range(1, 16)] + with open(main_fname, 'w', newline='') as f: + f.writelines([','.join(fieldnames), "\n"]) + + with open(main_fname, 'a', newline='') as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + for game_id, endgame in sorted(endgames.items()): + endgame['Game ID'] = "{}".format(game_id, game_id) + writer.writerow(endgame) + + special_fname = str(OUT_PATH / ('endgames' + postfix + '_modifier_{}.csv')) + + x = pandas.read_csv(main_fname) + x.to_html(main_fname.with_suffix('.html'), escape=False) + + all_endgames = full_analyze_endgames(ids, cheat) + fieldnames = ['Game ID'] + [str(i) for i in range(1, 11)] + for clue_modifier in range(-2, 3): + filename = special_fname.format(clue_modifier) + with open(filename, 'w') as f: + f.writelines([','.join(fieldnames), "\n"]) + with open(filename, 'a') as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + for game_id, endgame in sorted(all_endgames.items()): + # print(endgame) + row = {'Game ID': game_id} + for deck_size in range(1, 11): + val = lookup_val(endgame.get(str(deck_size), {}), clue_modifier) + if val is not None: + row[str(deck_size)] = val + else: + print("WARN: No results found for game {} and deck size {}: {}".format(game_id, deck_size, endgame.get(str(deck_size)))) + writer.writerow(row) + + print('processed file {}'.format(filename)) + + x = pandas.read_csv(filename) + x.to_html(Path(filename).with_suffix('.html'), escape=False) + + +def create_cheating_replay_links(ids: List[int]): + outfile = Path('out/cheating_links.csv') + with open(outfile, 'w') as f: + writer = csv.writer(f) + writer.writerow(["Game ID", "Cheating Replay Link"]) + for game_id in ids: + replay = get_game_json(game_id, True) + instance, actions = parse_json_game(replay) + game = HanabLiveGameState(instance) + for action in actions: + game.make_action(action) + writer.writerow([game_id, link(game)]) + + x = pandas.read_csv(outfile) + x.to_html(outfile.with_suffix('.html'), render_links=True) + + +def main(): games = collect_player_games() analysis = analyze_games(games) - endgames = analyze_endgames(games) streaks = {} fieldnames = ['Game ID', 'Seed', 'Player #', 'Result', 'BDR'] fieldnames += sort_players_by_num_games(games) @@ -164,12 +234,12 @@ if __name__ == "__main__": for game_id, entry in sorted(games.items()): bdrs, termination = analysis[game_id] row = { - 'Game ID': "{}".format(entry.game_id, entry.game_id), - 'Seed': "{}".format(entry.seed, entry.seed), - 'Player #': entry.num_players, - 'Result': 'Win' if entry.won else termination, - 'BDR': len(bdrs), - } + 'Game ID': "{}".format(entry.game_id, entry.game_id), + 'Seed': "{}".format(entry.seed, entry.seed), + 'Player #': entry.num_players, + 'Result': 'Win' if entry.won else termination, + 'BDR': len(bdrs), + } for player in entry.players: col = player_mapping.get(player, None) if col is not None: @@ -186,42 +256,15 @@ if __name__ == "__main__": row['Other'] = num_others + 1 writer.writerow(row) - fieldnames = ['Game ID'] + [str(i) for i in range(1, 16)] - with open('out/endgames.csv', 'w', newline='') as f: - f.writelines([','.join(fieldnames), "\n"]) - - with open('out/endgames.csv', 'a', newline='') as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - for game_id, endgame in sorted(endgames.items()): - endgame['Game ID'] = "{}".format(game_id, game_id) - writer.writerow(endgame) - - all_endgames = full_analyze_endgames(games) - fieldnames = ['Game ID'] + [str(i) for i in range(1, 11)] - for clue_modifier in range(-2, 3): - filename = 'endgames{}.csv'.format(clue_modifier) - with open('out/' + filename, 'w') as f: - f.writelines([','.join(fieldnames), "\n"]) - with open('out/' + filename, 'a') as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - for game_id, endgame in sorted(all_endgames.items()): - # print(endgame) - row = {'Game ID': game_id} - for deck_size in range(1, 11): - val = lookup_val(endgame.get(str(deck_size), {}), clue_modifier) - if val is not None: - row[str(deck_size)] = val - else: - print("WARN: No results found for game {} and deck size {}: {}".format(game_id, deck_size, endgame.get(str(deck_size)))) - writer.writerow(row) - - print('processed file {}'.format(filename)) - - x = pandas.read_csv('out/' + filename) - x.to_html('out/endgames_{}.html'.format(clue_modifier), escape=False) - a = pandas.read_csv("out/games.csv") a.to_html("out/games.html", escape=False) - b = pandas.read_csv('out/endgames.csv') - b.to_html("out/endgames.html", escape=False) + game_ids = [int(key) for key in games.keys()] + make_endgame_tables(game_ids, False) + create_cheating_replay_links(game_ids) + make_endgame_tables(game_ids, True) + + + +if __name__ == "__main__": + main()