fix: add top_n cutoff to ELO tournament (default top 10 generations)

This commit is contained in:
KoalaTrapLord committed 2026-03-27 01:48:10 -04:00
1 parent cd9d2eebd8
commit cb38081a8b
2 files changed
+40 -7

No files matched your search

+25 -1
View File
@@ -16,6 +16,7 @@ Reuses `run_match`, `LearnedPolicy`, `HeuristicPolicy`,
Usage:
python -m ml.eval.elo_tracker
python -m ml.eval.elo_tracker --episodes 50 --output data/elo_history.json
python -m ml.eval.elo_tracker --top-n 10
"""
import json
@@ -285,12 +286,17 @@ def run_generation_tournament(
episodes_per_matchup: int = 50,
playstyle: str = "midrange",
k_factor: float = DEFAULT_K,
top_n: int = 10,
) -> EloResult:
"""
Discover generation checkpoints and run an ELO tournament.
Looks for `gen-*/best_policy.pt` under checkpoint_dir.
Also includes heuristic and random baselines.
Args:
top_n: Only include the N most recent gen-* checkpoints.
0 means include all (unbounded). Default is 10.
"""
policies: Dict[str, object] = {
"heuristic": HeuristicPolicy(),
@@ -299,7 +305,20 @@ def run_generation_tournament(
ckpt_path = Path(checkpoint_dir)
if ckpt_path.exists():
for gen_dir in sorted(ckpt_path.glob("gen-*")):
gen_dirs = sorted(ckpt_path.glob("gen-*"))
# Apply top_n cutoff — keep only the latest N generation dirs
if top_n > 0 and len(gen_dirs) > top_n:
skipped = len(gen_dirs) - top_n
logger.info(
"top_n=%d: skipping %d older generation(s), using %s … %s",
top_n, skipped,
gen_dirs[-top_n].name,
gen_dirs[-1].name,
)
gen_dirs = gen_dirs[-top_n:]
for gen_dir in gen_dirs:
model_path = gen_dir / "best_policy.pt"
if model_path.exists():
name = gen_dir.name # e.g., "gen-001"
@@ -363,6 +382,10 @@ def main():
"--k-factor", type=float, default=32,
help="ELO K-factor (default: 32)",
)
parser.add_argument(
"--top-n", type=int, default=10,
help="Only include the N most recent gen checkpoints (0 = all, default: 10)",
)
args = parser.parse_args()
@@ -373,6 +396,7 @@ def main():
episodes_per_matchup=args.episodes,
playstyle=args.playstyle,
k_factor=args.k_factor,
top_n=args.top_n,
)
# Print results
+15 -6
View File
@@ -1097,7 +1097,7 @@ _elo_tournament_state = _EloTournamentState()
_elo_tournament_lock = threading.Lock()
def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
def _run_elo_tournament_pipeline(episodes: int, playstyle: str, top_n: int):
"""Run ELO tournament in a background thread."""
try:
import sys
@@ -1108,7 +1108,7 @@ def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
with _elo_tournament_lock:
_elo_tournament_state.running = True
_elo_tournament_state.phase = "running"
_elo_tournament_state.message = "Running ELO tournament..."
_elo_tournament_state.message = f"Running ELO tournament (top {top_n} generations)..."
_elo_tournament_state.error = None
_elo_tournament_state.result = None
@@ -1121,6 +1121,7 @@ def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
checkpoint_dir=ckpt_dir,
episodes_per_matchup=episodes,
playstyle=playstyle,
top_n=top_n,
)
# Save to ELO history
@@ -1155,29 +1156,37 @@ def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
@router.post("/api/ml/elo/tournament")
async def ml_start_elo_tournament(request: FastAPIRequest):
"""Start an ELO-rated tournament across all generation checkpoints."""
"""Start an ELO-rated tournament across generation checkpoints.
Body params:
episodes (int): Games per matchup pair (default 50)
playstyle (str): Game playstyle (default midrange)
topN (int): Max number of most-recent gen checkpoints to include
(default 10, 0 = unlimited)
"""
if _elo_tournament_state.running:
raise HTTPException(409, "ELO tournament already in progress")
body = await request.json() if await request.body() else {}
episodes = body.get("episodes", 50)
playstyle = body.get("playstyle", "midrange")
top_n = body.get("topN", 10)
with _elo_tournament_lock:
_elo_tournament_state.running = True
_elo_tournament_state.phase = "starting"
_elo_tournament_state.message = "Initializing ELO tournament..."
_elo_tournament_state.message = f"Initializing ELO tournament (top {top_n} generations)..."
_elo_tournament_state.result = None
_elo_tournament_state.error = None
thread = threading.Thread(
target=_run_elo_tournament_pipeline,
args=(episodes, playstyle),
args=(episodes, playstyle, top_n),
daemon=True,
)
thread.start()
return {"status": "started", "episodes": episodes}
return {"status": "started", "episodes": episodes, "top_n": top_n}
@router.get("/api/ml/elo/tournament/status")