"""Check that an engine change leaves every game identical (events hashed per game). Record before the change, check after (a rule change is expected to differ): uv run python agents/scripts/fingerprint_games.py write /tmp/fingerprints.json uv run python agents/scripts/fingerprint_games.py check /tmp/fingerprints.json """ import hashlib import json import sys from concurrent.futures import ProcessPoolExecutor from pathlib import Path from dojo.board import load_board from dojo.engine import Game, RandomSource from dojo.rules import RulesConfig from dojo.sim import make_agent BOARD = Path(__file__).resolve().parents[2] / "board" GAMES = 60 CASES = [ ("mixed4", RulesConfig(), ("balanced", "farmer", "aggressive", "balanced"), GAMES), ("casual4", RulesConfig(), ("casual",) * 4, GAMES), ( "mixed6", RulesConfig(), ("casual", "farmer", "aggressive", "balanced", "casual", "farmer"), 30, ), ( "stop-r1", RulesConfig(fatica_on_crossing=False, first_kumite_round=1), ("casual", "balanced", "farmer"), 30, ), ("random", RulesConfig(max_rounds=60), ("random", "casual", "random", "balanced"), 30), ] def play(case: int, seed: int) -> tuple[str, int, str]: name, config, bots, _ = CASES[case] board = load_board(BOARD / "quadrant.txt", BOARD / "legend.toml") agents = [make_agent(bot, seed, seat) for seat, bot in enumerate(bots)] game = Game(board, config, agents, RandomSource(seed)) game.run() digest = hashlib.sha256("\n".join(map(repr, game.events)).encode()).hexdigest() return name, seed, digest def main() -> None: mode, path = sys.argv[1], Path(sys.argv[2]) tasks = [(i, seed) for i, case in enumerate(CASES) for seed in range(case[3])] with ProcessPoolExecutor(8) as pool: results = {f"{n}:{s}": d for n, s, d in pool.map(play, *zip(*tasks, strict=True))} if mode == "write": path.write_text(json.dumps(results, indent=0)) print(f"wrote {len(results)} games") return expected = json.loads(path.read_text()) bad = [k for k in expected if results.get(k) != expected[k]] print(f"{len(expected) - len(bad)}/{len(expected)} games identical") if bad: print("differ:", bad[:10]) sys.exit(1) if __name__ == "__main__": main()