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.
Files changed (169) hide show
  1. cogames/cli/client.py +60 -6
  2. cogames/cli/docsync/__init__.py +0 -0
  3. cogames/cli/docsync/_nb_md_directive_processing.py +180 -0
  4. cogames/cli/docsync/_nb_md_sync.py +103 -0
  5. cogames/cli/docsync/_nb_py_sync.py +122 -0
  6. cogames/cli/docsync/_three_way_sync.py +115 -0
  7. cogames/cli/docsync/_utils.py +76 -0
  8. cogames/cli/docsync/docsync.py +156 -0
  9. cogames/cli/leaderboard.py +112 -28
  10. cogames/cli/mission.py +64 -53
  11. cogames/cli/policy.py +46 -10
  12. cogames/cli/submit.py +268 -67
  13. cogames/cogs_vs_clips/cog.py +79 -0
  14. cogames/cogs_vs_clips/cogs_vs_clips_mapgen.md +19 -16
  15. cogames/cogs_vs_clips/cogsguard_reward_variants.py +153 -0
  16. cogames/cogs_vs_clips/cogsguard_tutorial.py +56 -0
  17. cogames/cogs_vs_clips/evals/README.md +10 -16
  18. cogames/cogs_vs_clips/evals/cogsguard_evals.py +81 -0
  19. cogames/cogs_vs_clips/evals/diagnostic_evals.py +49 -444
  20. cogames/cogs_vs_clips/evals/difficulty_variants.py +13 -326
  21. cogames/cogs_vs_clips/evals/integrated_evals.py +5 -45
  22. cogames/cogs_vs_clips/evals/spanning_evals.py +9 -180
  23. cogames/cogs_vs_clips/mission.py +187 -146
  24. cogames/cogs_vs_clips/missions.py +46 -137
  25. cogames/cogs_vs_clips/procedural.py +8 -8
  26. cogames/cogs_vs_clips/sites.py +107 -3
  27. cogames/cogs_vs_clips/stations.py +198 -186
  28. cogames/cogs_vs_clips/tutorial_missions.py +1 -1
  29. cogames/cogs_vs_clips/variants.py +25 -476
  30. cogames/device.py +13 -1
  31. cogames/{policy/scripted_agent/README.md → docs/SCRIPTED_AGENT.md} +82 -58
  32. cogames/evaluate.py +18 -30
  33. cogames/main.py +1434 -243
  34. cogames/maps/canidate1_1000.map +1 -1
  35. cogames/maps/canidate1_1000_stations.map +2 -2
  36. cogames/maps/canidate1_500.map +1 -1
  37. cogames/maps/canidate1_500_stations.map +2 -2
  38. cogames/maps/canidate2_1000.map +1 -1
  39. cogames/maps/canidate2_1000_stations.map +2 -2
  40. cogames/maps/canidate2_500.map +1 -1
  41. cogames/maps/canidate2_500_stations.map +2 -2
  42. cogames/maps/canidate3_1000.map +1 -1
  43. cogames/maps/canidate3_1000_stations.map +2 -2
  44. cogames/maps/canidate3_500.map +1 -1
  45. cogames/maps/canidate3_500_stations.map +2 -2
  46. cogames/maps/canidate4_500.map +1 -1
  47. cogames/maps/canidate4_500_stations.map +2 -2
  48. cogames/maps/cave_base_50.map +2 -2
  49. cogames/maps/diagnostic_evals/diagnostic_agile.map +2 -2
  50. cogames/maps/diagnostic_evals/diagnostic_agile_hard.map +2 -2
  51. cogames/maps/diagnostic_evals/diagnostic_charge_up.map +2 -2
  52. cogames/maps/diagnostic_evals/diagnostic_charge_up_hard.map +2 -2
  53. cogames/maps/diagnostic_evals/diagnostic_chest_navigation1.map +2 -2
  54. cogames/maps/diagnostic_evals/diagnostic_chest_navigation1_hard.map +2 -2
  55. cogames/maps/diagnostic_evals/diagnostic_chest_navigation2.map +2 -2
  56. cogames/maps/diagnostic_evals/diagnostic_chest_navigation2_hard.map +2 -2
  57. cogames/maps/diagnostic_evals/diagnostic_chest_navigation3.map +2 -2
  58. cogames/maps/diagnostic_evals/diagnostic_chest_navigation3_hard.map +2 -2
  59. cogames/maps/diagnostic_evals/diagnostic_chest_near.map +2 -2
  60. cogames/maps/diagnostic_evals/diagnostic_chest_search.map +2 -2
  61. cogames/maps/diagnostic_evals/diagnostic_chest_search_hard.map +2 -2
  62. cogames/maps/diagnostic_evals/diagnostic_extract_lab.map +2 -2
  63. cogames/maps/diagnostic_evals/diagnostic_extract_lab_hard.map +2 -2
  64. cogames/maps/diagnostic_evals/diagnostic_memory.map +2 -2
  65. cogames/maps/diagnostic_evals/diagnostic_memory_hard.map +2 -2
  66. cogames/maps/diagnostic_evals/diagnostic_radial.map +2 -2
  67. cogames/maps/diagnostic_evals/diagnostic_radial_hard.map +2 -2
  68. cogames/maps/diagnostic_evals/diagnostic_resource_lab.map +2 -2
  69. cogames/maps/diagnostic_evals/diagnostic_unclip.map +2 -2
  70. cogames/maps/evals/eval_balanced_spread.map +9 -5
  71. cogames/maps/evals/eval_clip_oxygen.map +9 -5
  72. cogames/maps/evals/eval_collect_resources.map +9 -5
  73. cogames/maps/evals/eval_collect_resources_hard.map +9 -5
  74. cogames/maps/evals/eval_collect_resources_medium.map +9 -5
  75. cogames/maps/evals/eval_divide_and_conquer.map +9 -5
  76. cogames/maps/evals/eval_energy_starved.map +9 -5
  77. cogames/maps/evals/eval_multi_coordinated_collect_hard.map +9 -5
  78. cogames/maps/evals/eval_oxygen_bottleneck.map +9 -5
  79. cogames/maps/evals/eval_single_use_world.map +9 -5
  80. cogames/maps/evals/extractor_hub_100x100.map +9 -5
  81. cogames/maps/evals/extractor_hub_30x30.map +9 -5
  82. cogames/maps/evals/extractor_hub_50x50.map +9 -5
  83. cogames/maps/evals/extractor_hub_70x70.map +9 -5
  84. cogames/maps/evals/extractor_hub_80x80.map +9 -5
  85. cogames/maps/machina_100_stations.map +2 -2
  86. cogames/maps/machina_200_stations.map +2 -2
  87. cogames/maps/machina_200_stations_small.map +2 -2
  88. cogames/maps/machina_eval_exp01.map +2 -2
  89. cogames/maps/machina_eval_template_large.map +2 -2
  90. cogames/maps/machinatrainer4agents.map +2 -2
  91. cogames/maps/machinatrainer4agentsbase.map +2 -2
  92. cogames/maps/machinatrainerbig.map +2 -2
  93. cogames/maps/machinatrainersmall.map +2 -2
  94. cogames/maps/planky_evals/aligner_avoid_aoe.map +28 -0
  95. cogames/maps/planky_evals/aligner_full_cycle.map +28 -0
  96. cogames/maps/planky_evals/aligner_gear.map +24 -0
  97. cogames/maps/planky_evals/aligner_hearts.map +24 -0
  98. cogames/maps/planky_evals/aligner_junction.map +26 -0
  99. cogames/maps/planky_evals/exploration_distant.map +28 -0
  100. cogames/maps/planky_evals/maze.map +32 -0
  101. cogames/maps/planky_evals/miner_best_resource.map +26 -0
  102. cogames/maps/planky_evals/miner_deposit.map +24 -0
  103. cogames/maps/planky_evals/miner_extract.map +26 -0
  104. cogames/maps/planky_evals/miner_full_cycle.map +28 -0
  105. cogames/maps/planky_evals/miner_gear.map +24 -0
  106. cogames/maps/planky_evals/multi_role.map +28 -0
  107. cogames/maps/planky_evals/resource_chain.map +30 -0
  108. cogames/maps/planky_evals/scout_explore.map +32 -0
  109. cogames/maps/planky_evals/scout_gear.map +24 -0
  110. cogames/maps/planky_evals/scrambler_full_cycle.map +28 -0
  111. cogames/maps/planky_evals/scrambler_gear.map +24 -0
  112. cogames/maps/planky_evals/scrambler_target.map +26 -0
  113. cogames/maps/planky_evals/stuck_corridor.map +32 -0
  114. cogames/maps/planky_evals/survive_retreat.map +26 -0
  115. cogames/maps/training_facility_clipped.map +2 -2
  116. cogames/maps/training_facility_open_1.map +2 -2
  117. cogames/maps/training_facility_open_2.map +2 -2
  118. cogames/maps/training_facility_open_3.map +2 -2
  119. cogames/maps/training_facility_tight_4.map +2 -2
  120. cogames/maps/training_facility_tight_5.map +2 -2
  121. cogames/maps/vanilla_large.map +2 -2
  122. cogames/maps/vanilla_small.map +2 -2
  123. cogames/pickup.py +183 -0
  124. cogames/play.py +166 -33
  125. cogames/policy/chaos_monkey.py +54 -0
  126. cogames/policy/nim_agents/__init__.py +27 -10
  127. cogames/policy/nim_agents/agents.py +121 -60
  128. cogames/policy/nim_agents/thinky_eval.py +35 -222
  129. cogames/policy/pufferlib_policy.py +67 -32
  130. cogames/policy/starter_agent.py +184 -0
  131. cogames/policy/trainable_policy_template.py +4 -1
  132. cogames/train.py +51 -13
  133. cogames/verbose.py +2 -2
  134. cogames-0.3.64.dist-info/METADATA +1842 -0
  135. cogames-0.3.64.dist-info/RECORD +159 -0
  136. cogames-0.3.64.dist-info/licenses/LICENSE +21 -0
  137. cogames-0.3.64.dist-info/top_level.txt +2 -0
  138. metta_alo/__init__.py +0 -0
  139. metta_alo/job_specs.py +17 -0
  140. metta_alo/policy.py +16 -0
  141. metta_alo/pure_single_episode_runner.py +75 -0
  142. metta_alo/py.typed +0 -0
  143. metta_alo/rollout.py +322 -0
  144. metta_alo/scoring.py +168 -0
  145. cogames/maps/diagnostic_evals/diagnostic_assembler_near.map +0 -49
  146. cogames/maps/diagnostic_evals/diagnostic_assembler_search.map +0 -49
  147. cogames/maps/diagnostic_evals/diagnostic_assembler_search_hard.map +0 -89
  148. cogames/policy/nim_agents/common.nim +0 -887
  149. cogames/policy/nim_agents/install.sh +0 -1
  150. cogames/policy/nim_agents/ladybug_agent.nim +0 -984
  151. cogames/policy/nim_agents/nim_agents.nim +0 -55
  152. cogames/policy/nim_agents/nim_agents.nims +0 -14
  153. cogames/policy/nim_agents/nimby.lock +0 -3
  154. cogames/policy/nim_agents/racecar_agents.nim +0 -884
  155. cogames/policy/nim_agents/random_agents.nim +0 -68
  156. cogames/policy/nim_agents/test_agents.py +0 -53
  157. cogames/policy/nim_agents/thinky_agents.nim +0 -717
  158. cogames/policy/scripted_agent/baseline_agent.py +0 -1049
  159. cogames/policy/scripted_agent/demo_policy.py +0 -244
  160. cogames/policy/scripted_agent/pathfinding.py +0 -126
  161. cogames/policy/scripted_agent/starter_agent.py +0 -136
  162. cogames/policy/scripted_agent/types.py +0 -235
  163. cogames/policy/scripted_agent/unclipping_agent.py +0 -476
  164. cogames/policy/scripted_agent/utils.py +0 -385
  165. cogames-0.3.49.dist-info/METADATA +0 -406
  166. cogames-0.3.49.dist-info/RECORD +0 -136
  167. cogames-0.3.49.dist-info/top_level.txt +0 -1
  168. {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/WHEEL +0 -0
  169. {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/entry_points.txt +0 -0
cogames/cli/mission.py CHANGED
@@ -8,41 +8,57 @@ from rich import box
8
8
  from rich.table import Table
9
9
 
10
10
  from cogames.cli.base import console
11
- from cogames.cogs_vs_clips.mission import MAP_MISSION_DELIMITER, Mission, MissionVariant, NumCogsVariant, Site
11
+ from cogames.cogs_vs_clips.mission import (
12
+ MAP_MISSION_DELIMITER,
13
+ AnyMission,
14
+ Mission,
15
+ MissionVariant,
16
+ NumCogsVariant,
17
+ Site,
18
+ )
12
19
  from cogames.cogs_vs_clips.procedural import MachinaArena
13
20
  from cogames.cogs_vs_clips.sites import SITES
14
21
  from cogames.cogs_vs_clips.variants import HIDDEN_VARIANTS, VARIANTS
15
22
  from cogames.game import load_mission_config, load_mission_config_from_python
16
23
  from mettagrid import MettaGridConfig
17
- from mettagrid.config.mettagrid_config import AssemblerConfig
18
24
  from mettagrid.mapgen.mapgen import MapGen
19
25
 
20
26
 
21
27
  @lru_cache(maxsize=1)
22
- def _get_core_missions() -> list[Mission]:
28
+ def _get_core_missions() -> list[AnyMission]:
23
29
  from cogames.cogs_vs_clips.missions import get_core_missions
24
30
 
25
31
  return get_core_missions()
26
32
 
27
33
 
28
34
  @lru_cache(maxsize=1)
29
- def _get_eval_missions_all() -> list[Mission]:
35
+ def _get_legacy_missions() -> list[Mission]:
36
+ from cogames.cogs_vs_clips.missions import get_legacy_missions
37
+
38
+ return get_legacy_missions()
39
+
40
+
41
+ @lru_cache(maxsize=1)
42
+ def _get_eval_missions_all() -> list[AnyMission]:
43
+ from cogames.cogs_vs_clips.evals.cogsguard_evals import COGSGUARD_EVAL_MISSIONS
30
44
  from cogames.cogs_vs_clips.evals.diagnostic_evals import DIAGNOSTIC_EVALS
31
45
  from cogames.cogs_vs_clips.evals.integrated_evals import EVAL_MISSIONS as INTEGRATED_EVAL_MISSIONS
32
46
  from cogames.cogs_vs_clips.evals.spanning_evals import EVAL_MISSIONS as SPANNING_EVAL_MISSIONS
33
47
 
34
- missions: list[Mission] = []
48
+ missions: list[AnyMission] = []
49
+ missions.extend(COGSGUARD_EVAL_MISSIONS)
35
50
  missions.extend(INTEGRATED_EVAL_MISSIONS)
36
51
  missions.extend(SPANNING_EVAL_MISSIONS)
37
52
  missions.extend(mission_cls() for mission_cls in DIAGNOSTIC_EVALS) # type: ignore[call-arg]
38
53
  return missions
39
54
 
40
55
 
41
- def load_mission_set(mission_set: str) -> list[Mission]:
56
+ def load_mission_set(mission_set: str) -> list[AnyMission]:
42
57
  """Load a predefined set of evaluation missions.
43
58
 
44
59
  Args:
45
60
  mission_set: Name of mission set to load. Options:
61
+ - "cogsguard_evals": CogsGuard evaluation missions (map-based)
46
62
  - "integrated_evals": Integrated evaluation missions
47
63
  - "spanning_evals": Spanning evaluation missions
48
64
  - "diagnostic_evals": Diagnostic evaluation missions
@@ -54,6 +70,7 @@ def load_mission_set(mission_set: str) -> list[Mission]:
54
70
  Raises:
55
71
  ValueError: If mission_set name is unknown
56
72
  """
73
+ missions_list: list[AnyMission]
57
74
  if mission_set == "all":
58
75
  # All missions: eval missions + integrated + spanning + diagnostic + core missions
59
76
  missions_list = list(_get_eval_missions_all())
@@ -67,7 +84,11 @@ def load_mission_set(mission_set: str) -> list[Mission]:
67
84
  elif mission_set == "diagnostic_evals":
68
85
  from cogames.cogs_vs_clips.evals.diagnostic_evals import DIAGNOSTIC_EVALS
69
86
 
70
- missions_list: list[Mission] = [mission_cls() for mission_cls in DIAGNOSTIC_EVALS] # type: ignore[call-arg]
87
+ missions_list = [mission_cls() for mission_cls in DIAGNOSTIC_EVALS] # type: ignore[call-arg]
88
+ elif mission_set == "cogsguard_evals":
89
+ from cogames.cogs_vs_clips.evals.cogsguard_evals import COGSGUARD_EVAL_MISSIONS
90
+
91
+ missions_list = list(COGSGUARD_EVAL_MISSIONS)
71
92
  elif mission_set == "integrated_evals":
72
93
  from cogames.cogs_vs_clips.evals.integrated_evals import EVAL_MISSIONS as INTEGRATED_EVAL_MISSIONS
73
94
 
@@ -77,7 +98,7 @@ def load_mission_set(mission_set: str) -> list[Mission]:
77
98
 
78
99
  missions_list = list(SPANNING_EVAL_MISSIONS)
79
100
  else:
80
- available = "eval_missions, integrated_evals, spanning_evals, diagnostic_evals, all"
101
+ available = "cogsguard_evals, integrated_evals, spanning_evals, diagnostic_evals, all"
81
102
  raise ValueError(f"Unknown mission set: {mission_set}\nAvailable sets: {available}")
82
103
 
83
104
  return missions_list
@@ -145,7 +166,7 @@ def get_site_by_name(site_name: str) -> Site:
145
166
 
146
167
  def get_mission_name_and_config(
147
168
  ctx: typer.Context, mission_arg: Optional[str], variants_arg: Optional[list[str]] = None, cogs: Optional[int] = None
148
- ) -> tuple[str, MettaGridConfig, Optional[Mission]]:
169
+ ) -> tuple[str, MettaGridConfig, Optional[AnyMission]]:
149
170
  if not mission_arg:
150
171
  console.print(ctx.get_help())
151
172
  console.print("[yellow]Missing: --mission / -m[/yellow]\n")
@@ -153,13 +174,14 @@ def get_mission_name_and_config(
153
174
  try:
154
175
  return get_mission(mission_arg, variants_arg, cogs)
155
176
  except ValueError as e:
156
- console.print(f"[yellow]{e}[/yellow]\n")
177
+ console.print(f"[red]Mission '{mission_arg}' not found.[/red]")
178
+ console.print("[dim]Run: cogames missions (or cogames missions <site>) to list options.[/dim]\n")
179
+ raise typer.Exit(1) from e
157
180
  list_missions()
158
181
 
159
- if mission_arg is not None:
160
- console.print("\n" + ctx.get_usage())
182
+ console.print("\n" + ctx.get_usage())
161
183
  console.print("\n")
162
- raise typer.Exit(0)
184
+ raise typer.Exit(1)
163
185
 
164
186
 
165
187
  def get_mission_names_and_configs(
@@ -196,13 +218,14 @@ def get_mission_names_and_configs(
196
218
 
197
219
  return deduped
198
220
  except ValueError as e:
199
- console.print(f"[yellow]{e}[/yellow]\n")
221
+ console.print(f"[red]{e}[/red]")
222
+ console.print("[dim]Run: cogames missions (or cogames missions <site>) to list options.[/dim]\n")
223
+ raise typer.Exit(1) from e
200
224
  list_missions()
201
225
 
202
- if missions_arg is not None:
203
- console.print("\n" + ctx.get_usage())
226
+ console.print("\n" + ctx.get_usage())
204
227
  console.print("\n")
205
- raise typer.Exit(0)
228
+ raise typer.Exit(1)
206
229
 
207
230
 
208
231
  def _get_missions_by_possible_wildcard(
@@ -229,10 +252,13 @@ def find_mission(
229
252
  mission_name: str | None = None, # None means first mission on the site
230
253
  *,
231
254
  include_evals: bool = False,
232
- ) -> Mission:
233
- missions = _get_core_missions()
255
+ include_legacy: bool = False,
256
+ ) -> AnyMission:
257
+ missions: list[AnyMission] = list(_get_core_missions())
234
258
  if include_evals:
235
259
  missions = [*missions, *_get_eval_missions_all()]
260
+ if include_legacy:
261
+ missions = [*missions, *_get_legacy_missions()]
236
262
 
237
263
  found_site = False
238
264
  for mission in missions:
@@ -258,14 +284,18 @@ def find_mission(
258
284
 
259
285
 
260
286
  def get_mission(
261
- mission_arg: str, variants_arg: Optional[list[str]] = None, cogs: Optional[int] = None
262
- ) -> tuple[str, MettaGridConfig, Optional[Mission]]:
287
+ mission_arg: str,
288
+ variants_arg: Optional[list[str]] = None,
289
+ cogs: Optional[int] = None,
290
+ include_legacy: bool = False,
291
+ ) -> tuple[str, MettaGridConfig, Optional[AnyMission]]:
263
292
  """Get a specific mission configuration by name or file path.
264
293
 
265
294
  Args:
266
295
  mission_arg: Name of the map or path to config file (.yaml, .json, or .py)
267
296
  variants_arg: List of variant names like ["solar_flare", "dark_side"]
268
297
  cogs: Number of cogs (agents) to use, overrides the default from the mission
298
+ include_legacy: Whether to include legacy (pre-CogsGuard) missions
269
299
 
270
300
  Returns:
271
301
  Tuple of (mission name, MettaGridConfig, Mission or None)
@@ -300,10 +330,10 @@ def get_mission(
300
330
  else:
301
331
  site_name, mission_name = mission_arg.split(MAP_MISSION_DELIMITER)
302
332
 
303
- mission = find_mission(site_name, mission_name, include_evals=True)
304
- # Apply variants
305
- mission = mission.with_variants(variants)
333
+ mission: AnyMission = find_mission(site_name, mission_name, include_evals=True, include_legacy=include_legacy)
306
334
 
335
+ if variants:
336
+ mission = mission.with_variants(variants)
307
337
  if cogs is not None:
308
338
  mission = mission.with_variants([NumCogsVariant(num_cogs=cogs)])
309
339
 
@@ -403,22 +433,17 @@ def list_missions(site_filter: Optional[str] = None) -> None:
403
433
  console.print(table)
404
434
 
405
435
  console.print("\nTo set [bold blue]-m[/bold blue]:")
406
- console.print(" • Use [blue]<site>.<mission>[/blue] (e.g., training_facility.harvest)")
436
+ console.print(" • Use [blue]<site>.<mission>[/blue] (e.g., cogsguard_machina_1.basic)")
407
437
  console.print(" • Or pass a mission config file path")
408
- console.print(" • List a site's missions: [blue]cogames missions training_facility[/blue]")
409
- console.print("\nVariants:")
410
- console.print(" • Repeat [yellow]--variant <name>[/yellow] (e.g., --variant solar_flare)")
438
+ console.print(" • List a site's missions: [blue]cogames missions cogsguard_machina_1[/blue]")
411
439
  console.print("\nCogs:")
412
440
  console.print(" • [green]--cogs N[/green] or [green]-c N[/green]")
413
441
  console.print("\n[bold green]Examples:[/bold green]")
414
442
  console.print(" cogames missions")
415
- console.print(" cogames missions training_facility")
416
- console.print(" cogames play --mission [blue]training_facility.harvest[/blue]")
417
- console.print(
418
- " cogames play --mission [blue]machina_1.open_world[/blue] "
419
- "--variant [yellow]solar_flare[/yellow] --variant [yellow]rough_terrain[/yellow] --cogs [green]8[/green]"
420
- )
421
- console.print(" cogames train --mission [blue]<site>.<mission>[/blue] --cogs [green]4[/green]")
443
+ console.print(" cogames missions cogsguard_machina_1")
444
+ console.print(" cogames play --mission [blue]cogsguard_machina_1.basic[/blue]")
445
+ console.print(" cogames play --mission [blue]cogsguard_machina_1.basic[/blue] --cogs [green]8[/green]")
446
+ console.print(" cogames train --mission [blue]cogsguard_machina_1.basic[/blue] --cogs [green]4[/green]")
422
447
 
423
448
 
424
449
  def list_evals() -> None:
@@ -429,7 +454,7 @@ def list_evals() -> None:
429
454
  return
430
455
 
431
456
  # Group missions by site
432
- missions_by_site: dict[str, list[Mission]] = {}
457
+ missions_by_site: dict[str, list[AnyMission]] = {}
433
458
  for m in evals:
434
459
  missions_by_site.setdefault(m.site.name, []).append(m)
435
460
 
@@ -481,7 +506,7 @@ def list_evals() -> None:
481
506
  console.print(" [bold]cogames play[/bold] --mission [blue]evals.divide_and_conquer[/blue]")
482
507
 
483
508
 
484
- def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cfg: Mission | None = None) -> None:
509
+ def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cfg: AnyMission | None = None) -> None:
485
510
  """Print detailed information about a specific mission.
486
511
 
487
512
  Args:
@@ -497,7 +522,6 @@ def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cf
497
522
  console.print("[bold]Description:[/bold]")
498
523
  console.print(f" {mission_cfg.description}\n")
499
524
 
500
- # Variants applied
501
525
  if mission_cfg.variants:
502
526
  console.print("[bold]Variants Applied:[/bold]")
503
527
  for v in mission_cfg.variants:
@@ -519,14 +543,7 @@ def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cf
519
543
  console.print(f" • Biome weights: {instance.biome_weights}")
520
544
  console.print(f" • Building coverage: {instance.building_coverage}")
521
545
  # Key knobs
522
- console.print(
523
- f" • Regen interval: {game_config.game.inventory_regen_interval}, "
524
- f"Move energy cost: {game_config.game.actions.move.consumed_resources.get('energy', 0)}"
525
- )
526
- # Clipping info
527
- clip_period = getattr(game_config.game.clipper, "clip_period", 0)
528
- if clip_period:
529
- console.print(f" • Clip period: {clip_period}")
546
+ console.print(f" • Move energy cost: {game_config.game.actions.move.consumed_resources.get('energy', 0)}")
530
547
 
531
548
  # Display available actions
532
549
  console.print("\n[bold]Available Actions:[/bold]")
@@ -536,14 +553,8 @@ def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cf
536
553
 
537
554
  # Display objects
538
555
  console.print("\n[bold]Stations:[/bold]")
539
- for obj_name, obj_config in game_config.game.objects.items():
556
+ for obj_name, _obj_config in game_config.game.objects.items():
540
557
  console.print(f" • {obj_name}")
541
- if isinstance(obj_config, AssemblerConfig):
542
- for protocol in obj_config.protocols:
543
- if protocol.input_resources:
544
- inputs = ", ".join(f"{k}:{v}" for k, v in protocol.input_resources.items())
545
- outputs = ", ".join(f"{k}:{v}" for k, v in protocol.output_resources.items())
546
- console.print(f" {inputs} → {outputs} (cooldown: {protocol.cooldown})")
547
558
 
548
559
  # Display agent configuration
549
560
  console.print("\n[bold]Agent Configuration:[/bold]")
cogames/cli/policy.py CHANGED
@@ -19,8 +19,8 @@ ParsedPolicies = list[PolicySpec]
19
19
 
20
20
  default_checkpoint_dir = Path("train_dir")
21
21
 
22
- policy_arg_example = "URI (bundle dir or .zip) or class=NAME[,data=FILE][,kw.x=val]"
23
- policy_arg_w_proportion_example = "URI[,proportion=N] or class=NAME[,data=FILE][,proportion=N]"
22
+ policy_arg_example = "URI (bundle dir or .zip) or NAME or class=NAME[,data=FILE][,kw.x=val]"
23
+ policy_arg_w_proportion_example = "URI[,proportion=N] or NAME or class=NAME[,data=FILE][,proportion=N]"
24
24
 
25
25
 
26
26
  class PolicySpecWithProportion(PolicySpec):
@@ -74,7 +74,7 @@ def get_policy_spec(ctx: typer.Context, policy_arg: Optional[str]) -> PolicySpec
74
74
  console.print("[yellow]Missing: --policy / -p[/yellow]\n")
75
75
  else:
76
76
  try:
77
- return _parse_policy_spec(spec=policy_arg).to_policy_spec()
77
+ return parse_policy_spec(spec=policy_arg).to_policy_spec()
78
78
  except (ValueError, ModuleNotFoundError) as e:
79
79
  translated = _translate_error(e)
80
80
  console.print(f"[yellow]Error parsing policy argument: {translated}[/yellow]\n")
@@ -97,7 +97,7 @@ def get_policy_specs_with_proportions(
97
97
  console.print("[yellow]Supply at least one: --policy / -p[/yellow]\n")
98
98
  else:
99
99
  try:
100
- return [_parse_policy_spec(spec=policy_arg) for policy_arg in policy_args]
100
+ return [parse_policy_spec(spec=policy_arg) for policy_arg in policy_args]
101
101
  except (ValueError, ModuleNotFoundError) as e:
102
102
  translated = _translate_error(e)
103
103
  console.print(f"[yellow]Error parsing policy argument: {translated}[/yellow]")
@@ -112,16 +112,13 @@ def get_policy_specs_with_proportions(
112
112
  raise typer.Exit(1)
113
113
 
114
114
 
115
- def _parse_policy_spec(spec: str) -> PolicySpecWithProportion:
115
+ def parse_policy_spec(spec: str) -> PolicySpecWithProportion:
116
116
  """Parse a policy CLI option into its components.
117
117
 
118
118
  Supports two formats:
119
- - class=...[,data=...][,proportion=1.0][,kw.<key>=<value>]
119
+ - NAME (short name) or class=...[,data=...][,proportion=1.0][,kw.<key>=<value>]
120
120
  - URI: metta://policy/xxx[,proportion=1.0]
121
121
  """
122
- entries = [part.strip() for part in spec.split(",") if part.strip()]
123
- if not entries:
124
- raise ValueError("Policy specification cannot be empty.")
125
122
 
126
123
  def parse_key_value(entry: str) -> tuple[str, str]:
127
124
  if "=" not in entry:
@@ -142,9 +139,43 @@ def _parse_policy_spec(spec: str) -> PolicySpecWithProportion:
142
139
  raise ValueError("Policy proportion must be a positive number.")
143
140
  return fraction
144
141
 
142
+ def is_path_like(value: str) -> bool:
143
+ if value.startswith((".", "/", "~")):
144
+ return True
145
+ return len(value) >= 3 and value[0].isalpha() and value[1] == ":" and value[2] in ("/", "\\")
146
+
147
+ def is_uri(value: str) -> bool:
148
+ # Check for URI schemes before checking for '=' to handle query strings
149
+ if value.startswith("metta://"):
150
+ return True
151
+ return parse_uri(value, allow_none=True, default_scheme=None) is not None
152
+
153
+ # For metta:// URIs, we need to handle commas in query strings specially.
154
+ # Split on ",proportion=" to separate the URI from the proportion suffix.
155
+ if spec.startswith("metta://"):
156
+ proportion_marker = ",proportion="
157
+ if proportion_marker in spec:
158
+ uri_part, proportion_value = spec.split(proportion_marker, 1)
159
+ fraction = parse_proportion(proportion_value.strip())
160
+ else:
161
+ uri_part = spec
162
+ fraction = 1.0
163
+
164
+ policy = policy_spec_from_uri(uri_part.strip())
165
+ return PolicySpecWithProportion(
166
+ class_path=policy.class_path,
167
+ data_path=policy.data_path,
168
+ proportion=fraction,
169
+ init_kwargs=policy.init_kwargs,
170
+ )
171
+
172
+ entries = [part.strip() for part in spec.split(",") if part.strip()]
173
+ if not entries:
174
+ raise ValueError("Policy specification cannot be empty.")
175
+
145
176
  fraction = 1.0
146
177
  first = entries[0]
147
- if parse_uri(first, allow_none=True, default_scheme=None):
178
+ if is_uri(first) or ("=" not in first and is_path_like(first)):
148
179
  policy = policy_spec_from_uri(first)
149
180
  for entry in entries[1:]:
150
181
  key, value = parse_key_value(entry)
@@ -159,6 +190,11 @@ def _parse_policy_spec(spec: str) -> PolicySpecWithProportion:
159
190
  init_kwargs=policy.init_kwargs,
160
191
  )
161
192
 
193
+ if "=" not in first:
194
+ if ":" in first:
195
+ raise ValueError("Policy shorthand cannot include ':'; use class=... or a supported URI instead.")
196
+ entries[0] = f"class={first}"
197
+
162
198
  class_path: str | None = None
163
199
  data_path: str | None = None
164
200
  init_kwargs: dict[str, str] = {}