cogames 0.3.59.post1.dev2__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 (49) hide show
  1. cogames/cli/client.py +12 -0
  2. cogames/cli/leaderboard.py +40 -0
  3. cogames/cli/mission.py +31 -34
  4. cogames/cli/submit.py +1 -1
  5. cogames/cogs_vs_clips/clips.py +86 -0
  6. cogames/cogs_vs_clips/cog.py +14 -7
  7. cogames/cogs_vs_clips/cogsguard_tutorial.py +10 -11
  8. cogames/cogs_vs_clips/config.py +38 -0
  9. cogames/cogs_vs_clips/{cogs_vs_clips_mapgen.md → docs/cogs_vs_clips_mapgen.md} +6 -7
  10. cogames/cogs_vs_clips/evals/README.md +4 -4
  11. cogames/cogs_vs_clips/evals/cogsguard_evals.py +96 -0
  12. cogames/cogs_vs_clips/evals/diagnostic_evals.py +13 -100
  13. cogames/cogs_vs_clips/evals/difficulty_variants.py +9 -18
  14. cogames/cogs_vs_clips/evals/integrated_evals.py +8 -60
  15. cogames/cogs_vs_clips/evals/spanning_evals.py +48 -54
  16. cogames/cogs_vs_clips/mission.py +65 -277
  17. cogames/cogs_vs_clips/missions.py +16 -24
  18. cogames/cogs_vs_clips/sites.py +35 -25
  19. cogames/cogs_vs_clips/stations.py +33 -82
  20. cogames/cogs_vs_clips/team.py +44 -0
  21. cogames/cogs_vs_clips/{procedural.py → terrain.py} +12 -6
  22. cogames/cogs_vs_clips/variants.py +41 -118
  23. cogames/core.py +87 -0
  24. cogames/main.py +5 -1
  25. cogames/maps/evals/eval_balanced_spread.map +7 -3
  26. cogames/maps/evals/eval_clip_oxygen.map +7 -3
  27. cogames/maps/evals/eval_collect_resources.map +7 -3
  28. cogames/maps/evals/eval_collect_resources_hard.map +7 -3
  29. cogames/maps/evals/eval_collect_resources_medium.map +7 -3
  30. cogames/maps/evals/eval_divide_and_conquer.map +7 -3
  31. cogames/maps/evals/eval_energy_starved.map +7 -3
  32. cogames/maps/evals/eval_multi_coordinated_collect_hard.map +7 -3
  33. cogames/maps/evals/eval_oxygen_bottleneck.map +7 -3
  34. cogames/maps/evals/eval_single_use_world.map +7 -3
  35. cogames/maps/evals/extractor_hub_100x100.map +7 -3
  36. cogames/maps/evals/extractor_hub_30x30.map +7 -3
  37. cogames/maps/evals/extractor_hub_50x50.map +7 -3
  38. cogames/maps/evals/extractor_hub_70x70.map +7 -3
  39. cogames/maps/evals/extractor_hub_80x80.map +7 -3
  40. cogames/verbose.py +2 -2
  41. {cogames-0.3.59.post1.dev2.dist-info → cogames-0.3.65.dist-info}/METADATA +19 -3
  42. {cogames-0.3.59.post1.dev2.dist-info → cogames-0.3.65.dist-info}/RECORD +46 -44
  43. cogames/cogs_vs_clips/cogsguard_reward_variants.py +0 -138
  44. cogames/cogs_vs_clips/mission_utils.py +0 -19
  45. cogames/cogs_vs_clips/tutorial_missions.py +0 -25
  46. {cogames-0.3.59.post1.dev2.dist-info → cogames-0.3.65.dist-info}/WHEEL +0 -0
  47. {cogames-0.3.59.post1.dev2.dist-info → cogames-0.3.65.dist-info}/entry_points.txt +0 -0
  48. {cogames-0.3.59.post1.dev2.dist-info → cogames-0.3.65.dist-info}/licenses/LICENSE +0 -0
  49. {cogames-0.3.59.post1.dev2.dist-info → cogames-0.3.65.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from functools import lru_cache
2
2
 
3
- from cogames.cogs_vs_clips.mission import AnyMission, Mission
3
+ from cogames.cogs_vs_clips.mission import CvCMission
4
4
  from cogames.cogs_vs_clips.sites import (
5
5
  COGSGUARD_ARENA,
6
6
  COGSGUARD_MACHINA_1,
@@ -11,9 +11,9 @@ from mettagrid.config.mettagrid_config import MettaGridConfig
11
11
  # CogsGuard Missions
12
12
 
13
13
 
14
- def make_cogsguard_mission(num_agents: int = 10, max_steps: int = 10000) -> Mission:
14
+ def make_cogsguard_mission(num_agents: int = 10, max_steps: int = 10000) -> CvCMission:
15
15
  """Create a CogsGuard mission with configurable parameters (Machina1 layout)."""
16
- return Mission(
16
+ return CvCMission(
17
17
  name="basic",
18
18
  description="Basic CogsGuard mission (Machina1 layout)",
19
19
  site=make_cogsguard_machina1_site(num_agents),
@@ -22,7 +22,7 @@ def make_cogsguard_mission(num_agents: int = 10, max_steps: int = 10000) -> Miss
22
22
  )
23
23
 
24
24
 
25
- CogsGuardMachina1Mission = Mission(
25
+ CogsGuardMachina1Mission = CvCMission(
26
26
  name="basic",
27
27
  description="CogsGuard Machina1 - compete to control junctions with gear abilities.",
28
28
  site=COGSGUARD_MACHINA_1,
@@ -30,7 +30,7 @@ CogsGuardMachina1Mission = Mission(
30
30
  max_steps=10000,
31
31
  )
32
32
 
33
- CogsGuardBasicMission = Mission(
33
+ CogsGuardBasicMission = CvCMission(
34
34
  name="basic",
35
35
  description="CogsGuard Arena - compact training map with gear abilities.",
36
36
  site=COGSGUARD_ARENA,
@@ -39,25 +39,17 @@ CogsGuardBasicMission = Mission(
39
39
  )
40
40
 
41
41
 
42
- _CORE_MISSIONS: list[AnyMission] = [
42
+ _CORE_MISSIONS: list[CvCMission] = [
43
43
  CogsGuardMachina1Mission,
44
44
  CogsGuardBasicMission,
45
45
  ]
46
46
 
47
47
 
48
- def get_core_missions() -> list[AnyMission]:
48
+ def get_core_missions() -> list[CvCMission]:
49
49
  return list(_CORE_MISSIONS)
50
50
 
51
51
 
52
- def get_legacy_missions() -> list[Mission]:
53
- """Get legacy (pre-CogsGuard) missions for backward compatibility.
54
-
55
- Legacy missions have been removed. Returns empty list.
56
- """
57
- return []
58
-
59
-
60
- def _build_eval_missions() -> list[Mission]:
52
+ def _build_eval_missions() -> list[CvCMission]:
61
53
  from cogames.cogs_vs_clips.evals.integrated_evals import EVAL_MISSIONS as INTEGRATED_EVAL_MISSIONS
62
54
 
63
55
  return [
@@ -66,19 +58,19 @@ def _build_eval_missions() -> list[Mission]:
66
58
 
67
59
 
68
60
  @lru_cache(maxsize=1)
69
- def get_missions() -> list[AnyMission]:
61
+ def get_missions() -> list[CvCMission]:
70
62
  return [*_CORE_MISSIONS, *_build_eval_missions()]
71
63
 
72
64
 
73
- def __getattr__(name: str) -> list[AnyMission]:
65
+ def make_game(num_cogs: int = 2, map_name: str = "training_facility_open_1.map") -> MettaGridConfig:
66
+ """Create a default CogsGuard game configuration."""
67
+ mission = make_cogsguard_mission(num_agents=num_cogs)
68
+ return mission.make_env()
69
+
70
+
71
+ def __getattr__(name: str) -> list[CvCMission]:
74
72
  if name == "MISSIONS":
75
73
  missions = get_missions()
76
74
  globals()["MISSIONS"] = missions
77
75
  return missions
78
76
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
79
-
80
-
81
- def make_game(num_cogs: int = 2, map_name: str = "training_facility_open_1.map") -> MettaGridConfig:
82
- """Create a default CogsGuard game configuration."""
83
- mission = make_cogsguard_mission(num_agents=num_cogs)
84
- return mission.make_env()
@@ -1,14 +1,34 @@
1
1
  """Central site definitions shared across mission modules."""
2
2
 
3
+ from pathlib import Path
3
4
  from typing import cast
4
5
 
5
- from cogames.cogs_vs_clips.mission import Site
6
- from cogames.cogs_vs_clips.mission_utils import get_map
7
- from cogames.cogs_vs_clips.procedural import MachinaArena, RandomTransform, SequentialMachinaArena
6
+ from cogames.cogs_vs_clips.terrain import MachinaArena, RandomTransform, SequentialMachinaArena
7
+ from cogames.core import CoGameSite
8
+ from mettagrid.map_builder.map_builder import MapBuilderConfig
8
9
  from mettagrid.mapgen.mapgen import MapGen, MapGenConfig
9
10
  from mettagrid.mapgen.scenes.base_hub import BaseHub, BaseHubConfig
10
11
 
11
- TRAINING_FACILITY = Site(
12
+ MAPS_DIR = Path(__file__).resolve().parent.parent / "maps"
13
+
14
+
15
+ def get_map(map_name: str) -> MapBuilderConfig:
16
+ """Load a map builder configuration from the maps directory."""
17
+ normalized = map_name
18
+ if normalized.startswith("evals/"):
19
+ normalized = f"diagnostic_evals/{normalized.split('/', 1)[1]}"
20
+ map_path = MAPS_DIR / normalized
21
+ if not map_path.exists():
22
+ raise FileNotFoundError(f"Map not found: {map_path}")
23
+ return MapGen.Config(
24
+ instance=MapBuilderConfig.from_uri(str(map_path)),
25
+ instances=1, # Force single instance - use spawn points from ASCII map directly
26
+ fixed_spawn_order=False,
27
+ instance_border_width=0, # Don't add border - maps already have borders built in
28
+ )
29
+
30
+
31
+ TRAINING_FACILITY = CoGameSite(
12
32
  name="training_facility",
13
33
  description="COG Training Facility. Basic training facility with open spaces and no obstacles.",
14
34
  map_builder=MapGen.Config(
@@ -25,6 +45,7 @@ TRAINING_FACILITY = Site(
25
45
  "silicon_extractor",
26
46
  ],
27
47
  cross_bundle="none",
48
+ junction_object="junction",
28
49
  )
29
50
  ),
30
51
  ),
@@ -32,7 +53,7 @@ TRAINING_FACILITY = Site(
32
53
  max_cogs=4,
33
54
  )
34
55
 
35
- HELLO_WORLD = Site(
56
+ HELLO_WORLD = CoGameSite(
36
57
  name="hello_world",
37
58
  description="Welcome to space.",
38
59
  map_builder=MapGen.Config(width=100, height=100, instance=MachinaArena.Config(spawn_count=20)),
@@ -40,7 +61,7 @@ HELLO_WORLD = Site(
40
61
  max_cogs=20,
41
62
  )
42
63
 
43
- MACHINA_1 = Site(
64
+ MACHINA_1 = CoGameSite(
44
65
  name="machina_1",
45
66
  description="Your first mission. Collect resources and assemble HEARTs.",
46
67
  map_builder=MapGen.Config(width=88, height=88, instance=SequentialMachinaArena.Config(spawn_count=20)),
@@ -54,6 +75,7 @@ def _cogsguard_hub_config() -> BaseHubConfig:
54
75
  corner_bundle="extractors",
55
76
  cross_bundle="none",
56
77
  cross_distance=7,
78
+ junction_object="junction",
57
79
  stations=[
58
80
  "aligner_station",
59
81
  "scrambler_station",
@@ -66,7 +88,7 @@ def _cogsguard_hub_config() -> BaseHubConfig:
66
88
 
67
89
  # Evals site used by diagnostic evaluation missions
68
90
  # Note: Individual diagnostic missions override this with their own specific maps
69
- EVALS = Site(
91
+ EVALS = CoGameSite(
70
92
  name="evals",
71
93
  description="Diagnostic evaluation arenas.",
72
94
  map_builder=get_map("diagnostic_evals/diagnostic_radial.map"), # Default map (rarely used)
@@ -75,7 +97,7 @@ EVALS = Site(
75
97
  )
76
98
 
77
99
 
78
- def make_cogsguard_arena_site(num_agents: int = 10) -> Site:
100
+ def make_cogsguard_arena_site(num_agents: int = 10) -> CoGameSite:
79
101
  """Create a CogsGuard arena site with configurable agent count."""
80
102
  map_builder = MapGen.Config(
81
103
  width=50,
@@ -86,7 +108,7 @@ def make_cogsguard_arena_site(num_agents: int = 10) -> Site:
86
108
  hub=_cogsguard_hub_config(),
87
109
  ),
88
110
  )
89
- return Site(
111
+ return CoGameSite(
90
112
  name="cogsguard_arena",
91
113
  description="CogsGuard arena map",
92
114
  map_builder=map_builder,
@@ -111,9 +133,9 @@ def _build_cogsguard_machina1_map_builder(spawn_count: int) -> MapGenConfig:
111
133
  )
112
134
 
113
135
 
114
- def make_cogsguard_machina1_site(num_agents: int = 10) -> Site:
136
+ def make_cogsguard_machina1_site(num_agents: int = 10) -> CoGameSite:
115
137
  """Create a CogsGuard Machina1 site with configurable agent count."""
116
- return Site(
138
+ return CoGameSite(
117
139
  name="cogsguard_machina_1",
118
140
  description="CogsGuard Machina1 layout with gear stations.",
119
141
  map_builder=_build_cogsguard_machina1_map_builder(num_agents),
@@ -123,7 +145,7 @@ def make_cogsguard_machina1_site(num_agents: int = 10) -> Site:
123
145
 
124
146
 
125
147
  # Default CogsGuard Machina1 site with flexible agent count
126
- COGSGUARD_MACHINA_1 = Site(
148
+ COGSGUARD_MACHINA_1 = CoGameSite(
127
149
  name="cogsguard_machina_1",
128
150
  description="CogsGuard Machina1 layout with gear stations.",
129
151
  map_builder=_build_cogsguard_machina1_map_builder(20),
@@ -132,7 +154,7 @@ COGSGUARD_MACHINA_1 = Site(
132
154
  )
133
155
 
134
156
  # Default CogsGuard arena site with flexible agent count
135
- COGSGUARD_ARENA = Site(
157
+ COGSGUARD_ARENA = CoGameSite(
136
158
  name="cogsguard_arena",
137
159
  description="CogsGuard arena - compact training map with gear stations.",
138
160
  map_builder=MapGen.Config(
@@ -149,20 +171,8 @@ COGSGUARD_ARENA = Site(
149
171
  )
150
172
 
151
173
 
152
- # Feature flag: Set to True to include legacy (pre-CogsGuard) sites in the CLI.
153
- # To enable, add TRAINING_FACILITY, HELLO_WORLD, MACHINA_1 to SITES below.
154
- # Also set _INCLUDE_LEGACY_MISSIONS = True in missions.py.
155
- _INCLUDE_LEGACY_SITES = False
156
-
157
- _LEGACY_SITES = [
158
- TRAINING_FACILITY,
159
- HELLO_WORLD,
160
- MACHINA_1,
161
- ]
162
-
163
174
  SITES = [
164
175
  COGSGUARD_MACHINA_1,
165
176
  COGSGUARD_ARENA,
166
177
  EVALS,
167
- *(_LEGACY_SITES if _INCLUDE_LEGACY_SITES else []),
168
178
  ]
@@ -2,8 +2,8 @@ from typing import Optional
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
6
- from mettagrid.config import vibes
7
7
  from mettagrid.config.handler_config import (
8
8
  AOEConfig,
9
9
  ClearInventoryMutation,
@@ -25,48 +25,11 @@ from mettagrid.config.handler_config import (
25
25
  withdraw,
26
26
  )
27
27
  from mettagrid.config.mettagrid_config import (
28
- ChestConfig,
29
28
  GridObjectConfig,
30
29
  InventoryConfig,
31
30
  WallConfig,
32
31
  )
33
32
 
34
- resources = [
35
- "energy",
36
- "carbon",
37
- "oxygen",
38
- "germanium",
39
- "silicon",
40
- "heart",
41
- "decoder",
42
- "modulator",
43
- "resonator",
44
- "scrambler",
45
- ]
46
-
47
- # CogsGuard constants
48
- GEAR = ["aligner", "scrambler", "miner", "scout"]
49
- ELEMENTS = ["oxygen", "carbon", "germanium", "silicon"]
50
-
51
-
52
- HEART_COST = {e: 10 for e in ELEMENTS}
53
- COGSGUARD_ALIGN_COST = {"heart": 1}
54
- COGSGUARD_SCRAMBLE_COST = {"heart": 1}
55
-
56
- GEAR_COSTS = {
57
- "aligner": {"carbon": 3, "oxygen": 1, "germanium": 1, "silicon": 1},
58
- "scrambler": {"carbon": 1, "oxygen": 3, "germanium": 1, "silicon": 1},
59
- "miner": {"carbon": 1, "oxygen": 1, "germanium": 3, "silicon": 1},
60
- "scout": {"carbon": 1, "oxygen": 1, "germanium": 1, "silicon": 3},
61
- }
62
-
63
- GEAR_SYMBOLS = {
64
- "aligner": "🔗",
65
- "scrambler": "🌀",
66
- "miner": "⛏️",
67
- "scout": "🔭",
68
- }
69
-
70
33
 
71
34
  def _neg(recipe: dict[str, int]) -> dict[str, int]:
72
35
  return {k: -v for k, v in recipe.items()}
@@ -79,15 +42,10 @@ class CvCStationConfig(Config):
79
42
 
80
43
  class CvCWallConfig(CvCStationConfig):
81
44
  def station_cfg(self) -> WallConfig:
82
- return WallConfig(name="wall", render_symbol=vibes.VIBE_BY_NAME["wall"].symbol)
83
-
45
+ return WallConfig(name="wall", render_symbol="")
84
46
 
85
- # ==============================================================================
86
- # CogsGuard Station Configs
87
- # ==============================================================================
88
47
 
89
-
90
- class SimpleExtractorConfig(CvCStationConfig):
48
+ class CvCExtractorConfig(CvCStationConfig):
91
49
  """Simple resource extractor with inventory that transfers resources to actors."""
92
50
 
93
51
  resource: str = Field(description="The resource to extract")
@@ -95,10 +53,9 @@ class SimpleExtractorConfig(CvCStationConfig):
95
53
  small_amount: int = Field(default=1, description="Amount extracted without mining equipment")
96
54
  large_amount: int = Field(default=10, description="Amount extracted with mining equipment")
97
55
 
98
- def station_cfg(self) -> ChestConfig:
99
- return ChestConfig(
56
+ def station_cfg(self) -> GridObjectConfig:
57
+ return GridObjectConfig(
100
58
  name=f"{self.resource}_extractor",
101
- map_name=f"{self.resource}_extractor",
102
59
  render_symbol="📦",
103
60
  on_use_handlers={
104
61
  # Order matters: miner first so agents with miner gear get the bonus
@@ -115,24 +72,18 @@ class SimpleExtractorConfig(CvCStationConfig):
115
72
  )
116
73
 
117
74
 
118
- class JunctionConfig(CvCStationConfig):
75
+ class CvCJunctionConfig(CvCStationConfig):
119
76
  """Supply depot that receives element resources via default vibe into collective."""
120
77
 
121
- map_name: str = Field(description="Map name for this junction")
122
- team: Optional[str] = Field(default=None, description="Team/collective this junction belongs to")
123
78
  aoe_range: int = Field(default=10, description="Range for AOE effects")
124
79
  influence_deltas: dict[str, int] = Field(default_factory=lambda: {"influence": 10, "energy": 100, "hp": 100})
125
80
  attack_deltas: dict[str, int] = Field(default_factory=lambda: {"hp": -1, "influence": -100})
126
- elements: list[str] = Field(default_factory=lambda: ELEMENTS)
127
- align_cost: dict[str, int] = Field(default_factory=lambda: COGSGUARD_ALIGN_COST)
128
- scramble_cost: dict[str, int] = Field(default_factory=lambda: COGSGUARD_SCRAMBLE_COST)
129
81
 
130
- def station_cfg(self) -> GridObjectConfig:
82
+ def station_cfg(self, team: Optional[str] = None) -> GridObjectConfig:
131
83
  return GridObjectConfig(
132
84
  name="junction",
133
- map_name=self.map_name,
134
85
  render_symbol="📦",
135
- collective=self.team,
86
+ collective=team,
136
87
  aoes={
137
88
  "influence": AOEConfig(
138
89
  radius=self.aoe_range,
@@ -148,30 +99,33 @@ class JunctionConfig(CvCStationConfig):
148
99
  on_use_handlers={
149
100
  "deposit": Handler(
150
101
  filters=[isAlignedToActor()],
151
- mutations=[collectiveDeposit({resource: 100 for resource in self.elements})],
102
+ mutations=[collectiveDeposit({resource: 100 for resource in CvCConfig.ELEMENTS})],
152
103
  ),
153
104
  "align": Handler(
154
- filters=[isNeutral(), actorHas({"aligner": 1, "influence": 1, **self.align_cost})],
155
- mutations=[updateActor(_neg(self.align_cost)), alignToActor()],
105
+ filters=[isNeutral(), actorHas({"aligner": 1, "influence": 1, **CvCConfig.ALIGN_COST})],
106
+ mutations=[updateActor(_neg(CvCConfig.ALIGN_COST)), alignToActor()],
156
107
  ),
157
108
  "scramble": Handler(
158
- filters=[isEnemy(), actorHas({"scrambler": 1, **self.scramble_cost})],
159
- mutations=[removeAlignment(), updateActor(_neg(self.scramble_cost))],
109
+ filters=[isEnemy(), actorHas({"scrambler": 1, **CvCConfig.SCRAMBLE_COST})],
110
+ mutations=[removeAlignment(), updateActor(_neg(CvCConfig.SCRAMBLE_COST))],
160
111
  ),
161
112
  },
162
113
  )
163
114
 
164
115
 
165
- class HubConfig(JunctionConfig):
166
- """Main hub with influence AOE effect. A junction without align/scramble handlers."""
116
+ class CvCHubConfig(CvCStationConfig):
117
+ """Hub station that provides AOE influence/attack and accepts deposits."""
167
118
 
168
- def station_cfg(self) -> GridObjectConfig:
119
+ aoe_range: int = Field(default=10, description="Range for AOE effects")
120
+ influence_deltas: dict[str, int] = Field(default_factory=lambda: {"influence": 10, "energy": 100, "hp": 100})
121
+ attack_deltas: dict[str, int] = Field(default_factory=lambda: {"hp": -1, "influence": -100})
122
+ elements: list[str] = Field(default_factory=lambda: CvCConfig.ELEMENTS)
123
+
124
+ def station_cfg(self, team: str) -> GridObjectConfig:
169
125
  return GridObjectConfig(
170
126
  name="hub",
171
- map_name=self.map_name,
172
- render_name="hub",
173
127
  render_symbol="📦",
174
- collective=self.team,
128
+ collective=team,
175
129
  aoes={
176
130
  "influence": AOEConfig(
177
131
  radius=self.aoe_range,
@@ -193,18 +147,16 @@ class HubConfig(JunctionConfig):
193
147
  )
194
148
 
195
149
 
196
- class CogsGuardChestConfig(CvCStationConfig):
197
- """Chest for heart management in CogsGuard."""
150
+ class CvCChestConfig(CvCStationConfig):
151
+ """Chest station for heart management."""
198
152
 
199
- collective: str = Field(default="cogs", description="Collective this chest belongs to")
200
- heart_cost: dict[str, int] = Field(default_factory=lambda: HEART_COST)
153
+ heart_cost: dict[str, int] = Field(default_factory=lambda: CvCConfig.HEART_COST)
201
154
 
202
- def station_cfg(self) -> GridObjectConfig:
155
+ def station_cfg(self, team: str) -> GridObjectConfig:
203
156
  return GridObjectConfig(
204
157
  name="chest",
205
- map_name="chest",
206
158
  render_symbol="📦",
207
- collective=self.collective,
159
+ collective=team,
208
160
  on_use_handlers={
209
161
  "get_heart": Handler(
210
162
  filters=[isAlignedToActor(), targetCollectiveHas({"heart": 1})],
@@ -221,20 +173,19 @@ class CogsGuardChestConfig(CvCStationConfig):
221
173
  )
222
174
 
223
175
 
224
- class GearStationConfig(CvCStationConfig):
176
+ class CvCGearStationConfig(CvCStationConfig):
225
177
  """Gear station that clears all gear and adds the specified gear type."""
226
178
 
227
179
  gear_type: str = Field(description="Type of gear this station provides")
228
- collective: str = Field(default="cogs", description="Collective this station belongs to")
229
- gear_costs: dict[str, dict[str, int]] = Field(default_factory=lambda: GEAR_COSTS)
180
+ gear_costs: dict[str, dict[str, int]] = Field(default_factory=lambda: CvCConfig.GEAR_COSTS)
181
+ gear_symbols: dict[str, str] = Field(default_factory=lambda: CvCConfig.GEAR_SYMBOLS)
230
182
 
231
- def station_cfg(self) -> GridObjectConfig:
183
+ def station_cfg(self, team: str) -> GridObjectConfig:
232
184
  cost = self.gear_costs.get(self.gear_type, {})
233
185
  return GridObjectConfig(
234
186
  name=f"{self.gear_type}_station",
235
- map_name=f"{self.gear_type}_station",
236
- render_symbol=GEAR_SYMBOLS.get(self.gear_type, "⚙️"),
237
- collective=self.collective,
187
+ render_symbol=self.gear_symbols[self.gear_type],
188
+ collective=team,
238
189
  on_use_handlers={
239
190
  "keep_gear": Handler(
240
191
  filters=[isAlignedToActor(), actorHas({self.gear_type: 1})],
@@ -0,0 +1,44 @@
1
+ """Team configuration for CogsGuard missions.
2
+
3
+ Teams are named collectives (resource pools) shared by agents.
4
+ """
5
+
6
+ from pydantic import Field
7
+
8
+ from cogames.cogs_vs_clips.config import CvCConfig
9
+ from mettagrid.base_config import Config
10
+ from mettagrid.config.mettagrid_config import (
11
+ CollectiveConfig,
12
+ InventoryConfig,
13
+ ResourceLimitsConfig,
14
+ )
15
+
16
+
17
+ class CogTeam(Config):
18
+ """Configuration for a cogs team."""
19
+
20
+ name: str = Field(default="cogs", description="Team name")
21
+ wealth: int = Field(default=1, description="Wealth multiplier for initial resources")
22
+ num_agents: int = Field(default=8, ge=1, description="Number of agents in the team")
23
+
24
+ def collective_config(self) -> CollectiveConfig:
25
+ """Create a CollectiveConfig for this team.
26
+
27
+ Returns:
28
+ CollectiveConfig with resource limits and initial inventory.
29
+ """
30
+ return CollectiveConfig(
31
+ inventory=InventoryConfig(
32
+ limits={
33
+ "resources": ResourceLimitsConfig(min=10000, resources=CvCConfig.ELEMENTS),
34
+ "hearts": ResourceLimitsConfig(min=65535, resources=["heart"]),
35
+ },
36
+ initial={
37
+ "carbon": 10 * self.wealth,
38
+ "oxygen": 10 * self.wealth,
39
+ "germanium": 10 * self.wealth,
40
+ "silicon": 10 * self.wealth,
41
+ "heart": 5 * self.wealth,
42
+ },
43
+ ),
44
+ )
@@ -1,9 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  from abc import ABC, abstractmethod
2
- from typing import Any, Literal, override
4
+ from typing import TYPE_CHECKING, Any, Literal, override
3
5
 
4
6
  import numpy as np
5
7
 
6
- from cogames.cogs_vs_clips.mission import Mission, MissionVariant
8
+ from cogames.core import CoGameMissionVariant
9
+
10
+ if TYPE_CHECKING:
11
+ from cogames.cogs_vs_clips.mission import CvCMission
7
12
  from mettagrid.config.mettagrid_config import MettaGridConfig
8
13
  from mettagrid.mapgen.area import AreaWhere
9
14
  from mettagrid.mapgen.mapgen import MapGen, MapGenConfig
@@ -65,6 +70,7 @@ class MachinaArenaConfig(SceneConfig):
65
70
  corner_bundle="extractors",
66
71
  cross_bundle="none",
67
72
  cross_distance=7,
73
+ junction_object="junction",
68
74
  )
69
75
 
70
76
  # Optional asteroid-shaped boundary mask.
@@ -547,7 +553,7 @@ class RandomTransform(Scene[RandomTransformConfig]):
547
553
  ]
548
554
 
549
555
 
550
- class EnvNodeVariant[T](MissionVariant, ABC):
556
+ class EnvNodeVariant[T](CoGameMissionVariant, ABC):
551
557
  @abstractmethod
552
558
  def extract_node(self, env: MettaGridConfig) -> T: ...
553
559
 
@@ -590,7 +596,7 @@ class MapSeedVariant(MapGenVariant):
590
596
 
591
597
  class BaseHubVariant(EnvNodeVariant[BaseHubConfig]):
592
598
  @override
593
- def compat(self, mission: Mission) -> bool:
599
+ def compat(self, mission: CvCMission) -> bool:
594
600
  env = mission.make_env()
595
601
  if not isinstance(env.game.map_builder, MapGen.Config):
596
602
  return False
@@ -618,7 +624,7 @@ class BaseHubVariant(EnvNodeVariant[BaseHubConfig]):
618
624
 
619
625
 
620
626
  class MachinaArenaVariant(EnvNodeVariant[MachinaArenaConfig]):
621
- def compat(self, mission: Mission) -> bool:
627
+ def compat(self, mission: CvCMission) -> bool:
622
628
  env = mission.make_env()
623
629
  return isinstance(env.game.map_builder, MapGen.Config) and isinstance(
624
630
  env.game.map_builder.instance, MachinaArena.Config
@@ -632,7 +638,7 @@ class MachinaArenaVariant(EnvNodeVariant[MachinaArenaConfig]):
632
638
 
633
639
 
634
640
  class SequentialMachinaArenaVariant(EnvNodeVariant[SequentialMachinaArenaConfig]):
635
- def compat(self, mission: Mission) -> bool:
641
+ def compat(self, mission: CvCMission) -> bool:
636
642
  env = mission.make_env()
637
643
  return isinstance(env.game.map_builder, MapGen.Config) and isinstance(
638
644
  env.game.map_builder.instance, SequentialMachinaArena.Config