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
cogames/main.py
CHANGED
|
@@ -18,7 +18,7 @@ import sys
|
|
|
18
18
|
import threading
|
|
19
19
|
import time
|
|
20
20
|
from pathlib import Path
|
|
21
|
-
from typing import
|
|
21
|
+
from typing import Literal, Optional, TypeVar
|
|
22
22
|
|
|
23
23
|
import typer
|
|
24
24
|
import yaml # type: ignore[import]
|
|
@@ -29,14 +29,16 @@ from rich.panel import Panel
|
|
|
29
29
|
from rich.prompt import Prompt
|
|
30
30
|
from rich.table import Table
|
|
31
31
|
|
|
32
|
-
import cogames.policy.
|
|
32
|
+
import cogames.policy.starter_agent as starter_agent
|
|
33
33
|
import cogames.policy.trainable_policy_template as trainable_policy_template
|
|
34
34
|
from cogames import evaluate as evaluate_module
|
|
35
35
|
from cogames import game, verbose
|
|
36
|
+
from cogames import pickup as pickup_module
|
|
36
37
|
from cogames import play as play_module
|
|
37
38
|
from cogames import train as train_module
|
|
38
39
|
from cogames.cli.base import console
|
|
39
|
-
from cogames.cli.client import TournamentServerClient
|
|
40
|
+
from cogames.cli.client import SeasonInfo, TournamentServerClient, fetch_default_season, fetch_season_info
|
|
41
|
+
from cogames.cli.docsync import docsync
|
|
40
42
|
from cogames.cli.leaderboard import (
|
|
41
43
|
leaderboard_cmd,
|
|
42
44
|
parse_policy_identifier,
|
|
@@ -53,14 +55,17 @@ from cogames.cli.mission import (
|
|
|
53
55
|
list_variants,
|
|
54
56
|
)
|
|
55
57
|
from cogames.cli.policy import (
|
|
58
|
+
_translate_error,
|
|
56
59
|
get_policy_spec,
|
|
57
60
|
get_policy_specs_with_proportions,
|
|
61
|
+
parse_policy_spec,
|
|
58
62
|
policy_arg_example,
|
|
59
63
|
policy_arg_w_proportion_example,
|
|
60
64
|
)
|
|
61
|
-
from cogames.cli.submit import DEFAULT_SUBMIT_SERVER, upload_policy, validate_policy_spec
|
|
65
|
+
from cogames.cli.submit import DEFAULT_SUBMIT_SERVER, results_url_for_season, upload_policy, validate_policy_spec
|
|
62
66
|
from cogames.curricula import make_rotation
|
|
63
67
|
from cogames.device import resolve_training_device
|
|
68
|
+
from mettagrid.config.mettagrid_config import MettaGridConfig
|
|
64
69
|
from mettagrid.mapgen.mapgen import MapGen
|
|
65
70
|
from mettagrid.policy.loader import discover_and_register_policies
|
|
66
71
|
from mettagrid.policy.policy_registry import get_policy_registry
|
|
@@ -100,13 +105,21 @@ def _resolve_mettascope_script() -> Path:
|
|
|
100
105
|
)
|
|
101
106
|
|
|
102
107
|
|
|
108
|
+
def _register_policies() -> None:
|
|
109
|
+
discover_and_register_policies()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _register_policies_callback() -> None:
|
|
113
|
+
_register_policies()
|
|
114
|
+
|
|
115
|
+
|
|
103
116
|
app = typer.Typer(
|
|
104
117
|
help="CoGames - Multi-agent cooperative and competitive games",
|
|
105
118
|
context_settings={"help_option_names": ["-h", "--help"]},
|
|
106
119
|
no_args_is_help=True,
|
|
107
120
|
rich_markup_mode="rich",
|
|
108
121
|
pretty_exceptions_show_locals=False,
|
|
109
|
-
callback=
|
|
122
|
+
callback=_register_policies_callback,
|
|
110
123
|
)
|
|
111
124
|
|
|
112
125
|
tutorial_app = typer.Typer(
|
|
@@ -119,8 +132,12 @@ tutorial_app = typer.Typer(
|
|
|
119
132
|
if register_tribal_cli is not None:
|
|
120
133
|
register_tribal_cli(app)
|
|
121
134
|
|
|
135
|
+
app.add_typer(docsync.app, name="docsync", hidden=True)
|
|
122
136
|
|
|
123
|
-
|
|
137
|
+
|
|
138
|
+
@tutorial_app.command(
|
|
139
|
+
name="play", help="Interactive tutorial - learn to play Cogs vs Clips", rich_help_panel="Tutorial"
|
|
140
|
+
)
|
|
124
141
|
def tutorial_cmd(
|
|
125
142
|
ctx: typer.Context,
|
|
126
143
|
) -> None:
|
|
@@ -130,8 +147,8 @@ def tutorial_cmd(
|
|
|
130
147
|
|
|
131
148
|
console.print(
|
|
132
149
|
Panel.fit(
|
|
133
|
-
"[bold cyan]MISSION BRIEFING:
|
|
134
|
-
"Welcome, Cognitive. This simulation mirrors frontline
|
|
150
|
+
"[bold cyan]MISSION BRIEFING: CogsGuard Training Sector[/bold cyan]\n\n"
|
|
151
|
+
"Welcome, Cognitive. This simulation mirrors frontline CogsGuard ops.\n"
|
|
135
152
|
"We will launch the Mettascope visual interface now.\n\n"
|
|
136
153
|
"When you are ready to deploy, press Enter below and then return here to receive instructions.",
|
|
137
154
|
title="Mission Briefing",
|
|
@@ -142,13 +159,11 @@ def tutorial_cmd(
|
|
|
142
159
|
Prompt.ask("[dim]Press Enter to launch simulation[/dim]", default="", show_default=False)
|
|
143
160
|
console.print("[dim]Initializing Mettascope...[/dim]")
|
|
144
161
|
|
|
145
|
-
# Load tutorial mission
|
|
146
|
-
from cogames.cogs_vs_clips.
|
|
162
|
+
# Load tutorial mission (CogsGuard)
|
|
163
|
+
from cogames.cogs_vs_clips.missions import make_cogsguard_mission
|
|
147
164
|
|
|
148
165
|
# Create environment config
|
|
149
|
-
env_cfg =
|
|
150
|
-
# Force 1 agent for tutorial
|
|
151
|
-
env_cfg.game.num_agents = 1
|
|
166
|
+
env_cfg = make_cogsguard_mission(num_agents=1, max_steps=1000).make_env()
|
|
152
167
|
|
|
153
168
|
stop_event = threading.Event()
|
|
154
169
|
|
|
@@ -174,7 +189,7 @@ def tutorial_cmd(
|
|
|
174
189
|
"Right Pane (Vibe Deck): Select icons here to change your Cog's broadcast resonance.",
|
|
175
190
|
"Zoom/Pan: Scroll or pinch to zoom the arena; drag to pan.",
|
|
176
191
|
"Click various buildings to view their details in the Left Pane.",
|
|
177
|
-
"Look for the
|
|
192
|
+
"Look for the Hub (Hub), Junctions, Gear Stations, and Extractors.",
|
|
178
193
|
"Click your Cog to assume control.",
|
|
179
194
|
),
|
|
180
195
|
},
|
|
@@ -182,46 +197,46 @@ def tutorial_cmd(
|
|
|
182
197
|
"title": "Step 2 — Movement & Energy",
|
|
183
198
|
"lines": (
|
|
184
199
|
"Use WASD or Arrow Keys to move your Cog.",
|
|
185
|
-
"Every move costs Energy,
|
|
200
|
+
"Every move costs Energy, and aligned hubs/junctions recharge you.",
|
|
186
201
|
"Watch your battery bar on the Cog or in the HUD.",
|
|
187
|
-
"If low, rest (skip turn), lean against a wall (walk into it),
|
|
188
|
-
"
|
|
202
|
+
"If low, rest (skip turn), lean against a wall (walk into it), or",
|
|
203
|
+
"stand near the Hub or an aligned Junction.",
|
|
189
204
|
),
|
|
190
205
|
},
|
|
191
206
|
{
|
|
192
|
-
"title": "Step 3 —
|
|
207
|
+
"title": "Step 3 — Gear Up",
|
|
193
208
|
"lines": (
|
|
194
209
|
"Primary interaction mode is WALKING INTO things.",
|
|
195
|
-
"Locate
|
|
196
|
-
" [yellow]
|
|
197
|
-
" [yellow]
|
|
198
|
-
"
|
|
199
|
-
"Note: Silicon ([yellow]S[/yellow]) costs 20 energy!",
|
|
210
|
+
"Locate a Gear Station and walk into it to equip a role:",
|
|
211
|
+
" [yellow]⛏ Miner[/yellow], [yellow]🔭 Scout[/yellow],",
|
|
212
|
+
" [yellow]🔗 Aligner[/yellow], [yellow]🌀 Scrambler[/yellow].",
|
|
213
|
+
"Gear costs are paid from the team commons.",
|
|
200
214
|
),
|
|
201
215
|
},
|
|
202
216
|
{
|
|
203
|
-
"title": "Step 4 —
|
|
217
|
+
"title": "Step 4 — Resources & Hearts",
|
|
204
218
|
"lines": (
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
219
|
+
"Find an Extractor station to gather elements:",
|
|
220
|
+
" [yellow]C[/yellow] (Carbon), [yellow]O[/yellow] (Oxygen),",
|
|
221
|
+
" [yellow]G[/yellow] (Germanium), [yellow]S[/yellow] (Silicon).",
|
|
222
|
+
"Visit the Chest to assemble or withdraw Hearts from the commons.",
|
|
208
223
|
),
|
|
209
224
|
},
|
|
210
225
|
{
|
|
211
|
-
"title": "Step 5 —
|
|
226
|
+
"title": "Step 5 — Junction Control",
|
|
212
227
|
"lines": (
|
|
213
|
-
"
|
|
214
|
-
"
|
|
215
|
-
"
|
|
216
|
-
"
|
|
228
|
+
"Junctions (junctions) can be aligned to your team.",
|
|
229
|
+
"As an Aligner: get Influence (stand near the Hub) + a Heart, then bump a neutral junction.",
|
|
230
|
+
"As a Scrambler: get a Heart, then bump an enemy-aligned junction to neutralize it.",
|
|
231
|
+
"Aligned junctions recharge energy for your team.",
|
|
217
232
|
),
|
|
218
233
|
},
|
|
219
234
|
{
|
|
220
235
|
"title": "Step 6 — Objective Complete",
|
|
221
236
|
"lines": (
|
|
222
237
|
"[bold green]🎉 Congratulations![/bold green] You have completed the tutorial.",
|
|
223
|
-
"You've mastered
|
|
224
|
-
"[bold cyan]You're now ready to tackle the full
|
|
238
|
+
"You've mastered movement, gear, resources, and junction control.",
|
|
239
|
+
"[bold cyan]You're now ready to tackle the full CogsGuard arena![/bold cyan]",
|
|
225
240
|
),
|
|
226
241
|
},
|
|
227
242
|
)
|
|
@@ -241,7 +256,7 @@ def tutorial_cmd(
|
|
|
241
256
|
|
|
242
257
|
console.print(
|
|
243
258
|
"[bold green]REFERENCE DOSSIERS[/bold green]\n"
|
|
244
|
-
"- [link=packages/cogames/MISSION.md]MISSION.md[/link]:
|
|
259
|
+
"- [link=packages/cogames/MISSION.md]MISSION.md[/link]: CogsGuard deployment orders.\n"
|
|
245
260
|
"- [link=packages/cogames/README.md]README.md[/link]: System overview and CLI quick start.\n"
|
|
246
261
|
"- [link=packages/cogames/TECHNICAL_MANUAL.md]TECHNICAL_MANUAL.md[/link]: FACE sensor/command schematics."
|
|
247
262
|
)
|
|
@@ -268,40 +283,297 @@ def tutorial_cmd(
|
|
|
268
283
|
stop_event.set()
|
|
269
284
|
|
|
270
285
|
|
|
271
|
-
|
|
286
|
+
@tutorial_app.command(
|
|
287
|
+
name="cogsguard",
|
|
288
|
+
help="Interactive CogsGuard tutorial - learn roles and territory control",
|
|
289
|
+
rich_help_panel="Tutorial",
|
|
290
|
+
)
|
|
291
|
+
def cogsguard_tutorial_cmd(
|
|
292
|
+
ctx: typer.Context,
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Run the CogsGuard tutorial."""
|
|
295
|
+
# Suppress logs during tutorial to keep instructions visible
|
|
296
|
+
logging.getLogger().setLevel(logging.ERROR)
|
|
297
|
+
|
|
298
|
+
console.print(
|
|
299
|
+
Panel.fit(
|
|
300
|
+
"[bold cyan]MISSION BRIEFING: CogsGuard Training[/bold cyan]\n\n"
|
|
301
|
+
"Welcome, Cognitive. This simulation introduces you to CogsGuard operations.\n"
|
|
302
|
+
"You will learn about specialized gear, resource management, and territory control.\n\n"
|
|
303
|
+
"When you are ready to deploy, press Enter below and then return here to receive instructions.",
|
|
304
|
+
title="CogsGuard Briefing",
|
|
305
|
+
border_style="green",
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
Prompt.ask("[dim]Press Enter to launch simulation[/dim]", default="", show_default=False)
|
|
310
|
+
console.print("[dim]Initializing Mettascope...[/dim]")
|
|
311
|
+
|
|
312
|
+
# Load CogsGuard tutorial mission
|
|
313
|
+
from cogames.cogs_vs_clips.cogsguard_tutorial import CogsGuardTutorialMission
|
|
314
|
+
|
|
315
|
+
# Create environment config
|
|
316
|
+
env_cfg = CogsGuardTutorialMission.make_env()
|
|
317
|
+
|
|
318
|
+
stop_event = threading.Event()
|
|
319
|
+
|
|
320
|
+
def _wait_for_enter(prompt: str) -> bool:
|
|
321
|
+
if stop_event.is_set():
|
|
322
|
+
return False
|
|
323
|
+
try:
|
|
324
|
+
Prompt.ask(prompt, default="", show_default=False)
|
|
325
|
+
except (KeyboardInterrupt, EOFError):
|
|
326
|
+
stop_event.set()
|
|
327
|
+
return False
|
|
328
|
+
return True
|
|
329
|
+
|
|
330
|
+
def run_cogsguard_tutorial_steps():
|
|
331
|
+
# Wait a moment for the window to appear
|
|
332
|
+
time.sleep(3)
|
|
333
|
+
|
|
334
|
+
tutorial_steps = (
|
|
335
|
+
{
|
|
336
|
+
"title": "Step 1 — Objective & Scoring",
|
|
337
|
+
"lines": (
|
|
338
|
+
"CogsGuard is a territory control game. Your team earns points by holding junctions.",
|
|
339
|
+
"[bold]Reward per tick[/bold] = junctions held / max_steps / num_cogs",
|
|
340
|
+
"Control more junctions, earn more points. You start in your Hub (center).",
|
|
341
|
+
),
|
|
342
|
+
"task": "Click your Cog to select it, then explore your Hub and familiarize yourself with the area.",
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"title": "Step 2 — The Clips Threat",
|
|
346
|
+
"lines": (
|
|
347
|
+
"[bold red]WARNING:[/bold red] Clips are automated enemies that expand territory!",
|
|
348
|
+
"Every ~300 steps, Clips [yellow]scramble[/yellow] nearby Cog junctions to neutral.",
|
|
349
|
+
"Every ~300 steps, Clips [yellow]capture[/yellow] nearby neutral junctions.",
|
|
350
|
+
"Clips expansion has a 25-cell radius. You must actively defend or be overrun!",
|
|
351
|
+
),
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"title": "Step 3 — Territory & Resources",
|
|
355
|
+
"lines": (
|
|
356
|
+
"Junctions and Hubs project effects in a [bold]10-cell radius[/bold]:",
|
|
357
|
+
"[green]Friendly territory:[/green] Restores +100 HP, +100 energy, +10 influence per tick.",
|
|
358
|
+
"[red]Enemy territory:[/red] Drains -1 HP and -100 influence per tick.",
|
|
359
|
+
"[bold]HP:[/bold] Base 100. You lose -1 HP/tick outside friendly territory.",
|
|
360
|
+
" At 0 HP, gear and hearts are [bold red]destroyed[/bold red].",
|
|
361
|
+
"[bold]Energy:[/bold] Base 20. Moving costs [yellow]3 energy[/yellow]. Regens +1/tick.",
|
|
362
|
+
"[yellow]Key insight:[/yellow] Aligners can't capture in enemy AOE (influence drains too fast).",
|
|
363
|
+
),
|
|
364
|
+
"task": "Walk outside your Hub, watch your HP drain, then return to heal.",
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"title": "Step 4 — Gear Stations",
|
|
368
|
+
"lines": (
|
|
369
|
+
"Equip gear at stations. Each costs 6 collective resources (different mixes):",
|
|
370
|
+
"[yellow]Miner[/yellow]: +40 cargo, 10x extraction. Cost: 1C/1O/[bold]3G[/bold]/1S",
|
|
371
|
+
"[yellow]Aligner[/yellow]: +20 influence cap, captures territory. Cost: [bold]3C[/bold]/1O/1G/1S",
|
|
372
|
+
"[yellow]Scrambler[/yellow]: +200 HP, disrupts enemy junctions. Cost: 1C/[bold]3O[/bold]/1G/1S",
|
|
373
|
+
"[yellow]Scout[/yellow]: +400 HP, +100 energy, mobile recon. Cost: 1C/1O/1G/[bold]3S[/bold]",
|
|
374
|
+
"Switching gear replaces your current gear (only hold one at a time).",
|
|
375
|
+
),
|
|
376
|
+
"task": "Find a Gear Station in your base and equip Miner gear (walk into it).",
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"title": "Step 5 — Capturing & Scrambling",
|
|
380
|
+
"lines": (
|
|
381
|
+
"[bold]To capture a neutral junction (Aligner only):[/bold]",
|
|
382
|
+
" • Requires: Aligner gear + [yellow]1 heart[/yellow] + [yellow]1 influence[/yellow]",
|
|
383
|
+
" • Must NOT be in enemy AOE (influence would be drained)",
|
|
384
|
+
"[bold]To scramble an enemy junction (Scrambler only):[/bold]",
|
|
385
|
+
" • Requires: Scrambler gear + [yellow]1 heart[/yellow]",
|
|
386
|
+
" • Converts enemy junction to neutral (then Aligners can capture it)",
|
|
387
|
+
),
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"title": "Step 6 — Resources & Hearts",
|
|
391
|
+
"lines": (
|
|
392
|
+
"[bold]Extractors:[/bold] Walk into them to gather resources (1 per use, 10 with Miner gear).",
|
|
393
|
+
"[bold]Deposit:[/bold] Walk into the Hub (center of Hub) to deposit resources.",
|
|
394
|
+
"[bold]Hearts:[/bold] At the Chest, convert [yellow]1C + 1O + 1G + 1S[/yellow] into 1 heart.",
|
|
395
|
+
" Hearts are spent to capture/scramble junctions.",
|
|
396
|
+
"[bold]Aligning:[/bold] Switch to Aligner gear, then walk into a neutral junction to capture it.",
|
|
397
|
+
"Team coordination: Miners gather → deposit → make hearts → Aligners/Scramblers use them.",
|
|
398
|
+
),
|
|
399
|
+
"task": (
|
|
400
|
+
"Extract resources (C/O/G/S), deposit at the Hub, craft a heart, "
|
|
401
|
+
"then switch to Aligner and capture a junction."
|
|
402
|
+
),
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"title": "Step 7 — Tutorial Complete",
|
|
406
|
+
"lines": (
|
|
407
|
+
"[bold green]Congratulations![/bold green] You've completed the CogsGuard tutorial.",
|
|
408
|
+
"",
|
|
409
|
+
"[bold]Remember the core loop:[/bold]",
|
|
410
|
+
" 1. Miners gather resources and deposit at the Hub",
|
|
411
|
+
" 2. Convert resources to hearts at the Chest",
|
|
412
|
+
" 3. Scramblers neutralize enemy junctions (1 heart each)",
|
|
413
|
+
" 4. Aligners capture neutral junctions (1 heart + 1 influence each)",
|
|
414
|
+
" 5. Defend against Clips expansion!",
|
|
415
|
+
"",
|
|
416
|
+
"[bold cyan]You're ready for full CogsGuard missions![/bold cyan]",
|
|
417
|
+
),
|
|
418
|
+
},
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
for idx, step in enumerate(tutorial_steps):
|
|
422
|
+
if stop_event.is_set():
|
|
423
|
+
return
|
|
424
|
+
console.print()
|
|
425
|
+
console.print(f"[bold cyan]{step['title']}[/bold cyan]")
|
|
426
|
+
console.print()
|
|
427
|
+
for line in step["lines"]:
|
|
428
|
+
console.print(f" • {line}")
|
|
429
|
+
# Display task if present
|
|
430
|
+
if "task" in step:
|
|
431
|
+
console.print()
|
|
432
|
+
console.print(f" [bold yellow]TASK:[/bold yellow] {step['task']}")
|
|
433
|
+
console.print()
|
|
434
|
+
if idx < len(tutorial_steps) - 1:
|
|
435
|
+
console.print("[dim]Press Enter to continue...[/dim]")
|
|
436
|
+
if not _wait_for_enter(""):
|
|
437
|
+
return
|
|
438
|
+
|
|
439
|
+
console.print("[dim]CogsGuard tutorial briefing complete. Good luck, Cognitive.[/dim]")
|
|
440
|
+
console.print("[dim]Close the Mettascope window to exit the tutorial.[/dim]")
|
|
441
|
+
|
|
442
|
+
# Start tutorial interaction in a background thread
|
|
443
|
+
tutorial_thread = threading.Thread(target=run_cogsguard_tutorial_steps, daemon=True)
|
|
444
|
+
tutorial_thread.start()
|
|
445
|
+
|
|
446
|
+
# Run play (blocks main thread)
|
|
447
|
+
try:
|
|
448
|
+
play_module.play(
|
|
449
|
+
console,
|
|
450
|
+
env_cfg=env_cfg,
|
|
451
|
+
policy_spec=get_policy_spec(ctx, "class=noop"),
|
|
452
|
+
game_name="cogsguard_tutorial",
|
|
453
|
+
render_mode="gui",
|
|
454
|
+
)
|
|
455
|
+
except KeyboardInterrupt:
|
|
456
|
+
logger.info("CogsGuard tutorial interrupted; exiting.")
|
|
457
|
+
finally:
|
|
458
|
+
stop_event.set()
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
app.add_typer(tutorial_app, name="tutorial", rich_help_panel="Tutorials")
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _help_callback(ctx: typer.Context, value: bool) -> None:
|
|
465
|
+
"""Callback for custom help option."""
|
|
466
|
+
if value:
|
|
467
|
+
console.print(ctx.get_help())
|
|
468
|
+
raise typer.Exit()
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
@app.command(
|
|
472
|
+
name="missions",
|
|
473
|
+
help="""List available missions.
|
|
474
|
+
|
|
475
|
+
This command has three modes:
|
|
476
|
+
|
|
477
|
+
[bold]1. List sites:[/bold] Run with no arguments to see all available sites.
|
|
272
478
|
|
|
479
|
+
[bold]2. List missions at a site:[/bold] Pass a site name (e.g., 'cogsguard_machina_1') to see its missions.
|
|
273
480
|
|
|
274
|
-
|
|
481
|
+
[bold]3. Describe a mission:[/bold] Use -m to describe a specific mission. Only in this mode do \
|
|
482
|
+
--cogs, --variant, --format, and --save have any effect.""",
|
|
483
|
+
rich_help_panel="Missions",
|
|
484
|
+
epilog="""[dim]Examples:[/dim]
|
|
485
|
+
|
|
486
|
+
[cyan]cogames missions[/cyan] List all sites
|
|
487
|
+
|
|
488
|
+
[cyan]cogames missions cogsguard_machina_1[/cyan] List missions at site
|
|
489
|
+
|
|
490
|
+
[cyan]cogames missions -m cogsguard_machina_1.basic[/cyan] Describe a mission
|
|
491
|
+
|
|
492
|
+
[cyan]cogames missions -m arena --format json[/cyan] Output as JSON""",
|
|
493
|
+
add_help_option=False,
|
|
494
|
+
)
|
|
275
495
|
@app.command("games", hidden=True)
|
|
276
496
|
@app.command("mission", hidden=True)
|
|
277
497
|
def games_cmd(
|
|
278
498
|
ctx: typer.Context,
|
|
279
|
-
|
|
280
|
-
|
|
499
|
+
# --- List ---
|
|
500
|
+
site: Optional[str] = typer.Argument(
|
|
501
|
+
None,
|
|
502
|
+
metavar="SITE",
|
|
503
|
+
help="Filter by site (e.g., cogsguard_machina_1)",
|
|
504
|
+
),
|
|
505
|
+
# --- Describe (requires -m) ---
|
|
506
|
+
mission: Optional[str] = typer.Option(
|
|
507
|
+
None,
|
|
508
|
+
"--mission",
|
|
509
|
+
"-m",
|
|
510
|
+
metavar="MISSION",
|
|
511
|
+
help="Mission to describe",
|
|
512
|
+
rich_help_panel="Describe",
|
|
513
|
+
),
|
|
514
|
+
cogs: Optional[int] = typer.Option(
|
|
515
|
+
None,
|
|
516
|
+
"--cogs",
|
|
517
|
+
"-c",
|
|
518
|
+
help="Override agent count (requires -m)",
|
|
519
|
+
rich_help_panel="Describe",
|
|
520
|
+
),
|
|
281
521
|
variant: Optional[list[str]] = typer.Option( # noqa: B008
|
|
282
522
|
None,
|
|
283
523
|
"--variant",
|
|
284
524
|
"-v",
|
|
285
|
-
|
|
525
|
+
metavar="VARIANT",
|
|
526
|
+
help="Apply variant (requires -m, repeatable)",
|
|
527
|
+
rich_help_panel="Describe",
|
|
286
528
|
),
|
|
287
529
|
format_: Optional[Literal["yaml", "json"]] = typer.Option(
|
|
288
|
-
None,
|
|
530
|
+
None,
|
|
531
|
+
"--format",
|
|
532
|
+
help="Output format (requires -m)",
|
|
533
|
+
rich_help_panel="Describe",
|
|
289
534
|
),
|
|
290
535
|
save: Optional[Path] = typer.Option( # noqa: B008
|
|
291
536
|
None,
|
|
292
537
|
"--save",
|
|
293
538
|
"-s",
|
|
294
|
-
|
|
539
|
+
metavar="PATH",
|
|
540
|
+
help="Save config to file (requires -m)",
|
|
541
|
+
rich_help_panel="Describe",
|
|
542
|
+
),
|
|
543
|
+
# --- Debug ---
|
|
544
|
+
print_cvc_config: bool = typer.Option(
|
|
545
|
+
False,
|
|
546
|
+
"--print-cvc-config",
|
|
547
|
+
help="Print CVC mission config (requires -m)",
|
|
548
|
+
hidden=True,
|
|
549
|
+
),
|
|
550
|
+
print_mg_config: bool = typer.Option(
|
|
551
|
+
False,
|
|
552
|
+
"--print-mg-config",
|
|
553
|
+
help="Print MettaGrid config (requires -m)",
|
|
554
|
+
hidden=True,
|
|
555
|
+
),
|
|
556
|
+
# --- Help ---
|
|
557
|
+
_help: bool = typer.Option(
|
|
558
|
+
False,
|
|
559
|
+
"--help",
|
|
560
|
+
"-h",
|
|
561
|
+
help="Show this message and exit",
|
|
562
|
+
is_eager=True,
|
|
563
|
+
callback=_help_callback,
|
|
564
|
+
rich_help_panel="Other",
|
|
295
565
|
),
|
|
296
|
-
print_cvc_config: bool = typer.Option(False, "--print-cvc-config", help="Print Mission config (CVC config)"),
|
|
297
|
-
print_mg_config: bool = typer.Option(False, "--print-mg-config", help="Print MettaGridConfig"),
|
|
298
|
-
site: Optional[str] = typer.Argument(None, help="Site to list missions for (e.g., training_facility)"),
|
|
299
566
|
) -> None:
|
|
300
567
|
if mission is None:
|
|
301
568
|
list_missions(site)
|
|
302
569
|
return
|
|
303
570
|
|
|
304
|
-
|
|
571
|
+
try:
|
|
572
|
+
resolved_mission, env_cfg, mission_cfg = get_mission_name_and_config(ctx, mission, variant, cogs)
|
|
573
|
+
except typer.Exit as exc:
|
|
574
|
+
if exc.exit_code != 1:
|
|
575
|
+
raise
|
|
576
|
+
return
|
|
305
577
|
|
|
306
578
|
if print_cvc_config or print_mg_config:
|
|
307
579
|
try:
|
|
@@ -338,64 +610,190 @@ def games_cmd(
|
|
|
338
610
|
raise typer.Exit(1) from exc
|
|
339
611
|
|
|
340
612
|
|
|
341
|
-
@app.command("evals", help="List all eval missions")
|
|
613
|
+
@app.command("evals", help="List all eval missions", rich_help_panel="Missions")
|
|
342
614
|
def evals_cmd() -> None:
|
|
343
615
|
list_evals()
|
|
344
616
|
|
|
345
617
|
|
|
346
|
-
@app.command("variants", help="List all available mission variants")
|
|
618
|
+
@app.command("variants", help="List all available mission variants", rich_help_panel="Missions")
|
|
347
619
|
def variants_cmd() -> None:
|
|
348
620
|
list_variants()
|
|
349
621
|
|
|
350
622
|
|
|
351
|
-
@app.command(
|
|
623
|
+
@app.command(
|
|
624
|
+
name="describe",
|
|
625
|
+
help="Describe a mission and its configuration",
|
|
626
|
+
rich_help_panel="Missions",
|
|
627
|
+
epilog="""[dim]Examples:[/dim]
|
|
628
|
+
|
|
629
|
+
[cyan]cogames describe hello_world.open_world[/cyan] Describe mission
|
|
630
|
+
|
|
631
|
+
[cyan]cogames describe arena -c 4 -v dark_side[/cyan] With 4 cogs and variant""",
|
|
632
|
+
add_help_option=False,
|
|
633
|
+
)
|
|
352
634
|
def describe_cmd(
|
|
353
635
|
ctx: typer.Context,
|
|
354
|
-
mission: str = typer.Argument(
|
|
355
|
-
|
|
636
|
+
mission: str = typer.Argument(
|
|
637
|
+
...,
|
|
638
|
+
metavar="MISSION",
|
|
639
|
+
help="Mission name (e.g., hello_world.open_world)",
|
|
640
|
+
),
|
|
641
|
+
cogs: Optional[int] = typer.Option(
|
|
642
|
+
None,
|
|
643
|
+
"--cogs",
|
|
644
|
+
"-c",
|
|
645
|
+
help="Number of cogs (agents)",
|
|
646
|
+
rich_help_panel="Configuration",
|
|
647
|
+
),
|
|
356
648
|
variant: Optional[list[str]] = typer.Option( # noqa: B008
|
|
357
649
|
None,
|
|
358
650
|
"--variant",
|
|
359
651
|
"-v",
|
|
360
|
-
|
|
652
|
+
metavar="VARIANT",
|
|
653
|
+
help="Apply variant (repeatable)",
|
|
654
|
+
rich_help_panel="Configuration",
|
|
655
|
+
),
|
|
656
|
+
_help: bool = typer.Option(
|
|
657
|
+
False,
|
|
658
|
+
"--help",
|
|
659
|
+
"-h",
|
|
660
|
+
help="Show this message and exit",
|
|
661
|
+
is_eager=True,
|
|
662
|
+
callback=_help_callback,
|
|
663
|
+
rich_help_panel="Other",
|
|
361
664
|
),
|
|
362
665
|
) -> None:
|
|
363
666
|
resolved_mission, env_cfg, mission_cfg = get_mission_name_and_config(ctx, mission, variant, cogs)
|
|
364
667
|
describe_mission(resolved_mission, env_cfg, mission_cfg)
|
|
365
668
|
|
|
366
669
|
|
|
367
|
-
@app.command(
|
|
670
|
+
@app.command(
|
|
671
|
+
name="play",
|
|
672
|
+
rich_help_panel="Play",
|
|
673
|
+
help="""Play a game interactively.
|
|
674
|
+
|
|
675
|
+
This runs a single episode of the game using the specified policy.
|
|
676
|
+
|
|
677
|
+
By default, the policy is 'noop', so agents won't move unless manually controlled.
|
|
678
|
+
To see agents move by themselves, use `--policy class=random` or `--policy class=baseline`.
|
|
679
|
+
|
|
680
|
+
You can manually control the actions of a specific cog by clicking on a cog
|
|
681
|
+
in GUI mode or pressing M in unicode mode and using your arrow or WASD keys.
|
|
682
|
+
Log mode is non-interactive and doesn't support manual control.
|
|
683
|
+
""",
|
|
684
|
+
epilog="""[dim]Examples:[/dim]
|
|
685
|
+
|
|
686
|
+
[cyan]cogames play -m cogsguard_machina_1.basic[/cyan] Interactive
|
|
687
|
+
|
|
688
|
+
[cyan]cogames play -m cogsguard_machina_1.basic -p class=random[/cyan] Random policy
|
|
689
|
+
|
|
690
|
+
[cyan]cogames play -m cogsguard_machina_1.basic -c 4 -p class=baseline[/cyan] Baseline, 4 cogs
|
|
691
|
+
|
|
692
|
+
[cyan]cogames play -m cogsguard_machina_1 -r unicode[/cyan] Terminal mode""",
|
|
693
|
+
add_help_option=False,
|
|
694
|
+
)
|
|
368
695
|
def play_cmd(
|
|
369
696
|
ctx: typer.Context,
|
|
370
|
-
|
|
371
|
-
|
|
697
|
+
# --- Game Setup ---
|
|
698
|
+
mission: Optional[str] = typer.Option(
|
|
699
|
+
None,
|
|
700
|
+
"--mission",
|
|
701
|
+
"-m",
|
|
702
|
+
metavar="MISSION",
|
|
703
|
+
help="Mission to play (run [bold]cogames missions[/bold] to list)",
|
|
704
|
+
rich_help_panel="Game Setup",
|
|
705
|
+
),
|
|
372
706
|
variant: Optional[list[str]] = typer.Option( # noqa: B008
|
|
373
707
|
None,
|
|
374
708
|
"--variant",
|
|
375
709
|
"-v",
|
|
376
|
-
|
|
710
|
+
metavar="VARIANT",
|
|
711
|
+
help="Apply variant modifier (repeatable)",
|
|
712
|
+
rich_help_panel="Game Setup",
|
|
713
|
+
),
|
|
714
|
+
cogs: Optional[int] = typer.Option(
|
|
715
|
+
None,
|
|
716
|
+
"--cogs",
|
|
717
|
+
"-c",
|
|
718
|
+
metavar="N",
|
|
719
|
+
help="Number of cogs/agents",
|
|
720
|
+
show_default="from mission",
|
|
721
|
+
rich_help_panel="Game Setup",
|
|
722
|
+
),
|
|
723
|
+
# --- Policy ---
|
|
724
|
+
policy: str = typer.Option(
|
|
725
|
+
"class=noop",
|
|
726
|
+
"--policy",
|
|
727
|
+
"-p",
|
|
728
|
+
metavar="POLICY",
|
|
729
|
+
help="Policy controlling cogs ([bold]noop[/bold], [bold]random[/bold], [bold]lstm[/bold], or path)",
|
|
730
|
+
rich_help_panel="Policy",
|
|
731
|
+
),
|
|
732
|
+
# --- Simulation ---
|
|
733
|
+
steps: int = typer.Option(
|
|
734
|
+
1000,
|
|
735
|
+
"--steps",
|
|
736
|
+
"-s",
|
|
737
|
+
metavar="N",
|
|
738
|
+
help="Max steps per episode",
|
|
739
|
+
rich_help_panel="Simulation",
|
|
740
|
+
),
|
|
741
|
+
render: RenderMode = typer.Option( # noqa: B008
|
|
742
|
+
"gui",
|
|
743
|
+
"--render",
|
|
744
|
+
"-r",
|
|
745
|
+
help=(
|
|
746
|
+
"[bold]gui[/bold]=MettaScope, [bold]vibescope[/bold]=VibeScope, "
|
|
747
|
+
"[bold]unicode[/bold]=terminal, [bold]log[/bold]=metrics only"
|
|
748
|
+
),
|
|
749
|
+
rich_help_panel="Simulation",
|
|
750
|
+
),
|
|
751
|
+
seed: int = typer.Option(
|
|
752
|
+
42,
|
|
753
|
+
"--seed",
|
|
754
|
+
help="RNG seed for reproducibility",
|
|
755
|
+
rich_help_panel="Simulation",
|
|
377
756
|
),
|
|
378
|
-
policy: str = typer.Option("class=noop", "--policy", "-p", help=f"Policy ({policy_arg_example})"),
|
|
379
|
-
steps: int = typer.Option(1000, "--steps", "-s", help="Number of steps to run", min=1),
|
|
380
|
-
render: RenderMode = typer.Option("gui", "--render", "-r", help="Render mode"), # noqa: B008
|
|
381
|
-
seed: int = typer.Option(42, "--seed", help="Seed for the simulator and policy", min=0),
|
|
382
757
|
map_seed: Optional[int] = typer.Option(
|
|
383
758
|
None,
|
|
384
759
|
"--map-seed",
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
False, "--print-cvc-config", help="Print Mission config (CVC config) and exit"
|
|
760
|
+
metavar="SEED",
|
|
761
|
+
help="Separate seed for procedural map generation",
|
|
762
|
+
show_default="same as --seed",
|
|
763
|
+
rich_help_panel="Simulation",
|
|
390
764
|
),
|
|
391
|
-
|
|
765
|
+
# --- Output ---
|
|
392
766
|
save_replay_dir: Optional[Path] = typer.Option( # noqa: B008
|
|
393
767
|
None,
|
|
394
768
|
"--save-replay-dir",
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
769
|
+
metavar="DIR",
|
|
770
|
+
help="Save replay file for later viewing with [bold]cogames replay[/bold]",
|
|
771
|
+
rich_help_panel="Output",
|
|
772
|
+
),
|
|
773
|
+
# --- Debug (hidden from casual users) ---
|
|
774
|
+
print_cvc_config: bool = typer.Option(
|
|
775
|
+
False,
|
|
776
|
+
"--print-cvc-config",
|
|
777
|
+
help="Print mission config and exit",
|
|
778
|
+
rich_help_panel="Debug",
|
|
779
|
+
hidden=True,
|
|
780
|
+
),
|
|
781
|
+
print_mg_config: bool = typer.Option(
|
|
782
|
+
False,
|
|
783
|
+
"--print-mg-config",
|
|
784
|
+
help="Print MettaGrid config and exit",
|
|
785
|
+
rich_help_panel="Debug",
|
|
786
|
+
hidden=True,
|
|
787
|
+
),
|
|
788
|
+
# --- Help at end ---
|
|
789
|
+
_help: bool = typer.Option(
|
|
790
|
+
False,
|
|
791
|
+
"--help",
|
|
792
|
+
"-h",
|
|
793
|
+
help="Show this message and exit",
|
|
794
|
+
is_eager=True,
|
|
795
|
+
callback=_help_callback,
|
|
796
|
+
rich_help_panel="Other",
|
|
399
797
|
),
|
|
400
798
|
) -> None:
|
|
401
799
|
resolved_mission, env_cfg, mission_cfg = get_mission_name_and_config(ctx, mission, variant, cogs)
|
|
@@ -407,15 +805,11 @@ def play_cmd(
|
|
|
407
805
|
console.print(f"[red]Error printing config: {exc}[/red]")
|
|
408
806
|
raise typer.Exit(1) from exc
|
|
409
807
|
|
|
410
|
-
#
|
|
411
|
-
|
|
412
|
-
from mettagrid.mapgen.mapgen import MapGen
|
|
413
|
-
|
|
414
|
-
effective_map_seed: Optional[int] = map_seed if map_seed is not None else seed
|
|
415
|
-
if effective_map_seed is not None:
|
|
808
|
+
# Optional MapGen seed override for procedural maps.
|
|
809
|
+
if map_seed is not None:
|
|
416
810
|
map_builder = getattr(env_cfg.game, "map_builder", None)
|
|
417
|
-
if isinstance(map_builder, MapGen.Config)
|
|
418
|
-
map_builder.seed =
|
|
811
|
+
if isinstance(map_builder, MapGen.Config):
|
|
812
|
+
map_builder.seed = map_seed
|
|
419
813
|
|
|
420
814
|
policy_spec = get_policy_spec(ctx, policy)
|
|
421
815
|
console.print(f"[cyan]Playing {resolved_mission}[/cyan]")
|
|
@@ -439,11 +833,32 @@ def play_cmd(
|
|
|
439
833
|
)
|
|
440
834
|
|
|
441
835
|
|
|
442
|
-
@app.command(
|
|
836
|
+
@app.command(
|
|
837
|
+
name="replay",
|
|
838
|
+
help="Replay a saved game episode from a file in the GUI",
|
|
839
|
+
rich_help_panel="Play",
|
|
840
|
+
epilog="""[dim]Examples:[/dim]
|
|
841
|
+
|
|
842
|
+
[cyan]cogames replay ./replays/game.replay[/cyan] Replay a saved game
|
|
843
|
+
|
|
844
|
+
[cyan]cogames replay ./train_dir/my_run/replay.bin[/cyan] Replay from training run""",
|
|
845
|
+
add_help_option=False,
|
|
846
|
+
)
|
|
443
847
|
def replay_cmd(
|
|
444
|
-
replay_path: Path = typer.Argument(
|
|
848
|
+
replay_path: Path = typer.Argument( # noqa: B008
|
|
849
|
+
...,
|
|
850
|
+
metavar="FILE",
|
|
851
|
+
help="Path to the replay file (.replay or .bin)",
|
|
852
|
+
),
|
|
853
|
+
_help: bool = typer.Option(
|
|
854
|
+
False,
|
|
855
|
+
"--help",
|
|
856
|
+
"-h",
|
|
857
|
+
help="Show this message and exit",
|
|
858
|
+
is_eager=True,
|
|
859
|
+
callback=_help_callback,
|
|
860
|
+
),
|
|
445
861
|
) -> None:
|
|
446
|
-
"""Replay a saved game using MettaScope visualization tool."""
|
|
447
862
|
if not replay_path.exists():
|
|
448
863
|
console.print(f"[red]Error: Replay file not found: {replay_path}[/red]")
|
|
449
864
|
raise typer.Exit(1)
|
|
@@ -468,15 +883,73 @@ def replay_cmd(
|
|
|
468
883
|
raise typer.Exit(1) from exc
|
|
469
884
|
|
|
470
885
|
|
|
471
|
-
@app.command(
|
|
886
|
+
@app.command(
|
|
887
|
+
name="make-mission",
|
|
888
|
+
help="Create a custom mission from a base template",
|
|
889
|
+
rich_help_panel="Missions",
|
|
890
|
+
epilog="""[dim]Examples:[/dim]
|
|
891
|
+
|
|
892
|
+
[cyan]cogames make-mission -m hello_world -c 8 -o my_mission.yml[/cyan] 8 cogs
|
|
893
|
+
|
|
894
|
+
[cyan]cogames make-mission -m arena --width 64 --height 64 -o big.yml[/cyan] 64x64 map
|
|
895
|
+
|
|
896
|
+
[cyan]cogames play -m my_mission.yml[/cyan] Use custom mission""",
|
|
897
|
+
add_help_option=False,
|
|
898
|
+
)
|
|
472
899
|
@app.command("make-game", hidden=True)
|
|
473
900
|
def make_mission(
|
|
474
901
|
ctx: typer.Context,
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
902
|
+
# --- Mission ---
|
|
903
|
+
base_mission: Optional[str] = typer.Option(
|
|
904
|
+
None,
|
|
905
|
+
"--mission",
|
|
906
|
+
"-m",
|
|
907
|
+
metavar="MISSION",
|
|
908
|
+
help="Base mission to start from",
|
|
909
|
+
rich_help_panel="Mission",
|
|
910
|
+
),
|
|
911
|
+
# --- Customization ---
|
|
912
|
+
cogs: Optional[int] = typer.Option(
|
|
913
|
+
None,
|
|
914
|
+
"--cogs",
|
|
915
|
+
"-c",
|
|
916
|
+
help="Number of cogs (agents)",
|
|
917
|
+
min=1,
|
|
918
|
+
rich_help_panel="Customization",
|
|
919
|
+
),
|
|
920
|
+
width: Optional[int] = typer.Option(
|
|
921
|
+
None,
|
|
922
|
+
"--width",
|
|
923
|
+
help="Map width",
|
|
924
|
+
min=1,
|
|
925
|
+
rich_help_panel="Customization",
|
|
926
|
+
),
|
|
927
|
+
height: Optional[int] = typer.Option(
|
|
928
|
+
None,
|
|
929
|
+
"--height",
|
|
930
|
+
help="Map height",
|
|
931
|
+
min=1,
|
|
932
|
+
rich_help_panel="Customization",
|
|
933
|
+
),
|
|
934
|
+
# --- Output ---
|
|
935
|
+
output: Optional[Path] = typer.Option( # noqa: B008
|
|
936
|
+
None,
|
|
937
|
+
"--output",
|
|
938
|
+
"-o",
|
|
939
|
+
metavar="PATH",
|
|
940
|
+
help="Output file path (.yml or .json)",
|
|
941
|
+
rich_help_panel="Output",
|
|
942
|
+
),
|
|
943
|
+
# --- Help ---
|
|
944
|
+
_help: bool = typer.Option(
|
|
945
|
+
False,
|
|
946
|
+
"--help",
|
|
947
|
+
"-h",
|
|
948
|
+
help="Show this message and exit",
|
|
949
|
+
is_eager=True,
|
|
950
|
+
callback=_help_callback,
|
|
951
|
+
rich_help_panel="Other",
|
|
952
|
+
),
|
|
480
953
|
) -> None:
|
|
481
954
|
try:
|
|
482
955
|
resolved_mission, env_cfg, _ = get_mission_name_and_config(ctx, base_mission)
|
|
@@ -514,13 +987,52 @@ def make_mission(
|
|
|
514
987
|
raise typer.Exit(1) from exc
|
|
515
988
|
|
|
516
989
|
|
|
517
|
-
|
|
990
|
+
# TODO (cogsguard migration): Verify make-policy templates work with CogsGuard game mechanics
|
|
991
|
+
@tutorial_app.command(
|
|
992
|
+
name="make-policy",
|
|
993
|
+
help="Create a new policy from a template. Requires --trainable or --scripted.",
|
|
994
|
+
rich_help_panel="Tutorial",
|
|
995
|
+
epilog="""[dim]Examples:[/dim]
|
|
996
|
+
|
|
997
|
+
[cyan]cogames tutorial make-policy -t -o my_nn_policy.py[/cyan] Trainable (neural network)
|
|
998
|
+
|
|
999
|
+
[cyan]cogames tutorial make-policy -s -o my_scripted_policy.py[/cyan] Scripted (rule-based)""",
|
|
1000
|
+
add_help_option=False,
|
|
1001
|
+
)
|
|
518
1002
|
def make_policy(
|
|
519
|
-
|
|
520
|
-
trainable: bool = typer.Option(
|
|
521
|
-
|
|
1003
|
+
# --- Policy Type ---
|
|
1004
|
+
trainable: bool = typer.Option(
|
|
1005
|
+
False,
|
|
1006
|
+
"--trainable",
|
|
1007
|
+
help="Create a trainable (neural network) policy",
|
|
1008
|
+
rich_help_panel="Policy Type",
|
|
1009
|
+
),
|
|
1010
|
+
scripted: bool = typer.Option(
|
|
1011
|
+
False,
|
|
1012
|
+
"--scripted",
|
|
1013
|
+
help="Create a scripted (rule-based) policy",
|
|
1014
|
+
rich_help_panel="Policy Type",
|
|
1015
|
+
),
|
|
1016
|
+
# --- Output ---
|
|
1017
|
+
output: Path = typer.Option( # noqa: B008
|
|
1018
|
+
"my_policy.py",
|
|
1019
|
+
"--output",
|
|
1020
|
+
"-o",
|
|
1021
|
+
metavar="FILE",
|
|
1022
|
+
help="Output file path",
|
|
1023
|
+
rich_help_panel="Output",
|
|
1024
|
+
),
|
|
1025
|
+
# --- Help ---
|
|
1026
|
+
_help: bool = typer.Option(
|
|
1027
|
+
False,
|
|
1028
|
+
"--help",
|
|
1029
|
+
"-h",
|
|
1030
|
+
help="Show this message and exit",
|
|
1031
|
+
is_eager=True,
|
|
1032
|
+
callback=_help_callback,
|
|
1033
|
+
rich_help_panel="Other",
|
|
1034
|
+
),
|
|
522
1035
|
) -> None:
|
|
523
|
-
"""Create a new policy from a template. Requires either --trainable or --scripted."""
|
|
524
1036
|
if trainable == scripted:
|
|
525
1037
|
console.print("[red]Error: Specify exactly one of --trainable or --scripted[/red]")
|
|
526
1038
|
console.print("[dim]Examples:[/dim]")
|
|
@@ -550,14 +1062,20 @@ def make_policy(
|
|
|
550
1062
|
shutil.copy2(template_path, dest_path)
|
|
551
1063
|
console.print(f"[green]{policy_type} policy template copied to: {dest_path}[/green]")
|
|
552
1064
|
|
|
1065
|
+
if not trainable:
|
|
1066
|
+
content = dest_path.read_text()
|
|
1067
|
+
lines = content.splitlines()
|
|
1068
|
+
lines = [line for line in lines if not line.strip().startswith("short_names =")]
|
|
1069
|
+
dest_path.write_text("\n".join(lines) + "\n")
|
|
1070
|
+
|
|
553
1071
|
if trainable:
|
|
554
1072
|
console.print(
|
|
555
|
-
"[dim]Train with: cogames tutorial train -m
|
|
1073
|
+
"[dim]Train with: cogames tutorial train -m cogsguard_machina_1.basic -p class="
|
|
556
1074
|
f"{dest_path.stem}.{policy_class}[/dim]"
|
|
557
1075
|
)
|
|
558
1076
|
else:
|
|
559
1077
|
console.print(
|
|
560
|
-
"[dim]Play with: cogames play -m
|
|
1078
|
+
"[dim]Play with: cogames play -m cogsguard_machina_1.basic -p class="
|
|
561
1079
|
f"{dest_path.stem}.{policy_class}[/dim]"
|
|
562
1080
|
)
|
|
563
1081
|
|
|
@@ -569,57 +1087,179 @@ def make_policy(
|
|
|
569
1087
|
app.command(name="make-policy", hidden=True)(make_policy)
|
|
570
1088
|
|
|
571
1089
|
|
|
572
|
-
@tutorial_app.command(
|
|
1090
|
+
@tutorial_app.command(
|
|
1091
|
+
name="train",
|
|
1092
|
+
help="""Train a policy on one or more missions.
|
|
1093
|
+
|
|
1094
|
+
By default, our 'lstm' policy architecture is used. You can select a different architecture
|
|
1095
|
+
(like 'stateless' or 'baseline'), or define your own implementing the MultiAgentPolicy
|
|
1096
|
+
interface with a trainable network() method (see mettagrid/policy/policy.py).
|
|
1097
|
+
|
|
1098
|
+
Continue training from a checkpoint using URI format, or load weights into an explicit class
|
|
1099
|
+
with class=...,data=... syntax.
|
|
1100
|
+
|
|
1101
|
+
Supply repeated -m flags to create a training curriculum that rotates through missions.
|
|
1102
|
+
Use wildcards (*) in mission names to match multiple missions at once.""",
|
|
1103
|
+
rich_help_panel="Tutorial",
|
|
1104
|
+
epilog="""[dim]Examples:[/dim]
|
|
1105
|
+
|
|
1106
|
+
[cyan]cogames tutorial train -m cogsguard_machina_1.basic[/cyan] Basic training
|
|
1107
|
+
|
|
1108
|
+
[cyan]cogames tutorial train -m cogsguard_machina_1.basic -p class=baseline[/cyan]
|
|
1109
|
+
Train baseline policy
|
|
1110
|
+
|
|
1111
|
+
[cyan]cogames tutorial train -p ./train_dir/my_run:v5[/cyan] Continue from checkpoint
|
|
1112
|
+
|
|
1113
|
+
[cyan]cogames tutorial train -p class=lstm,data=./weights.safetensors[/cyan] Load weights into class
|
|
1114
|
+
|
|
1115
|
+
[cyan]cogames tutorial train -m mission_1 -m mission_2[/cyan] Curriculum (rotates)
|
|
1116
|
+
|
|
1117
|
+
[dim]Wildcard patterns:[/dim]
|
|
1118
|
+
|
|
1119
|
+
[cyan]cogames tutorial train -m 'machina_2_bigger:*'[/cyan] All missions on machina_2_bigger
|
|
1120
|
+
|
|
1121
|
+
[cyan]cogames tutorial train -m '*:shaped'[/cyan] All "shaped" missions
|
|
1122
|
+
|
|
1123
|
+
[cyan]cogames tutorial train -m 'machina*:shaped'[/cyan] All "shaped" on machina maps""",
|
|
1124
|
+
add_help_option=False,
|
|
1125
|
+
)
|
|
573
1126
|
def train_cmd(
|
|
574
1127
|
ctx: typer.Context,
|
|
575
|
-
|
|
576
|
-
|
|
1128
|
+
# --- Mission Setup ---
|
|
1129
|
+
missions: Optional[list[str]] = typer.Option( # noqa: B008
|
|
1130
|
+
None,
|
|
1131
|
+
"--mission",
|
|
1132
|
+
"-m",
|
|
1133
|
+
metavar="MISSION",
|
|
1134
|
+
help="Missions to train on (wildcards supported, repeatable for curriculum)",
|
|
1135
|
+
rich_help_panel="Mission Setup",
|
|
1136
|
+
),
|
|
1137
|
+
cogs: Optional[int] = typer.Option(
|
|
1138
|
+
None,
|
|
1139
|
+
"--cogs",
|
|
1140
|
+
"-c",
|
|
1141
|
+
metavar="N",
|
|
1142
|
+
help="Number of cogs (agents)",
|
|
1143
|
+
show_default="from mission",
|
|
1144
|
+
rich_help_panel="Mission Setup",
|
|
1145
|
+
),
|
|
577
1146
|
variant: Optional[list[str]] = typer.Option( # noqa: B008
|
|
578
1147
|
None,
|
|
579
1148
|
"--variant",
|
|
580
1149
|
"-v",
|
|
581
|
-
|
|
1150
|
+
metavar="VARIANT",
|
|
1151
|
+
help="Mission variant (repeatable)",
|
|
1152
|
+
rich_help_panel="Mission Setup",
|
|
582
1153
|
),
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
"
|
|
586
|
-
"--
|
|
587
|
-
|
|
1154
|
+
# --- Policy ---
|
|
1155
|
+
policy: str = typer.Option(
|
|
1156
|
+
"class=lstm",
|
|
1157
|
+
"--policy",
|
|
1158
|
+
"-p",
|
|
1159
|
+
metavar="POLICY",
|
|
1160
|
+
help=f"Policy to train ({policy_arg_example})",
|
|
1161
|
+
rich_help_panel="Policy",
|
|
588
1162
|
),
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
"--
|
|
593
|
-
|
|
1163
|
+
# --- Training ---
|
|
1164
|
+
steps: int = typer.Option(
|
|
1165
|
+
10_000_000_000,
|
|
1166
|
+
"--steps",
|
|
1167
|
+
metavar="N",
|
|
1168
|
+
help="Number of training steps",
|
|
1169
|
+
min=1,
|
|
1170
|
+
rich_help_panel="Training",
|
|
594
1171
|
),
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
"
|
|
599
|
-
help="
|
|
600
|
-
min=
|
|
1172
|
+
batch_size: int = typer.Option(
|
|
1173
|
+
4096,
|
|
1174
|
+
"--batch-size",
|
|
1175
|
+
metavar="N",
|
|
1176
|
+
help="Batch size for training",
|
|
1177
|
+
min=1,
|
|
1178
|
+
rich_help_panel="Training",
|
|
1179
|
+
),
|
|
1180
|
+
minibatch_size: int = typer.Option(
|
|
1181
|
+
4096,
|
|
1182
|
+
"--minibatch-size",
|
|
1183
|
+
metavar="N",
|
|
1184
|
+
help="Minibatch size for training",
|
|
1185
|
+
min=1,
|
|
1186
|
+
rich_help_panel="Training",
|
|
1187
|
+
),
|
|
1188
|
+
# --- Hardware ---
|
|
1189
|
+
device: str = typer.Option(
|
|
1190
|
+
"cpu",
|
|
1191
|
+
"--device",
|
|
1192
|
+
metavar="DEVICE",
|
|
1193
|
+
help="Device to train on (auto, cpu, cuda, mps)",
|
|
1194
|
+
rich_help_panel="Hardware",
|
|
601
1195
|
),
|
|
602
|
-
batch_size: int = typer.Option(4096, "--batch-size", help="Batch size for training", min=1),
|
|
603
|
-
minibatch_size: int = typer.Option(4096, "--minibatch-size", help="Minibatch size for training", min=1),
|
|
604
1196
|
num_workers: Optional[int] = typer.Option(
|
|
605
1197
|
None,
|
|
606
1198
|
"--num-workers",
|
|
607
|
-
|
|
1199
|
+
metavar="N",
|
|
1200
|
+
help="Number of worker processes",
|
|
1201
|
+
show_default="CPU cores",
|
|
608
1202
|
min=1,
|
|
1203
|
+
rich_help_panel="Hardware",
|
|
609
1204
|
),
|
|
610
1205
|
parallel_envs: Optional[int] = typer.Option(
|
|
611
1206
|
None,
|
|
612
1207
|
"--parallel-envs",
|
|
1208
|
+
metavar="N",
|
|
613
1209
|
help="Number of parallel environments",
|
|
614
1210
|
min=1,
|
|
1211
|
+
rich_help_panel="Hardware",
|
|
615
1212
|
),
|
|
616
1213
|
vector_batch_size: Optional[int] = typer.Option(
|
|
617
1214
|
None,
|
|
618
1215
|
"--vector-batch-size",
|
|
619
|
-
|
|
1216
|
+
metavar="N",
|
|
1217
|
+
help="Vectorized environment batch size",
|
|
620
1218
|
min=1,
|
|
1219
|
+
rich_help_panel="Hardware",
|
|
1220
|
+
),
|
|
1221
|
+
# --- Reproducibility ---
|
|
1222
|
+
seed: int = typer.Option(
|
|
1223
|
+
42,
|
|
1224
|
+
"--seed",
|
|
1225
|
+
metavar="N",
|
|
1226
|
+
help="Seed for training RNG",
|
|
1227
|
+
min=0,
|
|
1228
|
+
rich_help_panel="Reproducibility",
|
|
1229
|
+
),
|
|
1230
|
+
map_seed: Optional[int] = typer.Option(
|
|
1231
|
+
None,
|
|
1232
|
+
"--map-seed",
|
|
1233
|
+
metavar="N",
|
|
1234
|
+
help="MapGen seed for procedural map layout",
|
|
1235
|
+
show_default="same as --seed",
|
|
1236
|
+
min=0,
|
|
1237
|
+
rich_help_panel="Reproducibility",
|
|
1238
|
+
),
|
|
1239
|
+
# --- Output ---
|
|
1240
|
+
checkpoints_path: str = typer.Option(
|
|
1241
|
+
"./train_dir",
|
|
1242
|
+
"--checkpoints",
|
|
1243
|
+
metavar="DIR",
|
|
1244
|
+
help="Path to save training checkpoints",
|
|
1245
|
+
rich_help_panel="Output",
|
|
1246
|
+
),
|
|
1247
|
+
log_outputs: bool = typer.Option(
|
|
1248
|
+
False,
|
|
1249
|
+
"--log-outputs",
|
|
1250
|
+
help="Log training outputs",
|
|
1251
|
+
rich_help_panel="Output",
|
|
1252
|
+
),
|
|
1253
|
+
# --- Help ---
|
|
1254
|
+
_help: bool = typer.Option(
|
|
1255
|
+
False,
|
|
1256
|
+
"--help",
|
|
1257
|
+
"-h",
|
|
1258
|
+
help="Show this message and exit",
|
|
1259
|
+
is_eager=True,
|
|
1260
|
+
callback=_help_callback,
|
|
1261
|
+
rich_help_panel="Other",
|
|
621
1262
|
),
|
|
622
|
-
log_outputs: bool = typer.Option(False, "--log-outputs", help="Log training outputs"),
|
|
623
1263
|
) -> None:
|
|
624
1264
|
selected_missions = get_mission_names_and_configs(ctx, missions, variants_arg=variant, cogs=cogs)
|
|
625
1265
|
if len(selected_missions) == 1:
|
|
@@ -637,29 +1277,6 @@ def train_cmd(
|
|
|
637
1277
|
policy_spec = get_policy_spec(ctx, policy)
|
|
638
1278
|
torch_device = resolve_training_device(console, device)
|
|
639
1279
|
|
|
640
|
-
# Optional MapGen seed override for deterministic procedural maps during training.
|
|
641
|
-
# We keep this opt-in (via --map-seed) to avoid reducing map diversity by default.
|
|
642
|
-
|
|
643
|
-
if map_seed is not None:
|
|
644
|
-
|
|
645
|
-
def _maybe_seed(cfg: Any) -> None:
|
|
646
|
-
mb = getattr(cfg.game, "map_builder", None)
|
|
647
|
-
if isinstance(mb, MapGen.Config) and mb.seed is None:
|
|
648
|
-
mb.seed = map_seed
|
|
649
|
-
|
|
650
|
-
if env_cfg is not None:
|
|
651
|
-
_maybe_seed(env_cfg)
|
|
652
|
-
|
|
653
|
-
if supplier is not None:
|
|
654
|
-
base_supplier = supplier
|
|
655
|
-
|
|
656
|
-
def _seeded_supplier() -> Any:
|
|
657
|
-
cfg = base_supplier()
|
|
658
|
-
_maybe_seed(cfg)
|
|
659
|
-
return cfg
|
|
660
|
-
|
|
661
|
-
supplier = _seeded_supplier
|
|
662
|
-
|
|
663
1280
|
try:
|
|
664
1281
|
train_module.train(
|
|
665
1282
|
env_cfg=env_cfg,
|
|
@@ -669,6 +1286,7 @@ def train_cmd(
|
|
|
669
1286
|
num_steps=steps,
|
|
670
1287
|
checkpoints_path=Path(checkpoints_path),
|
|
671
1288
|
seed=seed,
|
|
1289
|
+
map_seed=map_seed,
|
|
672
1290
|
batch_size=batch_size,
|
|
673
1291
|
minibatch_size=minibatch_size,
|
|
674
1292
|
vector_num_workers=num_workers,
|
|
@@ -691,64 +1309,154 @@ app.command(name="train", hidden=True)(train_cmd)
|
|
|
691
1309
|
|
|
692
1310
|
@app.command(
|
|
693
1311
|
name="run",
|
|
694
|
-
help="Evaluate one or more policies on
|
|
1312
|
+
help="""Evaluate one or more policies on missions.
|
|
1313
|
+
|
|
1314
|
+
With multiple policies (e.g., 2 policies, 4 agents), each policy always controls 2 agents,
|
|
1315
|
+
but which agents swap between policies each episode.
|
|
1316
|
+
|
|
1317
|
+
With one policy, this command is equivalent to `cogames scrimmage`.
|
|
1318
|
+
""",
|
|
1319
|
+
rich_help_panel="Evaluate",
|
|
1320
|
+
epilog="""[dim]Examples:[/dim]
|
|
1321
|
+
|
|
1322
|
+
[cyan]cogames run -m cogsguard_machina_1.basic -p lstm[/cyan] Evaluate single policy
|
|
1323
|
+
|
|
1324
|
+
[cyan]cogames run -m cogsguard_machina_1 -p ./train_dir/my_run:v5[/cyan] Evaluate a checkpoint bundle
|
|
1325
|
+
|
|
1326
|
+
[cyan]cogames run -S integrated_evals -p ./train_dir/my_run:v5[/cyan] Evaluate on mission set
|
|
1327
|
+
|
|
1328
|
+
[cyan]cogames run -m 'arena.*' -p lstm -p random -e 20[/cyan] Evaluate multiple policies together
|
|
1329
|
+
|
|
1330
|
+
[cyan]cogames run -m cogsguard_machina_1 -p ./train_dir/my_run:v5,proportion=3 -p class=random,proportion=5[/cyan]
|
|
1331
|
+
Evaluate policies in 3:5 mix""",
|
|
1332
|
+
add_help_option=False,
|
|
1333
|
+
)
|
|
1334
|
+
@app.command(
|
|
1335
|
+
name="scrimmage",
|
|
1336
|
+
help="""Evaluate a single policy controlling all agents.
|
|
1337
|
+
|
|
1338
|
+
This command is equivalent to running `cogames run` with a single policy.
|
|
1339
|
+
""",
|
|
1340
|
+
rich_help_panel="Evaluate",
|
|
1341
|
+
epilog="""[dim]Examples:[/dim]
|
|
1342
|
+
|
|
1343
|
+
[cyan]cogames scrimmage -m arena.battle -p lstm[/cyan] Single policy eval""",
|
|
1344
|
+
add_help_option=False,
|
|
695
1345
|
)
|
|
696
1346
|
@app.command("eval", hidden=True)
|
|
697
1347
|
@app.command("evaluate", hidden=True)
|
|
698
1348
|
def run_cmd(
|
|
699
1349
|
ctx: typer.Context,
|
|
1350
|
+
# --- Mission ---
|
|
700
1351
|
missions: Optional[list[str]] = typer.Option( # noqa: B008
|
|
701
1352
|
None,
|
|
702
1353
|
"--mission",
|
|
703
1354
|
"-m",
|
|
704
|
-
|
|
1355
|
+
metavar="MISSION",
|
|
1356
|
+
help="Missions to evaluate (supports wildcards)",
|
|
1357
|
+
rich_help_panel="Mission",
|
|
705
1358
|
),
|
|
706
1359
|
mission_set: Optional[str] = typer.Option(
|
|
707
1360
|
None,
|
|
708
1361
|
"--mission-set",
|
|
709
1362
|
"-S",
|
|
710
|
-
|
|
1363
|
+
metavar="SET",
|
|
1364
|
+
help="Predefined set: integrated_evals, spanning_evals, diagnostic_evals, all",
|
|
1365
|
+
rich_help_panel="Mission",
|
|
1366
|
+
),
|
|
1367
|
+
cogs: Optional[int] = typer.Option(
|
|
1368
|
+
None,
|
|
1369
|
+
"--cogs",
|
|
1370
|
+
"-c",
|
|
1371
|
+
metavar="N",
|
|
1372
|
+
help="Number of cogs (agents)",
|
|
1373
|
+
rich_help_panel="Mission",
|
|
711
1374
|
),
|
|
712
|
-
cogs: Optional[int] = typer.Option(None, "--cogs", "-c", help="Number of cogs (agents)"),
|
|
713
1375
|
variant: Optional[list[str]] = typer.Option( # noqa: B008
|
|
714
1376
|
None,
|
|
715
1377
|
"--variant",
|
|
716
1378
|
"-v",
|
|
717
|
-
|
|
1379
|
+
metavar="VARIANT",
|
|
1380
|
+
help="Mission variant (repeatable)",
|
|
1381
|
+
rich_help_panel="Mission",
|
|
718
1382
|
),
|
|
1383
|
+
# --- Policy ---
|
|
719
1384
|
policies: Optional[list[str]] = typer.Option( # noqa: B008
|
|
720
1385
|
None,
|
|
721
1386
|
"--policy",
|
|
722
1387
|
"-p",
|
|
1388
|
+
metavar="POLICY",
|
|
723
1389
|
help=f"Policies to evaluate: ({policy_arg_w_proportion_example}...)",
|
|
1390
|
+
rich_help_panel="Policy",
|
|
724
1391
|
),
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
"--
|
|
729
|
-
|
|
1392
|
+
# --- Simulation ---
|
|
1393
|
+
episodes: int = typer.Option(
|
|
1394
|
+
10,
|
|
1395
|
+
"--episodes",
|
|
1396
|
+
"-e",
|
|
1397
|
+
metavar="N",
|
|
1398
|
+
help="Number of evaluation episodes",
|
|
1399
|
+
min=1,
|
|
1400
|
+
rich_help_panel="Simulation",
|
|
1401
|
+
),
|
|
1402
|
+
steps: Optional[int] = typer.Option(
|
|
1403
|
+
1000,
|
|
1404
|
+
"--steps",
|
|
1405
|
+
"-s",
|
|
1406
|
+
metavar="N",
|
|
1407
|
+
help="Max steps per episode",
|
|
730
1408
|
min=1,
|
|
1409
|
+
rich_help_panel="Simulation",
|
|
1410
|
+
),
|
|
1411
|
+
seed: int = typer.Option(
|
|
1412
|
+
42,
|
|
1413
|
+
"--seed",
|
|
1414
|
+
metavar="N",
|
|
1415
|
+
help="Seed for evaluation RNG",
|
|
1416
|
+
min=0,
|
|
1417
|
+
rich_help_panel="Simulation",
|
|
731
1418
|
),
|
|
732
|
-
steps: Optional[int] = typer.Option(1000, "--steps", "-s", help="Max steps per episode", min=1),
|
|
733
|
-
seed: int = typer.Option(42, "--seed", help="Base random seed for evaluation", min=0),
|
|
734
1419
|
map_seed: Optional[int] = typer.Option(
|
|
735
1420
|
None,
|
|
736
1421
|
"--map-seed",
|
|
737
|
-
|
|
1422
|
+
metavar="N",
|
|
1423
|
+
help="MapGen seed for procedural maps",
|
|
738
1424
|
min=0,
|
|
1425
|
+
show_default="same as --seed",
|
|
1426
|
+
rich_help_panel="Simulation",
|
|
739
1427
|
),
|
|
1428
|
+
action_timeout_ms: int = typer.Option(
|
|
1429
|
+
250,
|
|
1430
|
+
"--action-timeout-ms",
|
|
1431
|
+
metavar="MS",
|
|
1432
|
+
help="Max ms per action before noop",
|
|
1433
|
+
min=1,
|
|
1434
|
+
rich_help_panel="Simulation",
|
|
1435
|
+
),
|
|
1436
|
+
# --- Output ---
|
|
740
1437
|
format_: Optional[Literal["yaml", "json"]] = typer.Option(
|
|
741
1438
|
None,
|
|
742
1439
|
"--format",
|
|
743
|
-
|
|
1440
|
+
metavar="FMT",
|
|
1441
|
+
help="Output format: yaml or json",
|
|
1442
|
+
rich_help_panel="Output",
|
|
744
1443
|
),
|
|
745
1444
|
save_replay_dir: Optional[Path] = typer.Option( # noqa: B008
|
|
746
1445
|
None,
|
|
747
1446
|
"--save-replay-dir",
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1447
|
+
metavar="DIR",
|
|
1448
|
+
help="Directory to save replays",
|
|
1449
|
+
rich_help_panel="Output",
|
|
1450
|
+
),
|
|
1451
|
+
# --- Help ---
|
|
1452
|
+
_help: bool = typer.Option(
|
|
1453
|
+
False,
|
|
1454
|
+
"--help",
|
|
1455
|
+
"-h",
|
|
1456
|
+
help="Show this message and exit",
|
|
1457
|
+
is_eager=True,
|
|
1458
|
+
callback=_help_callback,
|
|
1459
|
+
rich_help_panel="Other",
|
|
752
1460
|
),
|
|
753
1461
|
) -> None:
|
|
754
1462
|
# Handle mission set expansion
|
|
@@ -773,19 +1481,23 @@ def run_cmd(
|
|
|
773
1481
|
|
|
774
1482
|
selected_missions = get_mission_names_and_configs(ctx, missions, variants_arg=variant, cogs=cogs, steps=steps)
|
|
775
1483
|
|
|
776
|
-
#
|
|
777
|
-
|
|
778
|
-
from mettagrid.mapgen.mapgen import MapGen
|
|
779
|
-
|
|
780
|
-
effective_map_seed: Optional[int] = map_seed if map_seed is not None else seed
|
|
781
|
-
if effective_map_seed is not None:
|
|
1484
|
+
# Optional MapGen seed override for procedural maps.
|
|
1485
|
+
if map_seed is not None:
|
|
782
1486
|
for _, env_cfg in selected_missions:
|
|
783
1487
|
map_builder = getattr(env_cfg.game, "map_builder", None)
|
|
784
1488
|
if isinstance(map_builder, MapGen.Config):
|
|
785
|
-
map_builder.seed =
|
|
1489
|
+
map_builder.seed = map_seed
|
|
786
1490
|
|
|
787
1491
|
policy_specs = get_policy_specs_with_proportions(ctx, policies)
|
|
788
1492
|
|
|
1493
|
+
if ctx.info_name == "scrimmage":
|
|
1494
|
+
if len(policy_specs) != 1:
|
|
1495
|
+
console.print("[red]Error: scrimmage accepts exactly one --policy / -p value.[/red]")
|
|
1496
|
+
raise typer.Exit(1)
|
|
1497
|
+
if policy_specs[0].proportion != 1.0:
|
|
1498
|
+
console.print("[red]Error: scrimmage does not support policy proportions.[/red]")
|
|
1499
|
+
raise typer.Exit(1)
|
|
1500
|
+
|
|
789
1501
|
console.print(
|
|
790
1502
|
f"[cyan]Preparing evaluation for {len(policy_specs)} policies across {len(selected_missions)} mission(s)[/cyan]"
|
|
791
1503
|
)
|
|
@@ -803,7 +1515,170 @@ def run_cmd(
|
|
|
803
1515
|
)
|
|
804
1516
|
|
|
805
1517
|
|
|
806
|
-
@app.command(
|
|
1518
|
+
@app.command(
|
|
1519
|
+
name="pickup",
|
|
1520
|
+
help="Evaluate a policy against a pool of other policies and compute VOR",
|
|
1521
|
+
rich_help_panel="Evaluate",
|
|
1522
|
+
epilog="""[dim]Examples:[/dim]
|
|
1523
|
+
|
|
1524
|
+
[cyan]cogames pickup -p greedy --pool random[/cyan] Test greedy against pool of random""",
|
|
1525
|
+
add_help_option=False,
|
|
1526
|
+
)
|
|
1527
|
+
def pickup_cmd(
|
|
1528
|
+
ctx: typer.Context,
|
|
1529
|
+
# --- Mission ---
|
|
1530
|
+
mission: str = typer.Option(
|
|
1531
|
+
"cogsguard_machina_1.basic",
|
|
1532
|
+
"--mission",
|
|
1533
|
+
"-m",
|
|
1534
|
+
metavar="MISSION",
|
|
1535
|
+
help="Mission to evaluate on",
|
|
1536
|
+
rich_help_panel="Mission",
|
|
1537
|
+
),
|
|
1538
|
+
cogs: int = typer.Option(
|
|
1539
|
+
4,
|
|
1540
|
+
"--cogs",
|
|
1541
|
+
"-c",
|
|
1542
|
+
metavar="N",
|
|
1543
|
+
help="Number of cogs (agents)",
|
|
1544
|
+
min=1,
|
|
1545
|
+
rich_help_panel="Mission",
|
|
1546
|
+
),
|
|
1547
|
+
variant: Optional[list[str]] = typer.Option( # noqa: B008
|
|
1548
|
+
None,
|
|
1549
|
+
"--variant",
|
|
1550
|
+
"-v",
|
|
1551
|
+
metavar="VARIANT",
|
|
1552
|
+
help="Mission variant (repeatable)",
|
|
1553
|
+
rich_help_panel="Mission",
|
|
1554
|
+
),
|
|
1555
|
+
# --- Policy ---
|
|
1556
|
+
policy: Optional[str] = typer.Option(
|
|
1557
|
+
None,
|
|
1558
|
+
"--policy",
|
|
1559
|
+
"-p",
|
|
1560
|
+
metavar="POLICY",
|
|
1561
|
+
help="Candidate policy to evaluate",
|
|
1562
|
+
rich_help_panel="Policy",
|
|
1563
|
+
),
|
|
1564
|
+
pool: Optional[list[str]] = typer.Option( # noqa: B008
|
|
1565
|
+
None,
|
|
1566
|
+
"--pool",
|
|
1567
|
+
metavar="POLICY",
|
|
1568
|
+
help="Pool policy (repeatable)",
|
|
1569
|
+
rich_help_panel="Policy",
|
|
1570
|
+
),
|
|
1571
|
+
# --- Simulation ---
|
|
1572
|
+
episodes: int = typer.Option(
|
|
1573
|
+
1,
|
|
1574
|
+
"--episodes",
|
|
1575
|
+
"-e",
|
|
1576
|
+
metavar="N",
|
|
1577
|
+
help="Episodes per scenario",
|
|
1578
|
+
min=1,
|
|
1579
|
+
rich_help_panel="Simulation",
|
|
1580
|
+
),
|
|
1581
|
+
steps: Optional[int] = typer.Option(
|
|
1582
|
+
1000,
|
|
1583
|
+
"--steps",
|
|
1584
|
+
"-s",
|
|
1585
|
+
metavar="N",
|
|
1586
|
+
help="Max steps per episode",
|
|
1587
|
+
min=1,
|
|
1588
|
+
rich_help_panel="Simulation",
|
|
1589
|
+
),
|
|
1590
|
+
seed: int = typer.Option(
|
|
1591
|
+
50,
|
|
1592
|
+
"--seed",
|
|
1593
|
+
metavar="N",
|
|
1594
|
+
help="Base random seed",
|
|
1595
|
+
min=0,
|
|
1596
|
+
rich_help_panel="Simulation",
|
|
1597
|
+
),
|
|
1598
|
+
map_seed: Optional[int] = typer.Option(
|
|
1599
|
+
None,
|
|
1600
|
+
"--map-seed",
|
|
1601
|
+
metavar="N",
|
|
1602
|
+
help="MapGen seed for procedural maps",
|
|
1603
|
+
min=0,
|
|
1604
|
+
show_default="same as --seed",
|
|
1605
|
+
rich_help_panel="Simulation",
|
|
1606
|
+
),
|
|
1607
|
+
action_timeout_ms: int = typer.Option(
|
|
1608
|
+
250,
|
|
1609
|
+
"--action-timeout-ms",
|
|
1610
|
+
metavar="MS",
|
|
1611
|
+
help="Max ms per action before noop",
|
|
1612
|
+
min=1,
|
|
1613
|
+
rich_help_panel="Simulation",
|
|
1614
|
+
),
|
|
1615
|
+
# --- Output ---
|
|
1616
|
+
save_replay_dir: Optional[Path] = typer.Option( # noqa: B008
|
|
1617
|
+
None,
|
|
1618
|
+
"--save-replay-dir",
|
|
1619
|
+
metavar="DIR",
|
|
1620
|
+
help="Directory to save replays",
|
|
1621
|
+
rich_help_panel="Output",
|
|
1622
|
+
),
|
|
1623
|
+
# --- Help ---
|
|
1624
|
+
_help: bool = typer.Option(
|
|
1625
|
+
False,
|
|
1626
|
+
"--help",
|
|
1627
|
+
"-h",
|
|
1628
|
+
help="Show this message and exit",
|
|
1629
|
+
is_eager=True,
|
|
1630
|
+
callback=_help_callback,
|
|
1631
|
+
rich_help_panel="Other",
|
|
1632
|
+
),
|
|
1633
|
+
) -> None:
|
|
1634
|
+
import httpx
|
|
1635
|
+
|
|
1636
|
+
if policy is None:
|
|
1637
|
+
console.print(ctx.get_help())
|
|
1638
|
+
console.print("[yellow]Missing: --policy / -p[/yellow]\n")
|
|
1639
|
+
raise typer.Exit(1)
|
|
1640
|
+
|
|
1641
|
+
if not pool:
|
|
1642
|
+
console.print(ctx.get_help())
|
|
1643
|
+
console.print("[yellow]Supply at least one: --pool[/yellow]\n")
|
|
1644
|
+
raise typer.Exit(1)
|
|
1645
|
+
|
|
1646
|
+
# Resolve mission
|
|
1647
|
+
resolved_mission, env_cfg, _ = get_mission_name_and_config(ctx, mission, variants_arg=variant, cogs=cogs)
|
|
1648
|
+
if steps is not None:
|
|
1649
|
+
env_cfg.game.max_steps = steps
|
|
1650
|
+
|
|
1651
|
+
candidate_label = policy
|
|
1652
|
+
pool_labels = pool
|
|
1653
|
+
candidate_spec = get_policy_spec(ctx, policy)
|
|
1654
|
+
try:
|
|
1655
|
+
pool_specs = [parse_policy_spec(spec).to_policy_spec() for spec in pool]
|
|
1656
|
+
except (ValueError, ModuleNotFoundError, httpx.HTTPError) as exc:
|
|
1657
|
+
translated = _translate_error(exc)
|
|
1658
|
+
console.print(f"[yellow]Error parsing pool policy: {translated}[/yellow]\n")
|
|
1659
|
+
raise typer.Exit(1) from exc
|
|
1660
|
+
|
|
1661
|
+
pickup_module.pickup(
|
|
1662
|
+
console,
|
|
1663
|
+
candidate_spec,
|
|
1664
|
+
pool_specs,
|
|
1665
|
+
env_cfg=env_cfg,
|
|
1666
|
+
mission_name=resolved_mission,
|
|
1667
|
+
episodes=episodes,
|
|
1668
|
+
seed=seed,
|
|
1669
|
+
map_seed=map_seed,
|
|
1670
|
+
action_timeout_ms=action_timeout_ms,
|
|
1671
|
+
save_replay_dir=save_replay_dir,
|
|
1672
|
+
candidate_label=candidate_label,
|
|
1673
|
+
pool_labels=pool_labels,
|
|
1674
|
+
)
|
|
1675
|
+
|
|
1676
|
+
|
|
1677
|
+
@app.command(
|
|
1678
|
+
name="version",
|
|
1679
|
+
help="Show version information for cogames and dependencies",
|
|
1680
|
+
rich_help_panel="Info",
|
|
1681
|
+
)
|
|
807
1682
|
def version_cmd() -> None:
|
|
808
1683
|
def public_version(dist_name: str) -> str:
|
|
809
1684
|
return str(Version(importlib.metadata.version(dist_name)).public)
|
|
@@ -818,7 +1693,18 @@ def version_cmd() -> None:
|
|
|
818
1693
|
console.print(table)
|
|
819
1694
|
|
|
820
1695
|
|
|
821
|
-
@app.command(
|
|
1696
|
+
@app.command(
|
|
1697
|
+
name="policies",
|
|
1698
|
+
help="Show available policy shorthand names",
|
|
1699
|
+
rich_help_panel="Policies",
|
|
1700
|
+
epilog="""[dim]Usage:[/dim]
|
|
1701
|
+
|
|
1702
|
+
Use these shorthand names with [cyan]--policy[/cyan] or [cyan]-p[/cyan]:
|
|
1703
|
+
|
|
1704
|
+
[cyan]cogames play -m arena -p class=random[/cyan] Use random policy
|
|
1705
|
+
|
|
1706
|
+
[cyan]cogames play -m arena -p class=baseline[/cyan] Use baseline policy""",
|
|
1707
|
+
)
|
|
822
1708
|
def policies_cmd() -> None:
|
|
823
1709
|
policy_registry = get_policy_registry()
|
|
824
1710
|
table = Table(show_header=False, box=None, show_lines=False, pad_edge=False)
|
|
@@ -832,26 +1718,48 @@ def policies_cmd() -> None:
|
|
|
832
1718
|
console.print(table)
|
|
833
1719
|
|
|
834
1720
|
|
|
835
|
-
@app.command(
|
|
1721
|
+
@app.command(
|
|
1722
|
+
name="login",
|
|
1723
|
+
help="Authenticate with CoGames server",
|
|
1724
|
+
rich_help_panel="Tournament",
|
|
1725
|
+
epilog="""[dim]Examples:[/dim]
|
|
1726
|
+
|
|
1727
|
+
[cyan]cogames login[/cyan] Authenticate with default server
|
|
1728
|
+
|
|
1729
|
+
[cyan]cogames login --force[/cyan] Re-authenticate even if already logged in""",
|
|
1730
|
+
add_help_option=False,
|
|
1731
|
+
)
|
|
836
1732
|
def login_cmd(
|
|
837
1733
|
server: str = typer.Option(
|
|
838
1734
|
DEFAULT_COGAMES_SERVER,
|
|
839
|
-
"--server",
|
|
840
|
-
"
|
|
841
|
-
help="
|
|
1735
|
+
"--login-server",
|
|
1736
|
+
metavar="URL",
|
|
1737
|
+
help="Authentication server URL",
|
|
1738
|
+
rich_help_panel="Server",
|
|
842
1739
|
),
|
|
843
1740
|
force: bool = typer.Option(
|
|
844
1741
|
False,
|
|
845
1742
|
"--force",
|
|
846
1743
|
"-f",
|
|
847
|
-
help="
|
|
1744
|
+
help="Re-authenticate even if already logged in",
|
|
1745
|
+
rich_help_panel="Options",
|
|
848
1746
|
),
|
|
849
1747
|
timeout: int = typer.Option(
|
|
850
1748
|
300,
|
|
851
1749
|
"--timeout",
|
|
852
1750
|
"-t",
|
|
1751
|
+
metavar="SECS",
|
|
853
1752
|
help="Authentication timeout in seconds",
|
|
854
|
-
|
|
1753
|
+
rich_help_panel="Options",
|
|
1754
|
+
),
|
|
1755
|
+
_help: bool = typer.Option(
|
|
1756
|
+
False,
|
|
1757
|
+
"--help",
|
|
1758
|
+
"-h",
|
|
1759
|
+
help="Show this message and exit",
|
|
1760
|
+
is_eager=True,
|
|
1761
|
+
callback=_help_callback,
|
|
1762
|
+
rich_help_panel="Other",
|
|
855
1763
|
),
|
|
856
1764
|
) -> None:
|
|
857
1765
|
from urllib.parse import urlparse
|
|
@@ -877,29 +1785,211 @@ def login_cmd(
|
|
|
877
1785
|
raise typer.Exit(1)
|
|
878
1786
|
|
|
879
1787
|
|
|
880
|
-
app.command(
|
|
1788
|
+
app.command(
|
|
1789
|
+
name="submissions",
|
|
1790
|
+
help="Show your uploads and tournament submissions",
|
|
1791
|
+
rich_help_panel="Tournament",
|
|
1792
|
+
epilog="""[dim]Examples:[/dim]
|
|
1793
|
+
|
|
1794
|
+
[cyan]cogames submissions[/cyan] All your uploads
|
|
1795
|
+
|
|
1796
|
+
[cyan]cogames submissions --season beta-cogsguard[/cyan] Submissions in a season
|
|
881
1797
|
|
|
882
|
-
|
|
1798
|
+
[cyan]cogames submissions -p my-policy[/cyan] Info on a specific policy""",
|
|
1799
|
+
add_help_option=False,
|
|
1800
|
+
)(submissions_cmd)
|
|
1801
|
+
|
|
1802
|
+
app.command(
|
|
1803
|
+
name="seasons",
|
|
1804
|
+
help="List currently running tournament seasons",
|
|
1805
|
+
rich_help_panel="Tournament",
|
|
1806
|
+
add_help_option=False,
|
|
1807
|
+
)(seasons_cmd)
|
|
883
1808
|
|
|
884
1809
|
app.command(
|
|
885
1810
|
name="leaderboard",
|
|
886
1811
|
help="Show tournament leaderboard for a season",
|
|
1812
|
+
rich_help_panel="Tournament",
|
|
1813
|
+
epilog="""[dim]Examples:[/dim]
|
|
1814
|
+
|
|
1815
|
+
[cyan]cogames leaderboard --season beta-cogsguard[/cyan] View rankings""",
|
|
1816
|
+
add_help_option=False,
|
|
887
1817
|
)(leaderboard_cmd)
|
|
888
1818
|
|
|
889
1819
|
|
|
890
|
-
@app.command(
|
|
1820
|
+
@app.command(
|
|
1821
|
+
name="diagnose",
|
|
1822
|
+
help="Run diagnostic evals for a policy checkpoint",
|
|
1823
|
+
rich_help_panel="Evaluate",
|
|
1824
|
+
epilog="""[dim]Examples:[/dim]
|
|
1825
|
+
|
|
1826
|
+
[cyan]cogames diagnose ./train_dir/my_run[/cyan] Default diagnostics
|
|
1827
|
+
|
|
1828
|
+
[cyan]cogames diagnose lstm -S tournament[/cyan] Tournament suite
|
|
1829
|
+
|
|
1830
|
+
[cyan]cogames diagnose lstm -c 4 -c 8 -e 5[/cyan] Custom cog counts""",
|
|
1831
|
+
add_help_option=False,
|
|
1832
|
+
)
|
|
1833
|
+
def diagnose_cmd(
|
|
1834
|
+
policy: str = typer.Argument(
|
|
1835
|
+
...,
|
|
1836
|
+
metavar="POLICY",
|
|
1837
|
+
help=f"Policy specification: {policy_arg_example}",
|
|
1838
|
+
),
|
|
1839
|
+
# --- Evaluation ---
|
|
1840
|
+
mission_set: Literal[
|
|
1841
|
+
"diagnostic_evals",
|
|
1842
|
+
"integrated_evals",
|
|
1843
|
+
"spanning_evals",
|
|
1844
|
+
"thinky_evals",
|
|
1845
|
+
"tournament",
|
|
1846
|
+
"all",
|
|
1847
|
+
] = typer.Option(
|
|
1848
|
+
"diagnostic_evals",
|
|
1849
|
+
"--mission-set",
|
|
1850
|
+
"-S",
|
|
1851
|
+
metavar="SET",
|
|
1852
|
+
help="Eval suite to run",
|
|
1853
|
+
rich_help_panel="Evaluation",
|
|
1854
|
+
),
|
|
1855
|
+
experiments: Optional[list[str]] = typer.Option( # noqa: B008
|
|
1856
|
+
None,
|
|
1857
|
+
"--experiments",
|
|
1858
|
+
metavar="NAME",
|
|
1859
|
+
help="Specific experiments (subset of mission set)",
|
|
1860
|
+
rich_help_panel="Evaluation",
|
|
1861
|
+
),
|
|
1862
|
+
cogs: Optional[list[int]] = typer.Option( # noqa: B008
|
|
1863
|
+
None,
|
|
1864
|
+
"--cogs",
|
|
1865
|
+
"-c",
|
|
1866
|
+
metavar="N",
|
|
1867
|
+
help="Agent counts to test (repeatable)",
|
|
1868
|
+
rich_help_panel="Evaluation",
|
|
1869
|
+
),
|
|
1870
|
+
# --- Simulation ---
|
|
1871
|
+
steps: int = typer.Option(
|
|
1872
|
+
1000,
|
|
1873
|
+
"--steps",
|
|
1874
|
+
"-s",
|
|
1875
|
+
metavar="N",
|
|
1876
|
+
help="Max steps per episode",
|
|
1877
|
+
rich_help_panel="Simulation",
|
|
1878
|
+
),
|
|
1879
|
+
episodes: int = typer.Option(
|
|
1880
|
+
3,
|
|
1881
|
+
"--episodes",
|
|
1882
|
+
"-e",
|
|
1883
|
+
metavar="N",
|
|
1884
|
+
help="Episodes per case",
|
|
1885
|
+
rich_help_panel="Simulation",
|
|
1886
|
+
),
|
|
1887
|
+
# --- Help ---
|
|
1888
|
+
_help: bool = typer.Option(
|
|
1889
|
+
False,
|
|
1890
|
+
"--help",
|
|
1891
|
+
"-h",
|
|
1892
|
+
help="Show this message and exit",
|
|
1893
|
+
is_eager=True,
|
|
1894
|
+
callback=_help_callback,
|
|
1895
|
+
rich_help_panel="Other",
|
|
1896
|
+
),
|
|
1897
|
+
) -> None:
|
|
1898
|
+
script_path = Path(__file__).resolve().parents[2] / "scripts" / "run_evaluation.py"
|
|
1899
|
+
|
|
1900
|
+
cmd = [sys.executable, str(script_path)]
|
|
1901
|
+
cmd.extend(["--mission-set", mission_set])
|
|
1902
|
+
|
|
1903
|
+
if experiments:
|
|
1904
|
+
cmd.append("--experiments")
|
|
1905
|
+
cmd.extend(experiments)
|
|
1906
|
+
|
|
1907
|
+
if cogs:
|
|
1908
|
+
cmd.append("--cogs")
|
|
1909
|
+
cmd.extend(str(c) for c in cogs)
|
|
1910
|
+
|
|
1911
|
+
cmd.extend(["--steps", str(steps)])
|
|
1912
|
+
cmd.extend(["--repeats", str(episodes)])
|
|
1913
|
+
cmd.append("--no-plots")
|
|
1914
|
+
|
|
1915
|
+
cmd.extend(["--policy", policy])
|
|
1916
|
+
|
|
1917
|
+
console.print("[cyan]Running diagnostic evaluation...[/cyan]")
|
|
1918
|
+
console.print(f"[dim]{' '.join(cmd)}[/dim]")
|
|
1919
|
+
subprocess.run(cmd, check=True)
|
|
1920
|
+
|
|
1921
|
+
|
|
1922
|
+
def _resolve_season(server: str, season_name: str | None = None) -> SeasonInfo:
|
|
1923
|
+
try:
|
|
1924
|
+
if season_name is not None:
|
|
1925
|
+
info = fetch_season_info(server, season_name)
|
|
1926
|
+
console.print(f"[dim]Using season: {info.name}[/dim]")
|
|
1927
|
+
else:
|
|
1928
|
+
info = fetch_default_season(server)
|
|
1929
|
+
console.print(f"[dim]Using default season: {info.name}[/dim]")
|
|
1930
|
+
return info
|
|
1931
|
+
except Exception as e:
|
|
1932
|
+
console.print(f"[red]Could not fetch season from server:[/red] {e}")
|
|
1933
|
+
console.print("Specify a season explicitly with [cyan]--season[/cyan]")
|
|
1934
|
+
raise typer.Exit(1) from None
|
|
1935
|
+
|
|
1936
|
+
|
|
1937
|
+
@app.command(
|
|
1938
|
+
name="validate-policy",
|
|
1939
|
+
help="Validate the policy loads and runs for at least a single step",
|
|
1940
|
+
rich_help_panel="Policies",
|
|
1941
|
+
add_help_option=False,
|
|
1942
|
+
)
|
|
891
1943
|
def validate_policy_cmd(
|
|
892
1944
|
ctx: typer.Context,
|
|
893
|
-
policy: str = typer.
|
|
1945
|
+
policy: str = typer.Option(
|
|
894
1946
|
...,
|
|
1947
|
+
"--policy",
|
|
1948
|
+
"-p",
|
|
1949
|
+
metavar="POLICY",
|
|
895
1950
|
help=f"Policy specification: {policy_arg_example}",
|
|
1951
|
+
rich_help_panel="Policy",
|
|
896
1952
|
),
|
|
897
1953
|
setup_script: Optional[str] = typer.Option(
|
|
898
1954
|
None,
|
|
899
1955
|
"--setup-script",
|
|
900
1956
|
help="Path to a Python setup script to run before loading the policy",
|
|
1957
|
+
rich_help_panel="Policy",
|
|
1958
|
+
),
|
|
1959
|
+
season: Optional[str] = typer.Option(
|
|
1960
|
+
None,
|
|
1961
|
+
"--season",
|
|
1962
|
+
metavar="SEASON",
|
|
1963
|
+
help="Tournament season (determines which game to validate against)",
|
|
1964
|
+
rich_help_panel="Tournament",
|
|
1965
|
+
),
|
|
1966
|
+
server: str = typer.Option(
|
|
1967
|
+
DEFAULT_SUBMIT_SERVER,
|
|
1968
|
+
"--server",
|
|
1969
|
+
metavar="URL",
|
|
1970
|
+
help="Tournament server URL (used to resolve default season)",
|
|
1971
|
+
rich_help_panel="Server",
|
|
1972
|
+
),
|
|
1973
|
+
_help: bool = typer.Option(
|
|
1974
|
+
False,
|
|
1975
|
+
"--help",
|
|
1976
|
+
"-h",
|
|
1977
|
+
help="Show this message and exit",
|
|
1978
|
+
is_eager=True,
|
|
1979
|
+
callback=_help_callback,
|
|
1980
|
+
rich_help_panel="Other",
|
|
901
1981
|
),
|
|
902
1982
|
) -> None:
|
|
1983
|
+
season_info = _resolve_season(server, season)
|
|
1984
|
+
entry_pool_info = next((p for p in season_info.pools if p.name == season_info.entry_pool), None)
|
|
1985
|
+
if not entry_pool_info or not entry_pool_info.config_id:
|
|
1986
|
+
console.print("[red]No entry config found for season[/red]")
|
|
1987
|
+
raise typer.Exit(1)
|
|
1988
|
+
|
|
1989
|
+
with TournamentServerClient(server_url=server) as client:
|
|
1990
|
+
config_data = client.get_config(entry_pool_info.config_id)
|
|
1991
|
+
env_cfg = MettaGridConfig.model_validate(config_data)
|
|
1992
|
+
|
|
903
1993
|
if setup_script:
|
|
904
1994
|
import subprocess
|
|
905
1995
|
import sys
|
|
@@ -923,7 +2013,7 @@ def validate_policy_cmd(
|
|
|
923
2013
|
console.print("[green]Setup script completed[/green]")
|
|
924
2014
|
|
|
925
2015
|
policy_spec = get_policy_spec(ctx, policy)
|
|
926
|
-
validate_policy_spec(policy_spec)
|
|
2016
|
+
validate_policy_spec(policy_spec, env_cfg)
|
|
927
2017
|
console.print("[green]Policy validated successfully[/green]")
|
|
928
2018
|
raise typer.Exit(0)
|
|
929
2019
|
|
|
@@ -936,66 +2026,125 @@ def _parse_init_kwarg(value: str) -> tuple[str, str]:
|
|
|
936
2026
|
return key.replace("-", "_"), val
|
|
937
2027
|
|
|
938
2028
|
|
|
939
|
-
@app.command(
|
|
2029
|
+
@app.command(
|
|
2030
|
+
name="upload",
|
|
2031
|
+
help="Upload a policy to CoGames",
|
|
2032
|
+
rich_help_panel="Tournament",
|
|
2033
|
+
epilog="""[dim]Examples:[/dim]
|
|
2034
|
+
|
|
2035
|
+
[cyan]cogames upload -p ./train_dir/my_run -n my-policy[/cyan] Upload and submit to default season
|
|
2036
|
+
|
|
2037
|
+
[cyan]cogames upload -p ./run -n my-policy --season beta-cvc[/cyan] Upload and submit to specific season
|
|
2038
|
+
|
|
2039
|
+
[cyan]cogames upload -p ./run -n my-policy --no-submit[/cyan] Upload without submitting
|
|
2040
|
+
|
|
2041
|
+
[cyan]cogames upload -p lstm -n my-lstm --dry-run[/cyan] Validate only""",
|
|
2042
|
+
add_help_option=False,
|
|
2043
|
+
)
|
|
940
2044
|
def upload_cmd(
|
|
941
2045
|
ctx: typer.Context,
|
|
2046
|
+
# --- Upload ---
|
|
2047
|
+
name: str = typer.Option(
|
|
2048
|
+
...,
|
|
2049
|
+
"--name",
|
|
2050
|
+
"-n",
|
|
2051
|
+
metavar="NAME",
|
|
2052
|
+
help="Name for your uploaded policy",
|
|
2053
|
+
rich_help_panel="Upload",
|
|
2054
|
+
),
|
|
2055
|
+
# --- Policy ---
|
|
942
2056
|
policy: str = typer.Option(
|
|
943
2057
|
...,
|
|
944
2058
|
"--policy",
|
|
945
2059
|
"-p",
|
|
2060
|
+
metavar="POLICY",
|
|
946
2061
|
help=f"Policy specification: {policy_arg_example}",
|
|
947
|
-
|
|
948
|
-
name: str = typer.Option(
|
|
949
|
-
...,
|
|
950
|
-
"--name",
|
|
951
|
-
"-n",
|
|
952
|
-
help="Policy name for the upload",
|
|
2062
|
+
rich_help_panel="Policy",
|
|
953
2063
|
),
|
|
954
2064
|
init_kwarg: Optional[list[str]] = typer.Option( # noqa: B008
|
|
955
2065
|
None,
|
|
956
2066
|
"--init-kwarg",
|
|
957
2067
|
"-k",
|
|
958
|
-
|
|
2068
|
+
metavar="KEY=VAL",
|
|
2069
|
+
help="Policy init kwargs (can be repeated)",
|
|
2070
|
+
rich_help_panel="Policy",
|
|
959
2071
|
),
|
|
2072
|
+
# --- Files ---
|
|
960
2073
|
include_files: Optional[list[str]] = typer.Option( # noqa: B008
|
|
961
2074
|
None,
|
|
962
2075
|
"--include-files",
|
|
963
2076
|
"-f",
|
|
964
|
-
|
|
2077
|
+
metavar="PATH",
|
|
2078
|
+
help="Files or directories to include (can be repeated)",
|
|
2079
|
+
rich_help_panel="Files",
|
|
965
2080
|
),
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
"--
|
|
969
|
-
|
|
2081
|
+
setup_script: Optional[str] = typer.Option(
|
|
2082
|
+
None,
|
|
2083
|
+
"--setup-script",
|
|
2084
|
+
metavar="PATH",
|
|
2085
|
+
help="Python setup script to run before loading the policy",
|
|
2086
|
+
rich_help_panel="Files",
|
|
970
2087
|
),
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
"
|
|
975
|
-
|
|
2088
|
+
# --- Tournament ---
|
|
2089
|
+
season: Optional[str] = typer.Option(
|
|
2090
|
+
None,
|
|
2091
|
+
"--season",
|
|
2092
|
+
metavar="SEASON",
|
|
2093
|
+
help="Tournament season (default: server's default season)",
|
|
2094
|
+
rich_help_panel="Tournament",
|
|
2095
|
+
),
|
|
2096
|
+
no_submit: bool = typer.Option(
|
|
2097
|
+
False,
|
|
2098
|
+
"--no-submit",
|
|
2099
|
+
help="Upload without submitting to a season",
|
|
2100
|
+
rich_help_panel="Tournament",
|
|
976
2101
|
),
|
|
2102
|
+
# --- Validation ---
|
|
977
2103
|
dry_run: bool = typer.Option(
|
|
978
2104
|
False,
|
|
979
2105
|
"--dry-run",
|
|
980
2106
|
help="Run validation only without uploading",
|
|
2107
|
+
rich_help_panel="Validation",
|
|
981
2108
|
),
|
|
982
2109
|
skip_validation: bool = typer.Option(
|
|
983
2110
|
False,
|
|
984
2111
|
"--skip-validation",
|
|
985
2112
|
help="Skip policy validation in isolated environment",
|
|
2113
|
+
rich_help_panel="Validation",
|
|
986
2114
|
),
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
2115
|
+
# --- Server ---
|
|
2116
|
+
login_server: str = typer.Option(
|
|
2117
|
+
DEFAULT_COGAMES_SERVER,
|
|
2118
|
+
"--login-server",
|
|
2119
|
+
metavar="URL",
|
|
2120
|
+
help="Authentication server URL",
|
|
2121
|
+
rich_help_panel="Server",
|
|
2122
|
+
),
|
|
2123
|
+
server: str = typer.Option(
|
|
2124
|
+
DEFAULT_SUBMIT_SERVER,
|
|
2125
|
+
"--server",
|
|
2126
|
+
metavar="URL",
|
|
2127
|
+
help="Tournament server URL",
|
|
2128
|
+
rich_help_panel="Server",
|
|
2129
|
+
),
|
|
2130
|
+
# --- Help ---
|
|
2131
|
+
_help: bool = typer.Option(
|
|
2132
|
+
False,
|
|
2133
|
+
"--help",
|
|
2134
|
+
"-h",
|
|
2135
|
+
help="Show this message and exit",
|
|
2136
|
+
is_eager=True,
|
|
2137
|
+
callback=_help_callback,
|
|
2138
|
+
rich_help_panel="Other",
|
|
991
2139
|
),
|
|
992
2140
|
) -> None:
|
|
993
|
-
|
|
2141
|
+
season_info = _resolve_season(server, season)
|
|
2142
|
+
|
|
2143
|
+
has_entry_config = any(p.config_id for p in season_info.pools if p.name == season_info.entry_pool)
|
|
2144
|
+
if not has_entry_config and not skip_validation:
|
|
2145
|
+
console.print("[yellow]Warning: No entry config found for season. Skipping validation.[/yellow]")
|
|
2146
|
+
skip_validation = True
|
|
994
2147
|
|
|
995
|
-
This command validates your policy, creates an upload package,
|
|
996
|
-
and uploads it to the CoGames server. You can then submit it
|
|
997
|
-
to tournaments using 'cogames submit'.
|
|
998
|
-
"""
|
|
999
2148
|
init_kwargs: dict[str, str] = {}
|
|
1000
2149
|
if init_kwarg:
|
|
1001
2150
|
for kv in init_kwarg:
|
|
@@ -1013,47 +2162,73 @@ def upload_cmd(
|
|
|
1013
2162
|
skip_validation=skip_validation,
|
|
1014
2163
|
init_kwargs=init_kwargs if init_kwargs else None,
|
|
1015
2164
|
setup_script=setup_script,
|
|
2165
|
+
validation_season=season_info.name,
|
|
2166
|
+
season=season_info.name if not no_submit else None,
|
|
1016
2167
|
)
|
|
1017
2168
|
|
|
1018
2169
|
if result:
|
|
1019
2170
|
console.print(f"[green]Upload complete: {result.name}:v{result.version}[/green]")
|
|
1020
|
-
|
|
2171
|
+
if result.pools:
|
|
2172
|
+
console.print(f"[dim]Added to pools: {', '.join(result.pools)}[/dim]")
|
|
2173
|
+
console.print(f"[dim]Results:[/dim] {results_url_for_season(server, season_info.name)}")
|
|
2174
|
+
elif no_submit:
|
|
2175
|
+
console.print(f"\nTo submit to a tournament: cogames submit {result.name}:v{result.version}")
|
|
1021
2176
|
|
|
1022
2177
|
|
|
1023
|
-
@app.command(
|
|
2178
|
+
@app.command(
|
|
2179
|
+
name="submit",
|
|
2180
|
+
help="Submit a policy to a tournament season",
|
|
2181
|
+
rich_help_panel="Tournament",
|
|
2182
|
+
epilog="""[dim]Examples:[/dim]
|
|
2183
|
+
|
|
2184
|
+
[cyan]cogames submit my-policy[/cyan] Submit to default season
|
|
2185
|
+
|
|
2186
|
+
[cyan]cogames submit my-policy:v3 --season beta-cvc[/cyan] Submit specific version to specific season""",
|
|
2187
|
+
add_help_option=False,
|
|
2188
|
+
)
|
|
1024
2189
|
def submit_cmd(
|
|
1025
2190
|
policy_name: str = typer.Argument(
|
|
1026
2191
|
...,
|
|
2192
|
+
metavar="POLICY",
|
|
1027
2193
|
help="Policy name (e.g., 'my-policy' or 'my-policy:v3' for specific version)",
|
|
1028
2194
|
),
|
|
1029
|
-
season: str = typer.Option(
|
|
1030
|
-
|
|
2195
|
+
season: Optional[str] = typer.Option(
|
|
2196
|
+
None,
|
|
1031
2197
|
"--season",
|
|
1032
|
-
|
|
2198
|
+
metavar="SEASON",
|
|
2199
|
+
help="Tournament season name",
|
|
2200
|
+
rich_help_panel="Tournament",
|
|
1033
2201
|
),
|
|
1034
2202
|
login_server: str = typer.Option(
|
|
1035
2203
|
DEFAULT_COGAMES_SERVER,
|
|
1036
2204
|
"--login-server",
|
|
1037
|
-
|
|
2205
|
+
metavar="URL",
|
|
2206
|
+
help="Authentication server URL",
|
|
2207
|
+
rich_help_panel="Server",
|
|
1038
2208
|
),
|
|
1039
2209
|
server: str = typer.Option(
|
|
1040
2210
|
DEFAULT_SUBMIT_SERVER,
|
|
1041
2211
|
"--server",
|
|
1042
2212
|
"-s",
|
|
1043
|
-
|
|
2213
|
+
metavar="URL",
|
|
2214
|
+
help="Tournament server URL",
|
|
2215
|
+
rich_help_panel="Server",
|
|
2216
|
+
),
|
|
2217
|
+
_help: bool = typer.Option(
|
|
2218
|
+
False,
|
|
2219
|
+
"--help",
|
|
2220
|
+
"-h",
|
|
2221
|
+
help="Show this message and exit",
|
|
2222
|
+
is_eager=True,
|
|
2223
|
+
callback=_help_callback,
|
|
2224
|
+
rich_help_panel="Other",
|
|
1044
2225
|
),
|
|
1045
2226
|
) -> None:
|
|
1046
|
-
"""Submit an uploaded policy to a tournament season.
|
|
1047
|
-
|
|
1048
|
-
First upload your policy with 'cogames upload', then submit it to
|
|
1049
|
-
a tournament season with this command.
|
|
1050
|
-
|
|
1051
|
-
Examples:
|
|
1052
|
-
cogames submit my-policy --season beta
|
|
1053
|
-
cogames submit my-policy:v3 --season beta
|
|
1054
|
-
"""
|
|
1055
2227
|
import httpx
|
|
1056
2228
|
|
|
2229
|
+
season_info = _resolve_season(server, season)
|
|
2230
|
+
season_name = season_info.name
|
|
2231
|
+
|
|
1057
2232
|
client = TournamentServerClient.from_login(server_url=server, login_server=login_server)
|
|
1058
2233
|
if not client:
|
|
1059
2234
|
raise typer.Exit(1)
|
|
@@ -1065,7 +2240,7 @@ def submit_cmd(
|
|
|
1065
2240
|
raise typer.Exit(1) from None
|
|
1066
2241
|
|
|
1067
2242
|
version_str = f"[dim]:v{version}[/dim]" if version is not None else "[dim] (latest)[/dim]"
|
|
1068
|
-
console.print(f"[bold]Submitting {name}[/bold]{version_str} to season '{
|
|
2243
|
+
console.print(f"[bold]Submitting {name}[/bold]{version_str} to season '{season_name}'\n")
|
|
1069
2244
|
|
|
1070
2245
|
with client:
|
|
1071
2246
|
pv = client.lookup_policy_version(name=name, version=version)
|
|
@@ -1076,12 +2251,12 @@ def submit_cmd(
|
|
|
1076
2251
|
raise typer.Exit(1)
|
|
1077
2252
|
|
|
1078
2253
|
try:
|
|
1079
|
-
result = client.submit_to_season(
|
|
2254
|
+
result = client.submit_to_season(season_name, pv.id)
|
|
1080
2255
|
except httpx.HTTPStatusError as exc:
|
|
1081
2256
|
if exc.response.status_code == 404:
|
|
1082
|
-
console.print(f"[red]Season '{
|
|
2257
|
+
console.print(f"[red]Season '{season_name}' not found[/red]")
|
|
1083
2258
|
elif exc.response.status_code == 409:
|
|
1084
|
-
console.print(f"[red]Policy already submitted to season '{
|
|
2259
|
+
console.print(f"[red]Policy already submitted to season '{season_name}'[/red]")
|
|
1085
2260
|
else:
|
|
1086
2261
|
console.print(f"[red]Submit failed with status {exc.response.status_code}[/red]")
|
|
1087
2262
|
console.print(f"[dim]{exc.response.text}[/dim]")
|
|
@@ -1090,33 +2265,49 @@ def submit_cmd(
|
|
|
1090
2265
|
console.print(f"[red]Submit failed:[/red] {exc}")
|
|
1091
2266
|
raise typer.Exit(1) from exc
|
|
1092
2267
|
|
|
1093
|
-
console.print(f"\n[bold green]Submitted to season '{
|
|
2268
|
+
console.print(f"\n[bold green]Submitted to season '{season_name}'[/bold green]")
|
|
1094
2269
|
if result.pools:
|
|
1095
|
-
console.print(f"[dim]
|
|
2270
|
+
console.print(f"[dim]Added to pools: {', '.join(result.pools)}[/dim]")
|
|
2271
|
+
console.print(f"[dim]Results:[/dim] {results_url_for_season(server, season_name)}")
|
|
2272
|
+
console.print(f"[dim]CLI:[/dim] cogames leaderboard --season {season_name}")
|
|
2273
|
+
|
|
1096
2274
|
|
|
2275
|
+
@app.command(
|
|
2276
|
+
name="docs",
|
|
2277
|
+
help="Print documentation (run without arguments to see available docs)",
|
|
2278
|
+
rich_help_panel="Info",
|
|
2279
|
+
epilog="""[dim]Examples:[/dim]
|
|
2280
|
+
|
|
2281
|
+
[cyan]cogames docs[/cyan] List available documents
|
|
1097
2282
|
|
|
1098
|
-
|
|
2283
|
+
[cyan]cogames docs readme[/cyan] Print README
|
|
2284
|
+
|
|
2285
|
+
[cyan]cogames docs mission[/cyan] Print mission briefing""",
|
|
2286
|
+
add_help_option=False,
|
|
2287
|
+
)
|
|
1099
2288
|
def docs_cmd(
|
|
1100
|
-
doc_name: Optional[str] = typer.Argument(
|
|
2289
|
+
doc_name: Optional[str] = typer.Argument(
|
|
2290
|
+
None,
|
|
2291
|
+
metavar="DOC",
|
|
2292
|
+
help="Document name (readme, mission, technical_manual, scripted_agent, evals, mapgen)",
|
|
2293
|
+
),
|
|
2294
|
+
_help: bool = typer.Option(
|
|
2295
|
+
False,
|
|
2296
|
+
"--help",
|
|
2297
|
+
"-h",
|
|
2298
|
+
help="Show this message and exit",
|
|
2299
|
+
is_eager=True,
|
|
2300
|
+
callback=_help_callback,
|
|
2301
|
+
),
|
|
1101
2302
|
) -> None:
|
|
1102
|
-
"""Print a documentation file.
|
|
1103
|
-
|
|
1104
|
-
Available documents:
|
|
1105
|
-
- readme: README.md - CoGames overview and documentation
|
|
1106
|
-
- mission: MISSION.md - Mission briefing for Machina VII Deployment
|
|
1107
|
-
- technical_manual: TECHNICAL_MANUAL.md - Technical manual for Cogames
|
|
1108
|
-
- scripted_agent: Scripted agent policy documentation
|
|
1109
|
-
- evals: Evaluation missions documentation
|
|
1110
|
-
- mapgen: Cogs vs Clips map generation documentation
|
|
1111
|
-
"""
|
|
1112
2303
|
# Hardcoded mapping of document names to file paths and descriptions
|
|
1113
2304
|
package_root = Path(__file__).parent.parent.parent
|
|
1114
2305
|
docs_map: dict[str, tuple[Path, str]] = {
|
|
1115
2306
|
"readme": (package_root / "README.md", "CoGames overview and documentation"),
|
|
1116
|
-
"mission": (package_root / "MISSION.md", "Mission briefing for
|
|
2307
|
+
"mission": (package_root / "MISSION.md", "Mission briefing for CogsGuard Deployment"),
|
|
1117
2308
|
"technical_manual": (package_root / "TECHNICAL_MANUAL.md", "Technical manual for Cogames"),
|
|
1118
2309
|
"scripted_agent": (
|
|
1119
|
-
Path(__file__).parent / "
|
|
2310
|
+
Path(__file__).parent / "docs" / "SCRIPTED_AGENT.md",
|
|
1120
2311
|
"Scripted agent policy documentation",
|
|
1121
2312
|
),
|
|
1122
2313
|
"evals": (
|