diff --git a/endgames.py b/endgames.py index f7da8d0..1437fa0 100644 --- a/endgames.py +++ b/endgames.py @@ -1,10 +1,10 @@ import json import re import subprocess -from typing import Dict +from typing import Dict, Optional from games import GAMES_PATH -from bots import ensure_cheat_game_exists +from bots import ensure_bot_game_exists from pathlib import Path DATA_FILE = Path('endgame-data.json') @@ -20,7 +20,7 @@ with open(DATA_FILE, 'r') as f: def analyze_game(filename: str): max_draw_pile_size = 15 try: - result = subprocess.run(['./endgame-analyzer', '-f', filename, '-d', str(max_draw_pile_size), '--interactive', '0', '--quiet', '-r'], stdout=subprocess.PIPE, timeout=60*15) + result = subprocess.run(['./endgame-analyzer', '-f', filename, '-d', str(max_draw_pile_size), '--interactive', '0', '--quiet', '-r'], stdout=subprocess.PIPE, timeout=30) raw_output = result.stdout except subprocess.TimeoutExpired as time_err: raw_output = time_err.stdout @@ -44,7 +44,7 @@ def analyze_game(filename: str): def full_analyze_game(filename: str): max_draw_pile_size = 10 try: - result = subprocess.run(['./endgame-analyzer', '-f', filename, '-d', str(max_draw_pile_size), '-i', '0', '--all-clues', '-r', '--quiet'], stdout=subprocess.PIPE, timeout=180) + result = subprocess.run(['./endgame-analyzer', '-f', filename, '-d', str(max_draw_pile_size), '-i', '0', '--all-clues', '-r', '--quiet'], stdout=subprocess.PIPE, timeout=30) raw_output = result.stdout except subprocess.TimeoutExpired as time_err: raw_output = time_err.stdout @@ -71,14 +71,14 @@ def full_analyze_game(filename: str): return probabilities -def full_analyze_game_cached(game_id: int, cheat: bool = False): - key = 'all' if not cheat else 'all-cheat' - if cheat: - ensure_cheat_game_exists(game_id) +def full_analyze_game_cached(game_id: int, strategy: Optional[str] = None): + key = 'all' if strategy is None else 'all-{}'.format(strategy) + if strategy is not None: + ensure_bot_game_exists(game_id, strategy) 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 '') + result = full_analyze_game(str(GAMES_PATH / str(game_id)) + '-{}'.format(strategy) if strategy is not None else '') if key not in DATA.keys(): DATA[key] = {} DATA[key][game_id] = result @@ -86,14 +86,14 @@ def full_analyze_game_cached(game_id: int, cheat: bool = False): return result -def analyze_game_cached(game_id: int, cheat: bool = False): - key = 'normal' if not cheat else 'normal-cheat' - if cheat: - ensure_cheat_game_exists(game_id) +def analyze_game_cached(game_id: int, strategy: Optional[str] = None): + key = 'normal' if strategy is None else 'normal-{}'.format(strategy) + if strategy is not None: + ensure_bot_game_exists(game_id, strategy) 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 '') + result = analyze_game(str(GAMES_PATH / str(game_id)) + '-{}'.format(strategy) if strategy is not None else '') if key not in DATA.keys(): DATA[key] = {} DATA[key][game_id] = result diff --git a/games.py b/games.py index 3b33107..41a5264 100644 --- a/games.py +++ b/games.py @@ -1,6 +1,6 @@ import json -from typing import Dict +from typing import Dict, Optional from pathlib import Path from hanabi.live.site_api import get @@ -11,14 +11,14 @@ if not GAMES_PATH.exists(): GAMES_PATH.mkdir(parents=True) -def get_game_json(game_id: int, cheat: bool = False) -> Dict: - filename = GAMES_PATH / (str(game_id) + ('-cheat' if cheat else '')) +def get_game_json(game_id: int, strategy: Optional[str] = None) -> Dict: + filename = GAMES_PATH / (str(game_id) + ('-{}'.format(strategy) if strategy is not None 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)) + if strategy is not None: + print('Failed to load replay of {} strategy version of game with id {}'.format(strategy, game_id)) return {} game = get("export/" + str(game_id)) diff --git a/get_sheet.py b/get_sheet.py index faa60d1..bd228ad 100644 --- a/get_sheet.py +++ b/get_sheet.py @@ -3,7 +3,7 @@ import requests_cache import json import csv import pandas -from typing import List +from typing import List, Optional from pathlib import Path from hanabi.database import global_db_connection_manager @@ -111,20 +111,20 @@ def analyze_games(games): return retval -def analyze_endgames(games, cheat): +def analyze_endgames(games, strategy: Optional[str] = None): retval = {} 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) + print('Analysing endgames {} of game {}'.format('with strategy {}'.format(strategy) if strategy is not None else '', game_id)) + result = analyze_game_cached(game_id, strategy) retval[game_id] = result return retval -def full_analyze_endgames(games, cheat): +def full_analyze_endgames(games, strategy: Optional[str] = None): retval = {} 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) + print('Analysing all endgames {} of game {}'.format('with strategy {}'.format(strategy) if strategy is not None else '', game_id)) + result = full_analyze_game_cached(game_id, strategy) retval[game_id] = result return retval @@ -155,10 +155,10 @@ def lookup_val(endgame_dict, clue_modifier) -> str: return retval -def make_endgame_tables(ids: List[int], cheat: bool): - endgames = analyze_endgames(ids, cheat) +def make_endgame_tables(ids: List[int], strategy: Optional[str] = None): + endgames = analyze_endgames(ids, strategy) - postfix = '-cheat' if cheat else '' + postfix = '-{}'.format(strategy) if strategy is not None else '' main_fname = OUT_PATH / ('endgames' + postfix + '.csv') fieldnames = ['Game ID'] + [str(i) for i in range(1, 16)] @@ -176,7 +176,7 @@ def make_endgame_tables(ids: List[int], cheat: bool): x = pandas.read_csv(main_fname) x.to_html(main_fname.with_suffix('.html'), escape=False) - all_endgames = full_analyze_endgames(ids, cheat) + all_endgames = full_analyze_endgames(ids, strategy) fieldnames = ['Game ID'] + [str(i) for i in range(1, 11)] for clue_modifier in range(-2, 3): filename = special_fname.format(clue_modifier) @@ -201,26 +201,9 @@ def make_endgame_tables(ids: List[int], cheat: bool): 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() +def make_results_table(games): analysis = analyze_games(games) + streaks = {} fieldnames = ['Game ID', 'Seed', 'Player #', 'Result', 'BDR'] fieldnames += sort_players_by_num_games(games) @@ -259,11 +242,42 @@ def main(): a = pandas.read_csv("out/games.csv") a.to_html("out/games.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) +def create_replay_links(ids: List[int], strategy: str): + outfile = Path('out/{}_links.csv'.format(strategy)) + with open(outfile, 'w') as f: + writer = csv.writer(f) + writer.writerow(["Game ID", "{} Replay Link".format(strategy)]) + for game_id in ids: + replay = get_game_json(game_id, strategy) + 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() + game_ids = sorted(int(key) for key in games.keys()) + + # This is the main table, tracking streaks, loss reasons, BDRs + make_results_table(games) + + # Additional endgame analysis stats: + # For the real games + make_endgame_tables(game_ids, None) + # For the cheating strategy + make_endgame_tables(game_ids, 'cheat') + # For the information strategy + make_endgame_tables(game_ids, 'info') + + # Create JSON replay links to the bot games to watch + create_replay_links(game_ids, 'cheat') + create_replay_links(game_ids, 'info') if __name__ == "__main__":