cogames 0.3.49__py3-none-any.whl → 0.3.64__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cogames/cli/client.py +60 -6
- cogames/cli/docsync/__init__.py +0 -0
- cogames/cli/docsync/_nb_md_directive_processing.py +180 -0
- cogames/cli/docsync/_nb_md_sync.py +103 -0
- cogames/cli/docsync/_nb_py_sync.py +122 -0
- cogames/cli/docsync/_three_way_sync.py +115 -0
- cogames/cli/docsync/_utils.py +76 -0
- cogames/cli/docsync/docsync.py +156 -0
- cogames/cli/leaderboard.py +112 -28
- cogames/cli/mission.py +64 -53
- cogames/cli/policy.py +46 -10
- cogames/cli/submit.py +268 -67
- cogames/cogs_vs_clips/cog.py +79 -0
- cogames/cogs_vs_clips/cogs_vs_clips_mapgen.md +19 -16
- cogames/cogs_vs_clips/cogsguard_reward_variants.py +153 -0
- cogames/cogs_vs_clips/cogsguard_tutorial.py +56 -0
- cogames/cogs_vs_clips/evals/README.md +10 -16
- cogames/cogs_vs_clips/evals/cogsguard_evals.py +81 -0
- cogames/cogs_vs_clips/evals/diagnostic_evals.py +49 -444
- cogames/cogs_vs_clips/evals/difficulty_variants.py +13 -326
- cogames/cogs_vs_clips/evals/integrated_evals.py +5 -45
- cogames/cogs_vs_clips/evals/spanning_evals.py +9 -180
- cogames/cogs_vs_clips/mission.py +187 -146
- cogames/cogs_vs_clips/missions.py +46 -137
- cogames/cogs_vs_clips/procedural.py +8 -8
- cogames/cogs_vs_clips/sites.py +107 -3
- cogames/cogs_vs_clips/stations.py +198 -186
- cogames/cogs_vs_clips/tutorial_missions.py +1 -1
- cogames/cogs_vs_clips/variants.py +25 -476
- cogames/device.py +13 -1
- cogames/{policy/scripted_agent/README.md → docs/SCRIPTED_AGENT.md} +82 -58
- cogames/evaluate.py +18 -30
- cogames/main.py +1434 -243
- cogames/maps/canidate1_1000.map +1 -1
- cogames/maps/canidate1_1000_stations.map +2 -2
- cogames/maps/canidate1_500.map +1 -1
- cogames/maps/canidate1_500_stations.map +2 -2
- cogames/maps/canidate2_1000.map +1 -1
- cogames/maps/canidate2_1000_stations.map +2 -2
- cogames/maps/canidate2_500.map +1 -1
- cogames/maps/canidate2_500_stations.map +2 -2
- cogames/maps/canidate3_1000.map +1 -1
- cogames/maps/canidate3_1000_stations.map +2 -2
- cogames/maps/canidate3_500.map +1 -1
- cogames/maps/canidate3_500_stations.map +2 -2
- cogames/maps/canidate4_500.map +1 -1
- cogames/maps/canidate4_500_stations.map +2 -2
- cogames/maps/cave_base_50.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_agile.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_agile_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_charge_up.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_charge_up_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation1.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation1_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation2.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation2_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation3.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation3_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_near.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_search.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_search_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_extract_lab.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_extract_lab_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_memory.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_memory_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_radial.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_radial_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_resource_lab.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_unclip.map +2 -2
- cogames/maps/evals/eval_balanced_spread.map +9 -5
- cogames/maps/evals/eval_clip_oxygen.map +9 -5
- cogames/maps/evals/eval_collect_resources.map +9 -5
- cogames/maps/evals/eval_collect_resources_hard.map +9 -5
- cogames/maps/evals/eval_collect_resources_medium.map +9 -5
- cogames/maps/evals/eval_divide_and_conquer.map +9 -5
- cogames/maps/evals/eval_energy_starved.map +9 -5
- cogames/maps/evals/eval_multi_coordinated_collect_hard.map +9 -5
- cogames/maps/evals/eval_oxygen_bottleneck.map +9 -5
- cogames/maps/evals/eval_single_use_world.map +9 -5
- cogames/maps/evals/extractor_hub_100x100.map +9 -5
- cogames/maps/evals/extractor_hub_30x30.map +9 -5
- cogames/maps/evals/extractor_hub_50x50.map +9 -5
- cogames/maps/evals/extractor_hub_70x70.map +9 -5
- cogames/maps/evals/extractor_hub_80x80.map +9 -5
- cogames/maps/machina_100_stations.map +2 -2
- cogames/maps/machina_200_stations.map +2 -2
- cogames/maps/machina_200_stations_small.map +2 -2
- cogames/maps/machina_eval_exp01.map +2 -2
- cogames/maps/machina_eval_template_large.map +2 -2
- cogames/maps/machinatrainer4agents.map +2 -2
- cogames/maps/machinatrainer4agentsbase.map +2 -2
- cogames/maps/machinatrainerbig.map +2 -2
- cogames/maps/machinatrainersmall.map +2 -2
- cogames/maps/planky_evals/aligner_avoid_aoe.map +28 -0
- cogames/maps/planky_evals/aligner_full_cycle.map +28 -0
- cogames/maps/planky_evals/aligner_gear.map +24 -0
- cogames/maps/planky_evals/aligner_hearts.map +24 -0
- cogames/maps/planky_evals/aligner_junction.map +26 -0
- cogames/maps/planky_evals/exploration_distant.map +28 -0
- cogames/maps/planky_evals/maze.map +32 -0
- cogames/maps/planky_evals/miner_best_resource.map +26 -0
- cogames/maps/planky_evals/miner_deposit.map +24 -0
- cogames/maps/planky_evals/miner_extract.map +26 -0
- cogames/maps/planky_evals/miner_full_cycle.map +28 -0
- cogames/maps/planky_evals/miner_gear.map +24 -0
- cogames/maps/planky_evals/multi_role.map +28 -0
- cogames/maps/planky_evals/resource_chain.map +30 -0
- cogames/maps/planky_evals/scout_explore.map +32 -0
- cogames/maps/planky_evals/scout_gear.map +24 -0
- cogames/maps/planky_evals/scrambler_full_cycle.map +28 -0
- cogames/maps/planky_evals/scrambler_gear.map +24 -0
- cogames/maps/planky_evals/scrambler_target.map +26 -0
- cogames/maps/planky_evals/stuck_corridor.map +32 -0
- cogames/maps/planky_evals/survive_retreat.map +26 -0
- cogames/maps/training_facility_clipped.map +2 -2
- cogames/maps/training_facility_open_1.map +2 -2
- cogames/maps/training_facility_open_2.map +2 -2
- cogames/maps/training_facility_open_3.map +2 -2
- cogames/maps/training_facility_tight_4.map +2 -2
- cogames/maps/training_facility_tight_5.map +2 -2
- cogames/maps/vanilla_large.map +2 -2
- cogames/maps/vanilla_small.map +2 -2
- cogames/pickup.py +183 -0
- cogames/play.py +166 -33
- cogames/policy/chaos_monkey.py +54 -0
- cogames/policy/nim_agents/__init__.py +27 -10
- cogames/policy/nim_agents/agents.py +121 -60
- cogames/policy/nim_agents/thinky_eval.py +35 -222
- cogames/policy/pufferlib_policy.py +67 -32
- cogames/policy/starter_agent.py +184 -0
- cogames/policy/trainable_policy_template.py +4 -1
- cogames/train.py +51 -13
- cogames/verbose.py +2 -2
- cogames-0.3.64.dist-info/METADATA +1842 -0
- cogames-0.3.64.dist-info/RECORD +159 -0
- cogames-0.3.64.dist-info/licenses/LICENSE +21 -0
- cogames-0.3.64.dist-info/top_level.txt +2 -0
- metta_alo/__init__.py +0 -0
- metta_alo/job_specs.py +17 -0
- metta_alo/policy.py +16 -0
- metta_alo/pure_single_episode_runner.py +75 -0
- metta_alo/py.typed +0 -0
- metta_alo/rollout.py +322 -0
- metta_alo/scoring.py +168 -0
- cogames/maps/diagnostic_evals/diagnostic_assembler_near.map +0 -49
- cogames/maps/diagnostic_evals/diagnostic_assembler_search.map +0 -49
- cogames/maps/diagnostic_evals/diagnostic_assembler_search_hard.map +0 -89
- cogames/policy/nim_agents/common.nim +0 -887
- cogames/policy/nim_agents/install.sh +0 -1
- cogames/policy/nim_agents/ladybug_agent.nim +0 -984
- cogames/policy/nim_agents/nim_agents.nim +0 -55
- cogames/policy/nim_agents/nim_agents.nims +0 -14
- cogames/policy/nim_agents/nimby.lock +0 -3
- cogames/policy/nim_agents/racecar_agents.nim +0 -884
- cogames/policy/nim_agents/random_agents.nim +0 -68
- cogames/policy/nim_agents/test_agents.py +0 -53
- cogames/policy/nim_agents/thinky_agents.nim +0 -717
- cogames/policy/scripted_agent/baseline_agent.py +0 -1049
- cogames/policy/scripted_agent/demo_policy.py +0 -244
- cogames/policy/scripted_agent/pathfinding.py +0 -126
- cogames/policy/scripted_agent/starter_agent.py +0 -136
- cogames/policy/scripted_agent/types.py +0 -235
- cogames/policy/scripted_agent/unclipping_agent.py +0 -476
- cogames/policy/scripted_agent/utils.py +0 -385
- cogames-0.3.49.dist-info/METADATA +0 -406
- cogames-0.3.49.dist-info/RECORD +0 -136
- cogames-0.3.49.dist-info/top_level.txt +0 -1
- {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/WHEEL +0 -0
- {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import jupytext
|
|
6
|
+
import nbformat
|
|
7
|
+
import typer
|
|
8
|
+
from nbstripout import strip_output
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_cogames_root() -> Path:
|
|
12
|
+
"""Get cogames root path by finding pyproject.toml."""
|
|
13
|
+
for parent in Path(__file__).resolve().parents:
|
|
14
|
+
if (parent / "pyproject.toml").exists():
|
|
15
|
+
return parent
|
|
16
|
+
raise RuntimeError("Could not find cogames root (no pyproject.toml found)")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def lint_py_file(path: Path, /) -> None:
|
|
20
|
+
"""Run ruff format on a Python file."""
|
|
21
|
+
subprocess.run(["ruff", "format", str(path)], check=True, capture_output=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def run_notebook(*, nb_path: Path) -> None:
|
|
25
|
+
"""Execute notebook in place."""
|
|
26
|
+
typer.echo(f" Executing {nb_path.name}...")
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
["jupyter", "execute", nb_path.name, "--inplace"],
|
|
29
|
+
cwd=nb_path.parent,
|
|
30
|
+
capture_output=True,
|
|
31
|
+
text=True,
|
|
32
|
+
)
|
|
33
|
+
if result.returncode != 0:
|
|
34
|
+
typer.echo(f" Error: notebook execution failed: {result.stderr}", err=True)
|
|
35
|
+
raise typer.Exit(1)
|
|
36
|
+
typer.echo(f" Executed {nb_path.name}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def clean_notebook_metadata(*, nb_path: Path) -> None:
|
|
40
|
+
"""Strip metadata (execution counts, cell IDs, execution timestamps) but keep outputs.
|
|
41
|
+
|
|
42
|
+
Also ensures accelerator is set to GPU for Colab.
|
|
43
|
+
"""
|
|
44
|
+
typer.echo(f" Cleaning metadata from {nb_path.name}...")
|
|
45
|
+
nb = nbformat.read(nb_path, as_version=4)
|
|
46
|
+
nb = strip_output(
|
|
47
|
+
nb,
|
|
48
|
+
keep_output=True,
|
|
49
|
+
keep_count=False,
|
|
50
|
+
keep_id=False,
|
|
51
|
+
extra_keys=["cell.metadata.execution"],
|
|
52
|
+
)
|
|
53
|
+
# Ensure GPU accelerator for Colab
|
|
54
|
+
# See: https://github.com/mwouts/jupytext/pull/235#issuecomment-495010137
|
|
55
|
+
nb.metadata["accelerator"] = "GPU"
|
|
56
|
+
nbformat.write(nb, nb_path)
|
|
57
|
+
typer.echo(f" Cleaned metadata from {nb_path.name}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def files_equal(path1: str | Path, path2: str | Path, /) -> bool:
|
|
61
|
+
"""Check if two files have identical content."""
|
|
62
|
+
return Path(path1).read_bytes() == Path(path2).read_bytes()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def py_nb_content_equal(*, py_path: Path, nb_path: Path) -> bool:
|
|
66
|
+
"""Check if .py and .ipynb have equivalent content (ignoring outputs and linting)."""
|
|
67
|
+
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp:
|
|
68
|
+
tmp_py = Path(tmp.name)
|
|
69
|
+
try:
|
|
70
|
+
# Convert current .ipynb to .py and compare with existing .py
|
|
71
|
+
nb = jupytext.read(nb_path)
|
|
72
|
+
jupytext.write(nb, tmp_py, fmt="py:percent")
|
|
73
|
+
lint_py_file(tmp_py) # Format generated .py for fair comparison
|
|
74
|
+
return files_equal(py_path, tmp_py)
|
|
75
|
+
finally:
|
|
76
|
+
tmp_py.unlink(missing_ok=True)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
`cogames docsync` is a dev-only tool to sync cogames docs between .ipynb, .py, and .md formats.
|
|
3
|
+
|
|
4
|
+
Tests for this module are in tests/docsync/
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from cogames.cli.docsync._nb_md_sync import convert_nb_to_md
|
|
13
|
+
from cogames.cli.docsync._nb_py_sync import convert_nb_to_py, convert_py_to_nb
|
|
14
|
+
from cogames.cli.docsync._three_way_sync import get_stem_paths, is_stem_in_sync_three_way, sync_stem_three_way
|
|
15
|
+
from cogames.cli.docsync._utils import get_cogames_root
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(
|
|
18
|
+
help="""Sync cogames docs between .ipynb, .py, and .md formats
|
|
19
|
+
|
|
20
|
+
Directives (first line of code cell):
|
|
21
|
+
# <<hide>> - skip entire cell (code + output)
|
|
22
|
+
# <<hide-input>> - skip code, show output
|
|
23
|
+
# <<hide-output>> - show code, skip output
|
|
24
|
+
# <<collapse-input>> - wrap code in <details>
|
|
25
|
+
# <<collapse-output>> - wrap output in <details>
|
|
26
|
+
|
|
27
|
+
Placeholders (in markdown cells):
|
|
28
|
+
<<colab-link>> - replaced with Colab URL for this notebook
|
|
29
|
+
"""
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.command()
|
|
34
|
+
def nb_to_md(
|
|
35
|
+
nb_path: Annotated[Path, typer.Argument(help="Path to .ipynb file")],
|
|
36
|
+
skip_rerun: Annotated[bool, typer.Option("--skip-rerun", help="Skip re-running notebook")] = False,
|
|
37
|
+
cogames_root_raw: Annotated[
|
|
38
|
+
Path | None, typer.Option("--cogames-root", hidden=True, help="Override root path (for testing)")
|
|
39
|
+
] = None,
|
|
40
|
+
):
|
|
41
|
+
"""Convert .ipynb to .md (reruns notebook by default to update outputs)."""
|
|
42
|
+
|
|
43
|
+
if not nb_path.exists():
|
|
44
|
+
typer.echo(f"Error: {nb_path} not found", err=True)
|
|
45
|
+
raise typer.Exit(1)
|
|
46
|
+
if nb_path.suffix != ".ipynb":
|
|
47
|
+
typer.echo(f"Error: expected .ipynb file, got {nb_path.suffix}", err=True)
|
|
48
|
+
raise typer.Exit(1)
|
|
49
|
+
|
|
50
|
+
cogames_root = cogames_root_raw or get_cogames_root()
|
|
51
|
+
typer.echo(f"Exporting {nb_path.name}...")
|
|
52
|
+
convert_nb_to_md(nb_path=nb_path, should_rerun=not skip_rerun, cogames_root=cogames_root)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command()
|
|
56
|
+
def nb_to_py(
|
|
57
|
+
nb_path: Annotated[Path, typer.Argument(help="Path to .ipynb file")],
|
|
58
|
+
):
|
|
59
|
+
"""Convert notebook to .py percent format (ignores outputs, keeps code)."""
|
|
60
|
+
|
|
61
|
+
if not nb_path.exists():
|
|
62
|
+
typer.echo(f"Error: {nb_path} not found", err=True)
|
|
63
|
+
raise typer.Exit(1)
|
|
64
|
+
if nb_path.suffix != ".ipynb":
|
|
65
|
+
typer.echo(f"Error: expected .ipynb file, got {nb_path.suffix}", err=True)
|
|
66
|
+
raise typer.Exit(1)
|
|
67
|
+
|
|
68
|
+
py_path = nb_path.with_suffix(".py")
|
|
69
|
+
typer.echo(f"Converting {nb_path.name} to .py...")
|
|
70
|
+
convert_nb_to_py(nb_path=nb_path, py_path=py_path)
|
|
71
|
+
typer.echo(f"Created {py_path.name}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@app.command()
|
|
75
|
+
def py_to_nb(
|
|
76
|
+
py_path: Annotated[Path, typer.Argument(help="Path to .py file")],
|
|
77
|
+
skip_rerun: Annotated[bool, typer.Option("--skip-rerun", help="Skip re-running notebook")] = False,
|
|
78
|
+
):
|
|
79
|
+
"""Convert .py percent format to .ipynb (runs notebook by default to populate outputs)."""
|
|
80
|
+
|
|
81
|
+
if not py_path.exists():
|
|
82
|
+
typer.echo(f"Error: {py_path} not found", err=True)
|
|
83
|
+
raise typer.Exit(1)
|
|
84
|
+
if py_path.suffix != ".py":
|
|
85
|
+
typer.echo(f"Error: expected .py file, got {py_path.suffix}", err=True)
|
|
86
|
+
raise typer.Exit(1)
|
|
87
|
+
|
|
88
|
+
nb_path = py_path.with_suffix(".ipynb")
|
|
89
|
+
typer.echo(f"Converting {py_path.name} to .ipynb...")
|
|
90
|
+
convert_py_to_nb(py_path=py_path, nb_path=nb_path, should_rerun=not skip_rerun)
|
|
91
|
+
typer.echo(f"Created {nb_path.name}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@app.command()
|
|
95
|
+
def check(
|
|
96
|
+
skip_rerun: Annotated[bool, typer.Option("--skip-rerun", help="Skip re-running notebooks")] = False,
|
|
97
|
+
cogames_root_raw: Annotated[
|
|
98
|
+
Path | None, typer.Option("--cogames-root", hidden=True, help="Override root path (for testing)")
|
|
99
|
+
] = None,
|
|
100
|
+
):
|
|
101
|
+
"""Verify that .py, .ipynb, and .md files are all in sync (without modifying files)."""
|
|
102
|
+
cogames_root = cogames_root_raw or get_cogames_root()
|
|
103
|
+
stem_paths = get_stem_paths(cogames_root=cogames_root)
|
|
104
|
+
should_rerun = not skip_rerun
|
|
105
|
+
|
|
106
|
+
typer.echo(f"Checking {len(stem_paths)} notebook(s)...")
|
|
107
|
+
|
|
108
|
+
errors: list[str] = []
|
|
109
|
+
for stem_path in stem_paths:
|
|
110
|
+
stem_errors = is_stem_in_sync_three_way(
|
|
111
|
+
stem_path=stem_path, should_rerun=should_rerun, cogames_root=cogames_root
|
|
112
|
+
)
|
|
113
|
+
if stem_errors:
|
|
114
|
+
typer.echo(f" ✗ {stem_path.name}")
|
|
115
|
+
else:
|
|
116
|
+
typer.echo(f" ✓ {stem_path.name}")
|
|
117
|
+
errors.extend(stem_errors)
|
|
118
|
+
|
|
119
|
+
if errors:
|
|
120
|
+
typer.echo("\nNotebook documentation is out of sync!", err=True)
|
|
121
|
+
for error in errors:
|
|
122
|
+
typer.echo(f" {error}", err=True)
|
|
123
|
+
typer.echo("\nTo fix: run 'cogames docsync all'", err=True)
|
|
124
|
+
raise typer.Exit(1)
|
|
125
|
+
|
|
126
|
+
typer.echo("All notebooks are in sync!")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.command()
|
|
130
|
+
def all(
|
|
131
|
+
skip_rerun: Annotated[bool, typer.Option("--skip-rerun", help="Skip re-running notebooks")] = False,
|
|
132
|
+
cogames_root_raw: Annotated[
|
|
133
|
+
Path | None, typer.Option("--cogames-root", hidden=True, help="Override root path (for testing)")
|
|
134
|
+
] = None,
|
|
135
|
+
):
|
|
136
|
+
"""Resyncs README and tutorials/ .py and .ipynb files with each other and exports to .md files."""
|
|
137
|
+
cogames_root = cogames_root_raw or get_cogames_root()
|
|
138
|
+
stem_paths = get_stem_paths(cogames_root=cogames_root)
|
|
139
|
+
should_rerun = not skip_rerun
|
|
140
|
+
|
|
141
|
+
typer.echo(f"Found {len(stem_paths)} notebook(s) to process")
|
|
142
|
+
|
|
143
|
+
for stem_path in stem_paths:
|
|
144
|
+
stem_path_relative = stem_path.relative_to(cogames_root)
|
|
145
|
+
typer.echo(f"Processing {stem_path_relative}...")
|
|
146
|
+
sync_stem_three_way(
|
|
147
|
+
stem_path=stem_path,
|
|
148
|
+
should_rerun=should_rerun,
|
|
149
|
+
cogames_root=cogames_root,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
typer.echo(f"Done! Processed {len(stem_paths)} notebook(s)")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
app()
|
cogames/cli/leaderboard.py
CHANGED
|
@@ -36,6 +36,22 @@ def parse_policy_identifier(identifier: str) -> tuple[str, int | None]:
|
|
|
36
36
|
return identifier, None
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def parse_season_ref(season_ref: str) -> tuple[str, int | None]:
|
|
40
|
+
if ":v" in season_ref:
|
|
41
|
+
name, version_str = season_ref.rsplit(":v", 1)
|
|
42
|
+
try:
|
|
43
|
+
return name, int(version_str)
|
|
44
|
+
except ValueError:
|
|
45
|
+
return season_ref, None
|
|
46
|
+
if ":" in season_ref:
|
|
47
|
+
name, version_str = season_ref.rsplit(":", 1)
|
|
48
|
+
try:
|
|
49
|
+
return name, int(version_str)
|
|
50
|
+
except ValueError:
|
|
51
|
+
return season_ref, None
|
|
52
|
+
return season_ref, None
|
|
53
|
+
|
|
54
|
+
|
|
39
55
|
def _format_timestamp(value: Optional[str]) -> str:
|
|
40
56
|
"""Format ISO timestamps for CLI output."""
|
|
41
57
|
if not value:
|
|
@@ -64,40 +80,60 @@ def _get_client(login_server: str, server: str) -> TournamentServerClient | None
|
|
|
64
80
|
return TournamentServerClient.from_login(server_url=server, login_server=login_server)
|
|
65
81
|
|
|
66
82
|
|
|
83
|
+
def _help_callback(ctx: typer.Context, value: bool) -> None:
|
|
84
|
+
"""Callback for custom help option."""
|
|
85
|
+
if value:
|
|
86
|
+
console.print(ctx.get_help())
|
|
87
|
+
raise typer.Exit()
|
|
88
|
+
|
|
89
|
+
|
|
67
90
|
def submissions_cmd(
|
|
68
|
-
policy_name: Optional[str] = typer.
|
|
91
|
+
policy_name: Optional[str] = typer.Option(
|
|
69
92
|
None,
|
|
70
|
-
|
|
93
|
+
"--policy",
|
|
94
|
+
"-p",
|
|
95
|
+
metavar="POLICY",
|
|
96
|
+
help="Filter by policy name (e.g., 'my-policy' or 'my-policy:v3')",
|
|
97
|
+
rich_help_panel="Filter",
|
|
71
98
|
),
|
|
72
99
|
season: Optional[str] = typer.Option(
|
|
73
100
|
None,
|
|
74
101
|
"--season",
|
|
102
|
+
metavar="SEASON",
|
|
75
103
|
help="Filter by tournament season",
|
|
104
|
+
rich_help_panel="Filter",
|
|
76
105
|
),
|
|
77
106
|
login_server: str = typer.Option(
|
|
78
107
|
DEFAULT_COGAMES_SERVER,
|
|
79
108
|
"--login-server",
|
|
80
|
-
|
|
109
|
+
metavar="URL",
|
|
110
|
+
help="Authentication server URL",
|
|
111
|
+
rich_help_panel="Server",
|
|
81
112
|
),
|
|
82
113
|
server: str = typer.Option(
|
|
83
114
|
DEFAULT_SUBMIT_SERVER,
|
|
84
115
|
"--server",
|
|
85
116
|
"-s",
|
|
86
|
-
|
|
117
|
+
metavar="URL",
|
|
118
|
+
help="Tournament server URL",
|
|
119
|
+
rich_help_panel="Server",
|
|
87
120
|
),
|
|
88
121
|
json_output: bool = typer.Option(
|
|
89
122
|
False,
|
|
90
123
|
"--json",
|
|
91
|
-
help="Print
|
|
124
|
+
help="Print raw JSON instead of table",
|
|
125
|
+
rich_help_panel="Output",
|
|
126
|
+
),
|
|
127
|
+
_help: bool = typer.Option(
|
|
128
|
+
False,
|
|
129
|
+
"--help",
|
|
130
|
+
"-h",
|
|
131
|
+
help="Show this message and exit",
|
|
132
|
+
is_eager=True,
|
|
133
|
+
callback=_help_callback,
|
|
134
|
+
rich_help_panel="Other",
|
|
92
135
|
),
|
|
93
136
|
) -> None:
|
|
94
|
-
"""Show your uploaded policies and tournament submissions.
|
|
95
|
-
|
|
96
|
-
Examples:
|
|
97
|
-
cogames submissions # All uploads
|
|
98
|
-
cogames submissions --season beta # Submissions in beta season
|
|
99
|
-
cogames submissions my-policy # Info on a specific policy
|
|
100
|
-
"""
|
|
101
137
|
client = _get_client(login_server, server)
|
|
102
138
|
if not client:
|
|
103
139
|
return
|
|
@@ -167,7 +203,7 @@ def _show_season_submissions(
|
|
|
167
203
|
) -> None:
|
|
168
204
|
"""Show submissions for a specific season."""
|
|
169
205
|
try:
|
|
170
|
-
entries = client.get_season_policies(season, mine=True)
|
|
206
|
+
entries = client.get_season_policies(season, mine=True, include_hidden_seasons=True)
|
|
171
207
|
except httpx.HTTPStatusError as exc:
|
|
172
208
|
if exc.response.status_code == 404:
|
|
173
209
|
console.print(f"[red]Season '{season}' not found[/red]")
|
|
@@ -219,39 +255,50 @@ def _show_season_submissions(
|
|
|
219
255
|
|
|
220
256
|
def leaderboard_cmd(
|
|
221
257
|
season: str = typer.Option(
|
|
222
|
-
|
|
258
|
+
"beta-cogsguard",
|
|
223
259
|
"--season",
|
|
224
|
-
|
|
260
|
+
metavar="SEASON",
|
|
261
|
+
help="Tournament season name",
|
|
262
|
+
rich_help_panel="Tournament",
|
|
225
263
|
),
|
|
226
264
|
login_server: str = typer.Option(
|
|
227
265
|
DEFAULT_COGAMES_SERVER,
|
|
228
266
|
"--login-server",
|
|
229
|
-
|
|
267
|
+
metavar="URL",
|
|
268
|
+
help="Authentication server URL",
|
|
269
|
+
rich_help_panel="Server",
|
|
230
270
|
),
|
|
231
271
|
server: str = typer.Option(
|
|
232
272
|
DEFAULT_SUBMIT_SERVER,
|
|
233
273
|
"--server",
|
|
234
274
|
"-s",
|
|
235
|
-
|
|
275
|
+
metavar="URL",
|
|
276
|
+
help="Tournament server URL",
|
|
277
|
+
rich_help_panel="Server",
|
|
236
278
|
),
|
|
237
279
|
json_output: bool = typer.Option(
|
|
238
280
|
False,
|
|
239
281
|
"--json",
|
|
240
|
-
help="Print
|
|
282
|
+
help="Print raw JSON instead of table",
|
|
283
|
+
rich_help_panel="Output",
|
|
284
|
+
),
|
|
285
|
+
_help: bool = typer.Option(
|
|
286
|
+
False,
|
|
287
|
+
"--help",
|
|
288
|
+
"-h",
|
|
289
|
+
help="Show this message and exit",
|
|
290
|
+
is_eager=True,
|
|
291
|
+
callback=_help_callback,
|
|
292
|
+
rich_help_panel="Other",
|
|
241
293
|
),
|
|
242
294
|
) -> None:
|
|
243
|
-
"""Display the tournament leaderboard for a season.
|
|
244
|
-
|
|
245
|
-
Example:
|
|
246
|
-
cogames leaderboard --season beta
|
|
247
|
-
"""
|
|
248
295
|
client = _get_client(login_server, server)
|
|
249
296
|
if not client:
|
|
250
297
|
return
|
|
251
298
|
|
|
252
299
|
try:
|
|
253
300
|
with client:
|
|
254
|
-
entries = client.get_leaderboard(season)
|
|
301
|
+
entries = client.get_leaderboard(season, include_hidden_seasons=True)
|
|
255
302
|
except httpx.HTTPStatusError as exc:
|
|
256
303
|
if exc.response.status_code == 404:
|
|
257
304
|
console.print(f"[red]Season '{season}' not found[/red]")
|
|
@@ -290,30 +337,67 @@ def leaderboard_cmd(
|
|
|
290
337
|
|
|
291
338
|
|
|
292
339
|
def seasons_cmd(
|
|
340
|
+
season_name: Optional[str] = typer.Argument(None, help="Show versions of a specific season"),
|
|
341
|
+
versions: bool = typer.Option(False, "--versions", "-v", help="List all versions of the season"),
|
|
293
342
|
login_server: str = typer.Option(
|
|
294
343
|
DEFAULT_COGAMES_SERVER,
|
|
295
344
|
"--login-server",
|
|
296
|
-
|
|
345
|
+
metavar="URL",
|
|
346
|
+
help="Authentication server URL",
|
|
347
|
+
rich_help_panel="Server",
|
|
297
348
|
),
|
|
298
349
|
server: str = typer.Option(
|
|
299
350
|
DEFAULT_SUBMIT_SERVER,
|
|
300
351
|
"--server",
|
|
301
352
|
"-s",
|
|
302
|
-
|
|
353
|
+
metavar="URL",
|
|
354
|
+
help="Tournament server URL",
|
|
355
|
+
rich_help_panel="Server",
|
|
303
356
|
),
|
|
304
357
|
json_output: bool = typer.Option(
|
|
305
358
|
False,
|
|
306
359
|
"--json",
|
|
307
|
-
help="Print
|
|
360
|
+
help="Print raw JSON instead of table",
|
|
361
|
+
rich_help_panel="Output",
|
|
362
|
+
),
|
|
363
|
+
_help: bool = typer.Option(
|
|
364
|
+
False,
|
|
365
|
+
"--help",
|
|
366
|
+
"-h",
|
|
367
|
+
help="Show this message and exit",
|
|
368
|
+
is_eager=True,
|
|
369
|
+
callback=_help_callback,
|
|
370
|
+
rich_help_panel="Other",
|
|
308
371
|
),
|
|
309
372
|
) -> None:
|
|
310
|
-
"""List available tournament seasons."""
|
|
311
373
|
client = _get_client(login_server, server)
|
|
312
374
|
if not client:
|
|
313
375
|
return
|
|
314
376
|
|
|
315
377
|
try:
|
|
316
378
|
with client:
|
|
379
|
+
if season_name and versions:
|
|
380
|
+
season_versions = client.get_season_versions(season_name)
|
|
381
|
+
if json_output:
|
|
382
|
+
console.print(json.dumps([v.model_dump() for v in season_versions], indent=2))
|
|
383
|
+
return
|
|
384
|
+
|
|
385
|
+
if not season_versions:
|
|
386
|
+
console.print(f"[yellow]No versions found for season '{season_name}'.[/yellow]")
|
|
387
|
+
return
|
|
388
|
+
|
|
389
|
+
table = Table(title=f"Versions: {season_name}", box=box.SIMPLE_HEAVY, show_lines=False, pad_edge=False)
|
|
390
|
+
table.add_column("Version", style="bold cyan")
|
|
391
|
+
table.add_column("Status", style="white")
|
|
392
|
+
table.add_column("Created", style="dim")
|
|
393
|
+
|
|
394
|
+
for v in season_versions:
|
|
395
|
+
status = "[green]canonical[/green]" if v.canonical else "[dim]historical[/dim]"
|
|
396
|
+
table.add_row(f"v{v.version}", status, _format_timestamp(v.created_at))
|
|
397
|
+
|
|
398
|
+
console.print(table)
|
|
399
|
+
return
|
|
400
|
+
|
|
317
401
|
seasons = client.get_seasons()
|
|
318
402
|
except httpx.HTTPError as exc:
|
|
319
403
|
console.print(f"[red]Failed to reach server:[/red] {exc}")
|