| 1 | """Bounded real Gomocup subprocess comparison; no score or production data writes. |
| 2 | Example: .venvs/gomoku/bin/python games/gomoku/tests/compare.py --output /tmp/gomoku.json |
| 3 | Use --season for the six benchmark openings, or --openings 10 for fresh paired ones. |
| 4 | """ |
| 5 | import argparse |
| 6 | import json |
| 7 | import pathlib |
| 8 | import random |
| 9 | import sys |
| 10 | import time |
| 11 | |
| 12 | ROOT = pathlib.Path(__file__).resolve().parents[1] |
| 13 | sys.path.insert(0, str(ROOT / 'referee')) |
| 14 | from gomoku import match |
| 15 | |
| 16 | parser = argparse.ArgumentParser() |
| 17 | parser.add_argument('--season', action='store_true') |
| 18 | parser.add_argument('--seed', type=int, default=20261023) |
| 19 | parser.add_argument('--openings', type=int, default=10) |
| 20 | parser.add_argument('--output', type=pathlib.Path) |
| 21 | args = parser.parse_args() |
| 22 | if not 1 <= args.openings <= 30: |
| 23 | parser.error('--openings must be between 1 and 30') |
| 24 | if args.season: |
| 25 | openings = [[tuple(map(int, p.split(','))) for p in line.split()] |
| 26 | for line in (ROOT / 'referee/openings.txt').read_text().splitlines() |
| 27 | if line and not line.startswith('#')] |
| 28 | else: |
| 29 | rng = random.Random(args.seed) |
| 30 | pool = [(x, y) for x in range(4, 11) for y in range(4, 11) if (x, y) != (7, 7)] |
| 31 | openings = [[(7, 7), *rng.sample(pool, 2)] for _ in range(args.openings)] |
| 32 | new = ([sys.executable, str(ROOT / 'field/threats/bot.py')], str(ROOT)) |
| 33 | old = ([sys.executable, str(ROOT / 'tests/fixtures/legacy_threats.py')], str(ROOT)) |
| 34 | results, started = [], time.monotonic() |
| 35 | for opening in openings: |
| 36 | for colour in (0, 1): |
| 37 | r = match([new, old] if colour == 0 else [old, new], opening) |
| 38 | results.append({'opening': opening, 'colour': colour, 'winner': r['winner'], |
| 39 | 'won': r['winner'] == colour, 'moves': len(r['moves']), |
| 40 | 'max_ms': max(m[2] for m in r['moves']), 'reason': r['reason']}) |
| 41 | print(json.dumps({'games': len(results), 'wins': sum(r['won'] for r in results)}), flush=True) |
| 42 | summary = {'games': len(results), 'wins': sum(r['won'] for r in results), |
| 43 | 'max_ms': max(r['max_ms'] for r in results), 'seconds': time.monotonic() - started} |
| 44 | print(json.dumps(summary)) |
| 45 | if args.output: |
| 46 | args.output.write_text(json.dumps({'summary': summary, 'games': results}, indent=2)) |