#!/usr/bin/env python3 """Replay every committed fixture and reproduce its recorded rewards. A fixture is a claim: "this model, on this seed, scored this." The claim is only worth anything if it can be re-derived from the moves it records. This does that in Python; `verify.ts` does the same thing in the browser, live, in front of the visitor. Exits non-zero if any fixture's rewards cannot be reproduced from its own turns. """ from __future__ import annotations import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent / "wordle_five")) from wordle_five import solver as S # noqa: E402 from wordle_five.engine import Game # noqa: E402 from wordle_five.protocol import parse_guess # noqa: E402 from wordle_five.reward import Episode, score # noqa: E402 TRACES = Path(__file__).parent.parent / "public" / "traces" / "wordle" TOLERANCE = 1e-9 def replay(fixture: dict) -> dict[str, float]: game = Game(seed=fixture["seed"]) if game.answer != fixture["answer"]: raise AssertionError( f"seed {fixture['seed']} gives {game.answer!r}, fixture claims {fixture['answer']!r}" ) for turn in fixture["turns"]: guess = parse_guess(turn["reply"]) if guess is None: game.rejected += 1 continue game.play(guess) episode = Episode( answer=game.answer, guesses=[g for g, _ in game.history], patterns=[p for _, p in game.history], rejected=game.rejected, reference_depth=S.reference_depth(game.answer), ) return score(episode) def main() -> int: fixtures = sorted(TRACES.glob("*.json")) fixtures = [f for f in fixtures if f.name != "manifest.json"] if not fixtures: print("no fixtures found — nothing to verify") return 0 failures = 0 for path in fixtures: data = json.loads(path.read_text()) recomputed = replay(data) recorded = data["rewards"] deltas = {k: abs(recomputed[k] - recorded[k]) for k in recorded} worst = max(deltas.values()) status = "ok" if worst <= TOLERANCE else "MISMATCH" if worst > TOLERANCE: failures += 1 culprit = max(deltas, key=deltas.get) print(f"{path.name:>20} {status} worst delta {worst:.3g} on '{culprit}'") else: print(f"{path.name:>20} {status} delta {worst:.1e}") print(f"\n{len(fixtures)} fixtures, {failures} mismatched") return 1 if failures else 0 if __name__ == "__main__": raise SystemExit(main())