cogames 0.3.64__py3-none-any.whl → 0.3.65__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 (31) hide show
  1. cogames/cli/mission.py +25 -35
  2. cogames/cli/submit.py +1 -1
  3. cogames/cogs_vs_clips/clips.py +86 -0
  4. cogames/cogs_vs_clips/cog.py +14 -7
  5. cogames/cogs_vs_clips/cogsguard_tutorial.py +10 -11
  6. cogames/cogs_vs_clips/config.py +38 -0
  7. cogames/cogs_vs_clips/{cogs_vs_clips_mapgen.md → docs/cogs_vs_clips_mapgen.md} +6 -7
  8. cogames/cogs_vs_clips/evals/README.md +4 -4
  9. cogames/cogs_vs_clips/evals/cogsguard_evals.py +21 -6
  10. cogames/cogs_vs_clips/evals/diagnostic_evals.py +13 -100
  11. cogames/cogs_vs_clips/evals/difficulty_variants.py +9 -18
  12. cogames/cogs_vs_clips/evals/integrated_evals.py +8 -60
  13. cogames/cogs_vs_clips/evals/spanning_evals.py +48 -54
  14. cogames/cogs_vs_clips/mission.py +65 -277
  15. cogames/cogs_vs_clips/missions.py +16 -26
  16. cogames/cogs_vs_clips/sites.py +35 -25
  17. cogames/cogs_vs_clips/stations.py +33 -82
  18. cogames/cogs_vs_clips/team.py +44 -0
  19. cogames/cogs_vs_clips/{procedural.py → terrain.py} +12 -6
  20. cogames/cogs_vs_clips/variants.py +41 -118
  21. cogames/core.py +87 -0
  22. cogames/verbose.py +2 -2
  23. {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/METADATA +2 -2
  24. {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/RECORD +28 -27
  25. cogames/cogs_vs_clips/cogsguard_reward_variants.py +0 -153
  26. cogames/cogs_vs_clips/mission_utils.py +0 -19
  27. cogames/cogs_vs_clips/tutorial_missions.py +0 -25
  28. {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/WHEEL +0 -0
  29. {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/entry_points.txt +0 -0
  30. {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/licenses/LICENSE +0 -0
  31. {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/top_level.txt +0 -0
cogames/cli/mission.py CHANGED
@@ -9,51 +9,43 @@ from rich.table import Table
9
9
 
10
10
  from cogames.cli.base import console
11
11
  from cogames.cogs_vs_clips.mission import (
12
- MAP_MISSION_DELIMITER,
13
- AnyMission,
14
- Mission,
15
- MissionVariant,
12
+ CvCMission,
16
13
  NumCogsVariant,
17
- Site,
18
14
  )
19
- from cogames.cogs_vs_clips.procedural import MachinaArena
20
15
  from cogames.cogs_vs_clips.sites import SITES
16
+ from cogames.cogs_vs_clips.terrain import MachinaArena
21
17
  from cogames.cogs_vs_clips.variants import HIDDEN_VARIANTS, VARIANTS
18
+ from cogames.core import (
19
+ MAP_MISSION_DELIMITER,
20
+ CoGameMissionVariant,
21
+ CoGameSite,
22
+ )
22
23
  from cogames.game import load_mission_config, load_mission_config_from_python
23
24
  from mettagrid import MettaGridConfig
24
25
  from mettagrid.mapgen.mapgen import MapGen
25
26
 
26
27
 
27
28
  @lru_cache(maxsize=1)
28
- def _get_core_missions() -> list[AnyMission]:
29
+ def _get_core_missions() -> list[CvCMission]:
29
30
  from cogames.cogs_vs_clips.missions import get_core_missions
30
31
 
31
32
  return get_core_missions()
32
33
 
33
34
 
34
35
  @lru_cache(maxsize=1)
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
36
+ def _get_eval_missions_all() -> list[CvCMission]:
44
37
  from cogames.cogs_vs_clips.evals.diagnostic_evals import DIAGNOSTIC_EVALS
45
38
  from cogames.cogs_vs_clips.evals.integrated_evals import EVAL_MISSIONS as INTEGRATED_EVAL_MISSIONS
46
39
  from cogames.cogs_vs_clips.evals.spanning_evals import EVAL_MISSIONS as SPANNING_EVAL_MISSIONS
47
40
 
48
- missions: list[AnyMission] = []
49
- missions.extend(COGSGUARD_EVAL_MISSIONS)
41
+ missions: list[CvCMission] = []
50
42
  missions.extend(INTEGRATED_EVAL_MISSIONS)
51
43
  missions.extend(SPANNING_EVAL_MISSIONS)
52
44
  missions.extend(mission_cls() for mission_cls in DIAGNOSTIC_EVALS) # type: ignore[call-arg]
53
45
  return missions
54
46
 
55
47
 
56
- def load_mission_set(mission_set: str) -> list[AnyMission]:
48
+ def load_mission_set(mission_set: str) -> list[CvCMission]:
57
49
  """Load a predefined set of evaluation missions.
58
50
 
59
51
  Args:
@@ -70,7 +62,7 @@ def load_mission_set(mission_set: str) -> list[AnyMission]:
70
62
  Raises:
71
63
  ValueError: If mission_set name is unknown
72
64
  """
73
- missions_list: list[AnyMission]
65
+ missions_list: list[CvCMission]
74
66
  if mission_set == "all":
75
67
  # All missions: eval missions + integrated + spanning + diagnostic + core missions
76
68
  missions_list = list(_get_eval_missions_all())
@@ -104,14 +96,14 @@ def load_mission_set(mission_set: str) -> list[AnyMission]:
104
96
  return missions_list
105
97
 
106
98
 
107
- def parse_variants(variants_arg: Optional[list[str]]) -> list[MissionVariant]:
99
+ def parse_variants(variants_arg: Optional[list[str]]) -> list[CoGameMissionVariant]:
108
100
  """Parse variant specifications from command line.
109
101
 
110
102
  Args:
111
103
  variants_arg: List of variant names like ["solar_flare", "dark_side"]
112
104
 
113
105
  Returns:
114
- List of configured MissionVariant instances
106
+ List of configured CoGameMissionVariant instances
115
107
 
116
108
  Raises:
117
109
  ValueError: If variant name is unknown
@@ -119,11 +111,11 @@ def parse_variants(variants_arg: Optional[list[str]]) -> list[MissionVariant]:
119
111
  if not variants_arg:
120
112
  return []
121
113
 
122
- variants: list[MissionVariant] = []
114
+ variants: list[CoGameMissionVariant] = []
123
115
  all_variants = [*VARIANTS, *HIDDEN_VARIANTS]
124
116
  for name in variants_arg:
125
117
  # Find matching variant class by instantiating and checking the name
126
- variant: MissionVariant | None = None
118
+ variant: CoGameMissionVariant | None = None
127
119
  for v in all_variants:
128
120
  if v.name == name:
129
121
  variant = v
@@ -150,7 +142,7 @@ def get_all_eval_missions() -> list[str]:
150
142
  return [mission.full_name() for mission in _get_eval_missions_all()]
151
143
 
152
144
 
153
- def get_site_by_name(site_name: str) -> Site:
145
+ def get_site_by_name(site_name: str) -> CoGameSite:
154
146
  """Get a site by name.
155
147
 
156
148
  Raises:
@@ -166,7 +158,7 @@ def get_site_by_name(site_name: str) -> Site:
166
158
 
167
159
  def get_mission_name_and_config(
168
160
  ctx: typer.Context, mission_arg: Optional[str], variants_arg: Optional[list[str]] = None, cogs: Optional[int] = None
169
- ) -> tuple[str, MettaGridConfig, Optional[AnyMission]]:
161
+ ) -> tuple[str, MettaGridConfig, Optional[CvCMission]]:
170
162
  if not mission_arg:
171
163
  console.print(ctx.get_help())
172
164
  console.print("[yellow]Missing: --mission / -m[/yellow]\n")
@@ -253,12 +245,10 @@ def find_mission(
253
245
  *,
254
246
  include_evals: bool = False,
255
247
  include_legacy: bool = False,
256
- ) -> AnyMission:
257
- missions: list[AnyMission] = list(_get_core_missions())
248
+ ) -> CvCMission:
249
+ missions: list[CvCMission] = list(_get_core_missions())
258
250
  if include_evals:
259
251
  missions = [*missions, *_get_eval_missions_all()]
260
- if include_legacy:
261
- missions = [*missions, *_get_legacy_missions()]
262
252
 
263
253
  found_site = False
264
254
  for mission in missions:
@@ -288,7 +278,7 @@ def get_mission(
288
278
  variants_arg: Optional[list[str]] = None,
289
279
  cogs: Optional[int] = None,
290
280
  include_legacy: bool = False,
291
- ) -> tuple[str, MettaGridConfig, Optional[AnyMission]]:
281
+ ) -> tuple[str, MettaGridConfig, Optional[CvCMission]]:
292
282
  """Get a specific mission configuration by name or file path.
293
283
 
294
284
  Args:
@@ -298,7 +288,7 @@ def get_mission(
298
288
  include_legacy: Whether to include legacy (pre-CogsGuard) missions
299
289
 
300
290
  Returns:
301
- Tuple of (mission name, MettaGridConfig, Mission or None)
291
+ Tuple of (mission name, MettaGridConfig, CvCMission or None)
302
292
 
303
293
  Raises:
304
294
  ValueError: If mission not found or file cannot be loaded
@@ -330,7 +320,7 @@ def get_mission(
330
320
  else:
331
321
  site_name, mission_name = mission_arg.split(MAP_MISSION_DELIMITER)
332
322
 
333
- mission: AnyMission = find_mission(site_name, mission_name, include_evals=True, include_legacy=include_legacy)
323
+ mission: CvCMission = find_mission(site_name, mission_name, include_evals=True, include_legacy=include_legacy)
334
324
 
335
325
  if variants:
336
326
  mission = mission.with_variants(variants)
@@ -454,7 +444,7 @@ def list_evals() -> None:
454
444
  return
455
445
 
456
446
  # Group missions by site
457
- missions_by_site: dict[str, list[AnyMission]] = {}
447
+ missions_by_site: dict[str, list[CvCMission]] = {}
458
448
  for m in evals:
459
449
  missions_by_site.setdefault(m.site.name, []).append(m)
460
450
 
@@ -506,7 +496,7 @@ def list_evals() -> None:
506
496
  console.print(" [bold]cogames play[/bold] --mission [blue]evals.divide_and_conquer[/blue]")
507
497
 
508
498
 
509
- def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cfg: AnyMission | None = None) -> None:
499
+ def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cfg: CvCMission | None = None) -> None:
510
500
  """Print detailed information about a specific mission.
511
501
 
512
502
  Args:
cogames/cli/submit.py CHANGED
@@ -290,6 +290,7 @@ def validate_policy_in_isolation(
290
290
  "run",
291
291
  "cogames",
292
292
  "validate-policy",
293
+ "--policy",
293
294
  policy_arg,
294
295
  "--season",
295
296
  season,
@@ -298,7 +299,6 @@ def validate_policy_in_isolation(
298
299
  ]
299
300
  if setup_script:
300
301
  validate_cmd.extend(["--setup-script", setup_script])
301
- validate_cmd.extend(["--policy", policy_arg])
302
302
 
303
303
  _run_from_tmp_dir(validate_cmd)
304
304
 
@@ -0,0 +1,86 @@
1
+ """Clips behavior events for CogsGuard missions.
2
+
3
+ Clips are a non-player faction that gradually takes over neutral junctions.
4
+ These events create the spreading/scrambling behavior that pressures players.
5
+ """
6
+
7
+ from pydantic import Field
8
+
9
+ from mettagrid.base_config import Config
10
+ from mettagrid.config.event_config import EventConfig, once, periodic
11
+ from mettagrid.config.filter import isAlignedTo, isNear
12
+ from mettagrid.config.filter.alignment_filter import isNeutral, isNotAlignedTo, isNotNeutral
13
+ from mettagrid.config.mettagrid_config import CollectiveConfig
14
+ from mettagrid.config.mutation import alignTo, removeAlignment
15
+ from mettagrid.config.tag import typeTag
16
+
17
+
18
+ class ClipsConfig(Config):
19
+ """Configuration for clips behavior in CogsGuard game mode."""
20
+
21
+ # Clips Behavior - scramble cogs junctions to neutral
22
+ initial_clips_start: int = Field(default=10)
23
+ initial_clips_spots: int = Field(default=1)
24
+
25
+ scramble_start: int = Field(default=50)
26
+ scramble_interval: int = Field(default=100)
27
+ scramble_radius: int = Field(default=25)
28
+
29
+ # Clips Behavior - align neutral junctions to clips
30
+ align_start: int = Field(default=100)
31
+ align_interval: int = Field(default=100)
32
+ align_radius: int = Field(default=25)
33
+
34
+ def events(self, max_steps: int) -> dict[str, EventConfig]:
35
+ """Create all clips events for a mission.
36
+
37
+ Returns:
38
+ Dictionary of event name to EventConfig.
39
+ """
40
+ return {
41
+ "initial_clips": EventConfig(
42
+ name="initial_clips",
43
+ target_tag=typeTag("junction"),
44
+ timesteps=once(self.initial_clips_start),
45
+ mutations=[alignTo("clips")],
46
+ max_targets=self.initial_clips_spots,
47
+ ),
48
+ "cogs_to_neutral": EventConfig(
49
+ name="cogs_to_neutral",
50
+ target_tag=typeTag("junction"),
51
+ timesteps=periodic(start=self.scramble_start, period=self.scramble_interval, end=max_steps),
52
+ # near a clips-aligned junction
53
+ filters=[
54
+ isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.scramble_radius),
55
+ isNotAlignedTo("clips"),
56
+ isNotNeutral(),
57
+ ],
58
+ mutations=[removeAlignment()],
59
+ max_targets=1,
60
+ ),
61
+ "neutral_to_clips": EventConfig(
62
+ name="neutral_to_clips",
63
+ target_tag=typeTag("junction"),
64
+ timesteps=periodic(start=self.align_start, period=self.align_interval, end=max_steps),
65
+ # neutral junctions near a clips-aligned junction
66
+ filters=[
67
+ isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.align_radius),
68
+ isNeutral(),
69
+ ],
70
+ mutations=[alignTo("clips")],
71
+ max_targets=1,
72
+ ),
73
+ # If there are no clips-aligned junctions, re-invade
74
+ "presence_check": EventConfig(
75
+ name="presence_check",
76
+ target_tag=typeTag("junction"),
77
+ timesteps=periodic(start=self.initial_clips_start, period=self.scramble_interval * 2, end=max_steps),
78
+ filters=[isNear(typeTag("junction"), [isAlignedTo("clips")], radius=1000)],
79
+ max_targets=1,
80
+ fallback="initial_clips",
81
+ ),
82
+ }
83
+
84
+ def collective_config(self) -> CollectiveConfig:
85
+ """Create a CollectiveConfig for this clips configuration."""
86
+ return CollectiveConfig(name="clips")
@@ -2,7 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  from pydantic import Field
4
4
 
5
+ from cogames.cogs_vs_clips.config import CvCConfig
5
6
  from mettagrid.base_config import Config
7
+ from mettagrid.config.game_value import stat as game_stat
6
8
  from mettagrid.config.handler_config import Handler
7
9
  from mettagrid.config.mettagrid_config import (
8
10
  AgentConfig,
@@ -10,6 +12,7 @@ from mettagrid.config.mettagrid_config import (
10
12
  ResourceLimitsConfig,
11
13
  )
12
14
  from mettagrid.config.mutation.resource_mutation import updateActor
15
+ from mettagrid.config.reward_config import reward
13
16
 
14
17
 
15
18
  class CogConfig(Config):
@@ -37,25 +40,23 @@ class CogConfig(Config):
37
40
  energy_regen: int = Field(default=1)
38
41
  hp_regen: int = Field(default=-1)
39
42
  influence_regen: int = Field(default=-1)
43
+ action_cost: dict[str, int] = Field(default_factory=lambda: {"energy": 3})
40
44
 
41
- # Movement cost
42
- move_energy_cost: int = Field(default=3)
43
-
44
- def agent_config(self, gear: list[str], elements: list[str]) -> AgentConfig:
45
+ def agent_config(self, team: str, max_steps: int) -> AgentConfig:
45
46
  """Create an AgentConfig for this cog configuration."""
46
47
  return AgentConfig(
47
- collective="cogs",
48
+ collective=team,
48
49
  inventory=InventoryConfig(
49
50
  limits={
50
51
  "hp": ResourceLimitsConfig(min=self.hp_limit, resources=["hp"], modifiers=self.hp_modifiers),
51
52
  # when hp == 0, the cog can't hold gear or hearts
52
- "gear": ResourceLimitsConfig(max=self.gear_limit, resources=gear, modifiers={"hp": 100}),
53
+ "gear": ResourceLimitsConfig(max=self.gear_limit, resources=CvCConfig.GEAR, modifiers={"hp": 100}),
53
54
  "heart": ResourceLimitsConfig(max=self.heart_limit, resources=["heart"], modifiers={"hp": 100}),
54
55
  "energy": ResourceLimitsConfig(
55
56
  min=self.energy_limit, resources=["energy"], modifiers=self.energy_modifiers
56
57
  ),
57
58
  "cargo": ResourceLimitsConfig(
58
- min=self.cargo_limit, resources=elements, modifiers=self.cargo_modifiers
59
+ min=self.cargo_limit, resources=CvCConfig.ELEMENTS, modifiers=self.cargo_modifiers
59
60
  ),
60
61
  "influence": ResourceLimitsConfig(
61
62
  min=self.influence_limit, resources=["influence"], modifiers=self.influence_modifiers
@@ -76,4 +77,10 @@ class CogConfig(Config):
76
77
  ]
77
78
  )
78
79
  },
80
+ rewards={
81
+ "aligned_junction_held": reward(
82
+ game_stat("collective.aligned.junction.held"),
83
+ weight=1.0 / max_steps,
84
+ ),
85
+ },
79
86
  )
@@ -1,12 +1,14 @@
1
1
  """CogsGuard tutorial mission configuration."""
2
2
 
3
- from cogames.cogs_vs_clips.mission import Mission, Site
4
- from cogames.cogs_vs_clips.procedural import MachinaArena
3
+ from cogames.cogs_vs_clips.mission import CvCMission
4
+ from cogames.cogs_vs_clips.team import CogTeam
5
+ from cogames.cogs_vs_clips.terrain import MachinaArena
6
+ from cogames.core import CoGameSite
5
7
  from mettagrid.mapgen.mapgen import MapGen
6
8
  from mettagrid.mapgen.scenes.base_hub import BaseHubConfig
7
9
 
8
10
 
9
- def make_cogsguard_tutorial_site() -> Site:
11
+ def make_cogsguard_tutorial_site() -> CoGameSite:
10
12
  """Create a smaller, simpler CogsGuard arena for the tutorial."""
11
13
  hub_config = BaseHubConfig(
12
14
  corner_bundle="extractors",
@@ -32,7 +34,7 @@ def make_cogsguard_tutorial_site() -> Site:
32
34
  hub=hub_config,
33
35
  ),
34
36
  )
35
- return Site(
37
+ return CoGameSite(
36
38
  name="cogsguard_tutorial",
37
39
  description="CogsGuard tutorial arena - small map for learning",
38
40
  map_builder=map_builder,
@@ -41,16 +43,13 @@ def make_cogsguard_tutorial_site() -> Site:
41
43
  )
42
44
 
43
45
 
44
- CogsGuardTutorialMission = Mission(
46
+ CogsGuardTutorialMission = CvCMission(
45
47
  name="tutorial",
46
48
  description="Learn the basics of CogsGuard: Roles, Resources, and Territory Control.",
47
49
  site=make_cogsguard_tutorial_site(),
48
50
  num_cogs=1,
49
51
  max_steps=2000,
50
- # Generous initial resources for learning
51
- collective_initial_carbon=50,
52
- collective_initial_oxygen=50,
53
- collective_initial_germanium=50,
54
- collective_initial_silicon=50,
55
- collective_initial_heart=10,
52
+ teams={
53
+ "cogs": CogTeam(name="cogs", num_agents=1, wealth=1),
54
+ },
56
55
  )
@@ -0,0 +1,38 @@
1
+ from types import SimpleNamespace
2
+
3
+ from mettagrid.config.vibes import Vibe
4
+
5
+ _GEAR = ["aligner", "scrambler", "miner", "scout"]
6
+ _ELEMENTS = ["oxygen", "carbon", "germanium", "silicon"]
7
+ _VIBES = [
8
+ Vibe("😐", "default"),
9
+ Vibe("❤️", "heart"),
10
+ Vibe("⚙️", "gear"),
11
+ Vibe("🌀", "scrambler"),
12
+ Vibe("🔗", "aligner"),
13
+ Vibe("⛏️", "miner"),
14
+ Vibe("🔭", "scout"),
15
+ ]
16
+
17
+ CvCConfig = SimpleNamespace(
18
+ GEAR=_GEAR,
19
+ ELEMENTS=_ELEMENTS,
20
+ HEART_COST={e: 10 for e in _ELEMENTS},
21
+ ALIGN_COST={"heart": 1},
22
+ SCRAMBLE_COST={"heart": 1},
23
+ GEAR_COSTS={
24
+ "aligner": {"carbon": 3, "oxygen": 1, "germanium": 1, "silicon": 1},
25
+ "scrambler": {"carbon": 1, "oxygen": 3, "germanium": 1, "silicon": 1},
26
+ "miner": {"carbon": 1, "oxygen": 1, "germanium": 3, "silicon": 1},
27
+ "scout": {"carbon": 1, "oxygen": 1, "germanium": 1, "silicon": 3},
28
+ },
29
+ GEAR_SYMBOLS={
30
+ "aligner": "🔗",
31
+ "scrambler": "🌀",
32
+ "miner": "⛏️",
33
+ "scout": "🔭",
34
+ },
35
+ RESOURCES=["energy", "heart", "hp", "influence", *_ELEMENTS, *_GEAR],
36
+ VIBES=_VIBES,
37
+ VIBE_NAMES=[vibe.name for vibe in _VIBES],
38
+ )
@@ -324,7 +324,6 @@ Key design choices:
324
324
 
325
325
  - Pack the base hub lightly (`EmptyBaseVariant`) where appropriate to encourage early exploration without
326
326
  over-constraining.
327
- - Add guidance (`CompassVariant`) on distance-heavy tasks to reduce pure exploration failure modes.
328
327
  - Raise agent caps modestly (`PackRatVariant`) to avoid early inventory stalls but keep routing relevant.
329
328
  - Shape reward on vibe missions (`HeartChorusVariant`) so partial progress is scored.
330
329
  - Keep vibe mechanics intact unless the mission explicitly focuses on vibe manipulation.
@@ -334,9 +333,9 @@ Included missions and variants:
334
333
  - oxygen_bottleneck: `EmptyBaseVariant(missing=["oxygen_extractor"])`, `ResourceBottleneckVariant(["oxygen"])`,
335
334
  `SingleResourceUniformVariant("oxygen_extractor")`, `PackRatVariant`
336
335
  - energy_starved: `EmptyBaseVariant`, `DarkSideVariant`, `PackRatVariant`
337
- - distant_resources: `EmptyBaseVariant`, `CompassVariant`, `DistantResourcesVariant`
338
- - quadrant_buildings: `EmptyBaseVariant`, `QuadrantBuildingsVariant`, `CompassVariant`
339
- - single_use_swarm: `EmptyBaseVariant`, `SingleUseSwarmVariant`, `CompassVariant`, `PackRatVariant`
336
+ - distant_resources: `EmptyBaseVariant`, `DistantResourcesVariant`
337
+ - quadrant_buildings: `EmptyBaseVariant`, `QuadrantBuildingsVariant`
338
+ - single_use_swarm: `EmptyBaseVariant`, `SingleUseSwarmVariant`, `PackRatVariant`
340
339
  - vibe_check: `HeartChorusVariant`, `VibeCheckMin2Variant`
341
340
 
342
341
  Usage example:
@@ -349,6 +348,6 @@ uv run python packages/cogames/scripts/run_evaluation.py \
349
348
  --repeats 2
350
349
  ```
351
350
 
352
- Recommendation: When designing new scorable baselines, combine one shaping variant (e.g., `CompassVariant`,
353
- `HeartChorusVariant`, `PackRatVariant`) with one constraint variant (e.g., `DarkSideVariant`,
354
- `ResourceBottleneckVariant`, `SingleUseSwarmVariant`) to keep tasks legible yet challenging.
351
+ Recommendation: When designing new scorable baselines, combine one "shaping" variant (e.g., `HeartChorusVariant`,
352
+ `PackRatVariant`) with one "constraint" variant (e.g., `DarkSideVariant`, `ResourceBottleneckVariant`,
353
+ `SingleUseSwarmVariant`) to keep tasks legible yet challenging.
@@ -179,11 +179,11 @@ uv run cogames play --mission hello_world.easy_hearts --cogs 2
179
179
  You can apply additional variants on top of the mission's built-in variants:
180
180
 
181
181
  ```bash
182
- # Add compass variant
183
- uv run cogames play --mission hello_world.oxygen_bottleneck --cogs 2 --variant compass
182
+ # Add pack_rat variant
183
+ uv run cogames play --mission hello_world.oxygen_bottleneck --cogs 2 --variant pack_rat
184
184
 
185
185
  # Add multiple variants
186
- uv run cogames play --mission hello_world.energy_starved --cogs 2 --variant compass --variant small_50
186
+ uv run cogames play --mission hello_world.energy_starved --cogs 2 --variant pack_rat --variant small_50
187
187
 
188
188
  # With policy
189
189
  uv run cogames play --mission hello_world.single_use_swarm --cogs 4 -p baseline
@@ -230,7 +230,7 @@ mission_variant_curriculum.train(
230
230
  mission_variant_curriculum.train(
231
231
  base_missions=["oxygen_bottleneck", "energy_starved"],
232
232
  num_cogs=4,
233
- variants=["compass", "pack_rat"]
233
+ variants=["pack_rat", "energized"]
234
234
  )
235
235
  ```
236
236
 
@@ -2,15 +2,30 @@ from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- from cogames.cogs_vs_clips.mission import CogsGuardMission, Site
6
- from cogames.cogs_vs_clips.mission_utils import get_map
5
+ from cogames.cogs_vs_clips.mission import CvCMission
6
+ from cogames.core import CoGameSite as Site
7
+ from mettagrid.map_builder.map_builder import MapBuilderConfig
8
+ from mettagrid.mapgen.mapgen import MapGen, MapGenConfig
7
9
 
8
10
  MAPS_DIR = Path(__file__).resolve().parent.parent.parent / "maps"
9
11
 
12
+
13
+ def _load_map(map_name: str) -> MapGenConfig:
14
+ map_path = MAPS_DIR / map_name
15
+ if not map_path.exists():
16
+ raise FileNotFoundError(f"Map not found: {map_path}")
17
+ return MapGen.Config(
18
+ instance=MapBuilderConfig.from_uri(str(map_path)),
19
+ instances=1,
20
+ fixed_spawn_order=False,
21
+ instance_border_width=0,
22
+ )
23
+
24
+
10
25
  COGSGUARD_EVALS_BASE = Site(
11
26
  name="cogsguard_evals",
12
27
  description="CogsGuard evaluation arenas.",
13
- map_builder=get_map("evals/eval_balanced_spread.map"),
28
+ map_builder=_load_map("evals/eval_balanced_spread.map"),
14
29
  min_cogs=1,
15
30
  max_cogs=20,
16
31
  )
@@ -30,7 +45,7 @@ def _count_spawn_pads(map_path: Path) -> int:
30
45
  def _make_eval_site(map_name: str, num_cogs: int) -> Site:
31
46
  site = COGSGUARD_EVALS_BASE.model_copy(
32
47
  update={
33
- "map_builder": get_map(map_name),
48
+ "map_builder": _load_map(map_name),
34
49
  "min_cogs": num_cogs,
35
50
  "max_cogs": num_cogs,
36
51
  }
@@ -66,13 +81,13 @@ COGSGUARD_EVAL_MAPS: list[str] = [
66
81
 
67
82
  COGSGUARD_EVAL_COGS = {map_name: _count_spawn_pads(MAPS_DIR / map_name) for map_name in COGSGUARD_EVAL_MAPS}
68
83
 
69
- COGSGUARD_EVAL_MISSIONS: list[CogsGuardMission] = []
84
+ COGSGUARD_EVAL_MISSIONS: list[CvCMission] = []
70
85
  for map_name in COGSGUARD_EVAL_MAPS:
71
86
  stem = Path(map_name).stem
72
87
  num_cogs = COGSGUARD_EVAL_COGS[map_name]
73
88
  site = _make_eval_site(map_name, num_cogs)
74
89
  COGSGUARD_EVAL_MISSIONS.append(
75
- CogsGuardMission(
90
+ CvCMission(
76
91
  name=stem,
77
92
  description=_description_from_stem(stem),
78
93
  site=site,