6.5 KiB
AGENTS.md
This repository is managed with uv. Use uv run ... for commands so the
project environment and Cython extensions are built/loaded consistently.
Project Layout
src/coolrl_lost_cities/games/classic/game.pyx: Cython Lost Cities engine.src/coolrl_lost_cities/games/classic/deep_cfr/: Deep CFR training, traversal, evaluation, analysis, and CLI code.configs/deep_cfr/: Deep CFR YAML configs.runs/: generated training runs. This path is gitignored and may be a symlink to larger storage.docs/: profiling notes, migration notes, and experiment documentation.
Core Commands
Run lint:
uv run ruff check .
Run all tests:
uv run pytest -q
Run focused Deep CFR tests:
uv run pytest -q tests/games/classic/test_deep_cfr_trainer.py
Run the CLI through the console script:
uv run lost-cities-deep-cfr --help
Equivalent module form:
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.cli --help
Deep CFR Training
Main full config:
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml
Unbounded config:
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability_unbounded.yaml
Short fixed-iteration run:
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml \
--set run.iterations=100 \
--set run.max_hours=null \
--set run.max_iterations=null \
--set checkpoint.save_latest_only=true \
--set checkpoint.save_every_iteration=false
Use explicit run directories for experiments. Put Deep CFR runs under
runs/deep_cfr/ and prefix generated run names with the date:
RUN_DIR="runs/deep_cfr/$(date +%Y-%m-%d_%H%M%S)_deep_cfr_experiment_name"
uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml \
--set checkpoint.directory="$RUN_DIR" \
--set run.max_iterations=100
Date-prefixed examples:
runs/deep_cfr/YYYY-MM-DD_HHMMSS_deep_cfr_100iterruns/deep_cfr/YYYY-MM-DD_HHMMSS_deep_cfr_unbounded
Useful train controls:
--resume: resume from<checkpoint-dir>/latest.pt.--resume PATH: resume from a specific checkpoint.--device DEVICE: override the trainer device for this invocation.--set PATH=VALUE: override config fields. It is repeatable and parses values as YAML, e.g.--set traversal.num_workers=4or--set run.max_hours=null.
Common --set overrides:
--set checkpoint.exact_resume=true: require checkpoint config compatibility.--set checkpoint.save_latest=false --set checkpoint.save_every_iteration=false --set checkpoint.save_iteration_interval=0: disable checkpoint writes.--set checkpoint.save_latest_only=true --set checkpoint.save_every_iteration=false: keep onlylatest.pt.--set checkpoint.save_iteration_interval=N: archive every N iterations.
Long Runs
Run long jobs in a real tmux session so the user can attach and stop them.
Do not rely on Codex command sessions for long user-observable training runs.
Start a long unbounded run:
tmux new-session -s coolrl-deepcfr-unbounded \
-c /home/coolguy/dev/coolrl-lost-cities \
'uv run lost-cities-deep-cfr train \
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability_unbounded.yaml'
Attach later:
tmux attach -t coolrl-deepcfr-unbounded
Detach without stopping:
Ctrl+B, D
Stop training:
Ctrl+C
Follow logs from another terminal:
tail -f runs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability_unbounded/train.log
The unbounded config intentionally has:
run:
iterations: null
max_iterations: null
max_hours: null
checkpoint:
save_iteration_interval: 100
latest.pt is updated continuously; archive checkpoints are written every 100
iterations. If disk is tight, prefer --set checkpoint.save_latest_only=true or
increase save_iteration_interval.
Evaluation And Analysis
Evaluate a checkpoint:
uv run lost-cities-deep-cfr eval \
--checkpoint runs/deep_cfr/<run-name>/latest.pt \
--opponent random \
--games 100 \
--device cpu
Save evaluation game records:
uv run lost-cities-deep-cfr eval \
--checkpoint runs/deep_cfr/<run-name>/latest.pt \
--opponent random \
--games 100 \
--device cpu \
--save-games runs/deep_cfr/<run-name>/eval_random_games.json
Generate analysis plots from metrics.jsonl:
uv run lost-cities-deep-cfr analyze \
--run runs/deep_cfr/<run-name>
Write plots to a separate directory:
uv run lost-cities-deep-cfr analyze \
--run runs/deep_cfr/<run-name> \
--output-dir runs/deep_cfr/<run-name>/analysis
The analyzer reads metrics.jsonl and writes PNG files grouped by diagnostic
section. Opponents are compared within each plot using fixed colors. The
lost-cities-deep-cfr analyze subcommand uses the analyzer default smoothing
window, currently 1 iteration (no smoothing), and supports --max-iteration.
For smoothing controls, run the analyzer module directly:
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.analyze \
--run runs/deep_cfr/<run-name> \
--smoothing-window 5
Use --no-smoothing to force no moving average.
Current output files:
analysis_01_loss.pnganalysis_02_match.pnganalysis_03_action.pnganalysis_04_gameflow.pnganalysis_05_open_quality.pnganalysis_06_expedition_outcomes.pnganalysis_07_calibration.pnganalysis_08_traversal.pnganalysis_09_selectivity.pnganalysis_final_eval_summary.png
Runtime Artifacts
Each training run writes:
metrics.jsonl: structured metrics, one completed iteration per line.train.log: human-readable timestamped logs.runtime_progress.json: latest progress snapshot.latest.pt: latest checkpoint.iteration_*.pt: archive checkpoints when enabled.config.json: resolved config for the run.
If a run is stopped mid-iteration, the in-progress iteration may not appear in
metrics.jsonl. Analyze the latest completed metric row.
Notes For Future Agents
- Prefer
rg/rg --filesfor search. - Use
apply_patchfor manual edits. - Do not commit generated run artifacts from
runs/. - Cython-generated
.cfiles are gitignored; edit.pyx/.pxdsources. - Before committing, run
uv run ruff check .and at least the relevant pytest subset. For Deep CFR changes, runuv run pytest -q tests/games/classic/test_deep_cfr_trainer.py.