magicciv/tools/matchup-grid-audit.py

79 lines
2.5 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""matchup-grid-audit.py — list player_clans assignment per game in a matchup-grid batch.
Helps detect contaminated data when a resumed batch was launched with the wrong
pinning convention (e.g., bare `AI_PIN_PERSONALITY` instead of per-slot
`AI_PIN_PERSONALITY_P0/P1`). The expected assignment for a pair `<a>_vs_<b>` is:
<pair>/as_<a>/ player_clans = {"0": "<a>", "1": "<b>"}
<pair>/as_<b>/ player_clans = {"0": "<b>", "1": "<a>"}
Any game whose meta.json deviates from that should be flagged or pruned before
the matchup-grid-report runs.
Usage:
python3 tools/matchup-grid-audit.py <batch-dir>
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
def expected_clans(pair: str, perspective: str) -> dict[str, str]:
a, b = pair.split("_vs_")
if perspective == a:
return {"0": a, "1": b}
if perspective == b:
return {"0": b, "1": a}
return {}
def audit(batch_dir: Path) -> int:
bad = 0
good = 0
for pair_dir in sorted(batch_dir.iterdir()):
if not pair_dir.is_dir() or "_vs_" not in pair_dir.name:
continue
pair = pair_dir.name
for sub in sorted(pair_dir.iterdir()):
if not sub.is_dir() or not sub.name.startswith("as_"):
continue
perspective = sub.name[len("as_"):]
expected = expected_clans(pair, perspective)
if not expected:
continue
for game in sorted(sub.iterdir()):
if not game.is_dir():
continue
meta = game / "meta.json"
if not meta.exists():
continue
try:
data = json.loads(meta.read_text())
except Exception:
print(f" PARSE_ERR {pair}/{sub.name}/{game.name}")
bad += 1
continue
got = data.get("player_clans") or {}
if got == expected:
good += 1
else:
print(f" MISMATCH {pair}/{sub.name}/{game.name}: expected={expected} got={got}")
bad += 1
print()
print(f"Total: {good} correct, {bad} mismatched/parse-err")
return 1 if bad else 0
def main(argv: list[str]) -> int:
if len(argv) != 2:
print(f"Usage: {argv[0]} <batch-dir>", file=sys.stderr)
return 2
return audit(Path(argv[1]))
if __name__ == "__main__":
sys.exit(main(sys.argv))