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
@@ -5,26 +5,24 @@ from __future__ import annotations
5
5
 
6
6
  import logging
7
7
 
8
- from cogames.cogs_vs_clips.mission import Mission, Site
9
- from cogames.cogs_vs_clips.procedural import MachinaArena
8
+ from cogames.cogs_vs_clips.mission import CvCMission
10
9
  from cogames.cogs_vs_clips.sites import HELLO_WORLD, TRAINING_FACILITY
10
+ from cogames.cogs_vs_clips.terrain import MachinaArena
11
11
  from cogames.cogs_vs_clips.variants import (
12
- CompassVariant,
13
12
  DarkSideVariant,
14
13
  DistantResourcesVariant,
15
14
  EmptyBaseVariant,
16
15
  EnergizedVariant,
17
- PackRatVariant,
18
16
  QuadrantBuildingsVariant,
19
- RoughTerrainVariant,
20
17
  SingleResourceUniformVariant,
21
18
  SuperChargedVariant,
22
19
  )
20
+ from cogames.core import CoGameSite
23
21
  from mettagrid.mapgen.mapgen import MapGen
24
22
 
25
23
  logger = logging.getLogger(__name__)
26
24
 
27
- SMALL_HELLO_WORLD = Site(
25
+ SMALL_HELLO_WORLD = CoGameSite(
28
26
  name="small_hello_world",
29
27
  description="Small hello world map.",
30
28
  map_builder=MapGen.Config(width=50, height=50, instance=MachinaArena.Config(spawn_count=20)),
@@ -32,7 +30,7 @@ SMALL_HELLO_WORLD = Site(
32
30
  max_cogs=20,
33
31
  )
34
32
 
35
- MEDIUM_HELLO_WORLD = Site(
33
+ MEDIUM_HELLO_WORLD = CoGameSite(
36
34
  name="medium_hello_world",
37
35
  description="Medium hello world map.",
38
36
  map_builder=MapGen.Config(width=100, height=100, instance=MachinaArena.Config(spawn_count=20)),
@@ -40,7 +38,7 @@ MEDIUM_HELLO_WORLD = Site(
40
38
  max_cogs=20,
41
39
  )
42
40
 
43
- LARGE_HELLO_WORLD = Site(
41
+ LARGE_HELLO_WORLD = CoGameSite(
44
42
  name="large_hello_world",
45
43
  description="Large hello world map.",
46
44
  map_builder=MapGen.Config(width=500, height=500, instance=MachinaArena.Config(spawn_count=20)),
@@ -49,19 +47,19 @@ LARGE_HELLO_WORLD = Site(
49
47
  )
50
48
 
51
49
  # Resource Bottleneck evals
52
- OxygenBottleneck = Mission(
50
+ OxygenBottleneck = CvCMission(
53
51
  name="oxygen_bottleneck",
54
52
  description="Oxygen is the limiting resource; agents must prioritize oxygen over other resources.",
55
53
  site=HELLO_WORLD,
56
54
  variants=[
57
55
  EmptyBaseVariant(missing=["oxygen_extractor"]),
58
56
  SingleResourceUniformVariant(building_name="oxygen_extractor"),
59
- PackRatVariant(),
57
+ EnergizedVariant(),
60
58
  ],
61
59
  )
62
60
 
63
61
  # Energy Starved evals
64
- EnergyStarved = Mission(
62
+ EnergyStarved = CvCMission(
65
63
  name="energy_starved",
66
64
  description="Energy is the limiting resource; agents must prioritize energy over other resources.",
67
65
  site=HELLO_WORLD,
@@ -74,17 +72,17 @@ EnergyStarved = Mission(
74
72
  # Curated difficulty tiers per mission
75
73
  # ------------------------------------------------------------
76
74
  # Oxygen Bottleneck
77
- OxygenBottleneckEasy = Mission(
75
+ OxygenBottleneckEasy = CvCMission(
78
76
  name="oxygen_bottleneck_easy",
79
77
  description="Easy: tuned oxygen focus with simple layout and generous capacities.",
80
78
  site=HELLO_WORLD,
81
79
  variants=[
82
80
  SingleResourceUniformVariant(building_name="oxygen_extractor"),
83
- PackRatVariant(),
81
+ EnergizedVariant(),
84
82
  ],
85
83
  )
86
84
 
87
- OxygenBottleneckStandard = Mission(
85
+ OxygenBottleneckStandard = CvCMission(
88
86
  name="oxygen_bottleneck_standard",
89
87
  description="Standard: oxygen is the bottleneck; extractor missing at base.",
90
88
  site=HELLO_WORLD,
@@ -93,18 +91,18 @@ OxygenBottleneckStandard = Mission(
93
91
  ],
94
92
  )
95
93
 
96
- OxygenBottleneckHard = Mission(
94
+ OxygenBottleneckHard = CvCMission(
97
95
  name="oxygen_bottleneck_hard",
98
- description="Hard: oxygen bottleneck plus rough terrain.",
96
+ description="Hard: oxygen bottleneck with dark side.",
99
97
  site=HELLO_WORLD,
100
98
  variants=[
101
99
  EmptyBaseVariant(missing=["oxygen_extractor"]),
102
- RoughTerrainVariant(),
100
+ DarkSideVariant(),
103
101
  ],
104
102
  )
105
103
 
106
104
  # Energy Starved
107
- EnergyStarvedEasy = Mission(
105
+ EnergyStarvedEasy = CvCMission(
108
106
  name="energy_starved_easy",
109
107
  description="Easy: abundant energy regen and capacity.",
110
108
  site=HELLO_WORLD,
@@ -114,7 +112,7 @@ EnergyStarvedEasy = Mission(
114
112
  ],
115
113
  )
116
114
 
117
- EnergyStarvedStandard = Mission(
115
+ EnergyStarvedStandard = CvCMission(
118
116
  name="energy_starved_standard",
119
117
  description="Standard: energy is the limiting resource with dark-side regen.",
120
118
  site=HELLO_WORLD,
@@ -123,18 +121,17 @@ EnergyStarvedStandard = Mission(
123
121
  ],
124
122
  )
125
123
 
126
- EnergyStarvedHard = Mission(
124
+ EnergyStarvedHard = CvCMission(
127
125
  name="energy_starved_hard",
128
- description="Hard: energy bottleneck with dark side and rough terrain.",
126
+ description="Hard: energy bottleneck with dark side.",
129
127
  site=HELLO_WORLD,
130
128
  variants=[
131
129
  DarkSideVariant(),
132
- RoughTerrainVariant(),
133
130
  ],
134
131
  )
135
132
 
136
133
  # Collect Distant Resources evals
137
- DistantResources = Mission(
134
+ DistantResources = CvCMission(
138
135
  name="distant_resources",
139
136
  description="Resources scattered far from base; heavy routing coordination.",
140
137
  site=HELLO_WORLD,
@@ -145,39 +142,37 @@ DistantResources = Mission(
145
142
  )
146
143
 
147
144
  # Distant Resources tiers
148
- DistantResourcesEasy = Mission(
145
+ DistantResourcesEasy = CvCMission(
149
146
  name="distant_resources_easy",
150
- description="Easy: simplified distribution and navigation aids.",
147
+ description="Easy: simplified distribution with generous capacity.",
151
148
  site=HELLO_WORLD,
152
149
  variants=[
153
- CompassVariant(),
154
- PackRatVariant(),
150
+ EnergizedVariant(),
155
151
  DistantResourcesVariant(),
156
152
  ],
157
153
  )
158
154
 
159
- DistantResourcesStandard = Mission(
155
+ DistantResourcesStandard = CvCMission(
160
156
  name="distant_resources_standard",
161
157
  description="Standard: resources scattered far from base.",
162
158
  site=HELLO_WORLD,
163
159
  variants=[
164
- CompassVariant(),
165
160
  DistantResourcesVariant(),
166
161
  ],
167
162
  )
168
163
 
169
- DistantResourcesHard = Mission(
164
+ DistantResourcesHard = CvCMission(
170
165
  name="distant_resources_hard",
171
- description="Hard: distant resources with rough terrain.",
166
+ description="Hard: distant resources with dark side.",
172
167
  site=HELLO_WORLD,
173
168
  variants=[
174
169
  DistantResourcesVariant(),
175
- RoughTerrainVariant(),
170
+ DarkSideVariant(),
176
171
  ],
177
172
  )
178
173
 
179
174
  # Divide and Conquer evals
180
- QuadrantBuildings = Mission(
175
+ QuadrantBuildings = CvCMission(
181
176
  name="quadrant_buildings",
182
177
  description="Place buildings in the four quadrants of the map.",
183
178
  site=HELLO_WORLD,
@@ -188,18 +183,17 @@ QuadrantBuildings = Mission(
188
183
  )
189
184
 
190
185
  # Quadrant Buildings tiers
191
- QuadrantBuildingsEasy = Mission(
186
+ QuadrantBuildingsEasy = CvCMission(
192
187
  name="quadrant_buildings_easy",
193
- description="Easy: buildings in quadrants with navigation aid and inventory boost.",
188
+ description="Easy: buildings in quadrants with energy boost.",
194
189
  site=HELLO_WORLD,
195
190
  variants=[
196
191
  QuadrantBuildingsVariant(),
197
- CompassVariant(),
198
- PackRatVariant(),
192
+ EnergizedVariant(),
199
193
  ],
200
194
  )
201
195
 
202
- QuadrantBuildingsStandard = Mission(
196
+ QuadrantBuildingsStandard = CvCMission(
203
197
  name="quadrant_buildings_standard",
204
198
  description="Standard: buildings placed in quadrants.",
205
199
  site=HELLO_WORLD,
@@ -209,54 +203,54 @@ QuadrantBuildingsStandard = Mission(
209
203
  ],
210
204
  )
211
205
 
212
- QuadrantBuildingsHard = Mission(
206
+ QuadrantBuildingsHard = CvCMission(
213
207
  name="quadrant_buildings_hard",
214
- description="Hard: quadrant distribution with empty base and rough terrain.",
208
+ description="Hard: quadrant distribution with empty base and dark side.",
215
209
  site=HELLO_WORLD,
216
210
  variants=[
217
211
  QuadrantBuildingsVariant(),
218
212
  EmptyBaseVariant(),
219
- RoughTerrainVariant(),
213
+ DarkSideVariant(),
220
214
  ],
221
215
  )
222
216
 
223
- EasyHeartsTraining = Mission(
217
+ EasyHeartsTraining = CvCMission(
224
218
  name="easy_hearts_training",
225
- description="Simplified heart crafting with generous caps and extractor base.",
219
+ description="Simplified heart crafting with generous energy.",
226
220
  site=TRAINING_FACILITY,
227
221
  variants=[
228
- PackRatVariant(),
222
+ EnergizedVariant(),
229
223
  ],
230
224
  )
231
225
 
232
- EasyHeartsSmallWorld = Mission(
226
+ EasyHeartsSmallWorld = CvCMission(
233
227
  name="easy_small_hearts",
234
- description="Simplified heart crafting with generous caps and extractor base.",
228
+ description="Simplified heart crafting with generous energy.",
235
229
  site=SMALL_HELLO_WORLD,
236
230
  variants=[
237
- PackRatVariant(),
231
+ EnergizedVariant(),
238
232
  ],
239
233
  )
240
234
 
241
- EasyHeartsMediumWorld = Mission(
235
+ EasyHeartsMediumWorld = CvCMission(
242
236
  name="easy_medium_hearts",
243
- description="Simplified heart crafting with generous caps and extractor base.",
237
+ description="Simplified heart crafting with generous energy.",
244
238
  site=MEDIUM_HELLO_WORLD,
245
239
  variants=[
246
- PackRatVariant(),
240
+ EnergizedVariant(),
247
241
  ],
248
242
  )
249
243
 
250
- EasyHeartsLargeWorld = Mission(
244
+ EasyHeartsLargeWorld = CvCMission(
251
245
  name="easy_large_hearts",
252
- description="Simplified heart crafting with generous caps and extractor base.",
246
+ description="Simplified heart crafting with generous energy.",
253
247
  site=LARGE_HELLO_WORLD,
254
248
  variants=[
255
- PackRatVariant(),
249
+ EnergizedVariant(),
256
250
  ],
257
251
  )
258
252
 
259
- EVAL_MISSIONS: list[Mission] = [
253
+ EVAL_MISSIONS: list[CvCMission] = [
260
254
  # Oxygen bottleneck tiers
261
255
  OxygenBottleneckEasy,
262
256
  OxygenBottleneckStandard,
@@ -1,188 +1,70 @@
1
1
  from __future__ import annotations
2
2
 
3
- from abc import ABC
4
- from typing import TypeVar, override
5
-
6
3
  from pydantic import Field
7
- from typing_extensions import Self
8
4
 
5
+ from cogames.cogs_vs_clips.clips import ClipsConfig
9
6
  from cogames.cogs_vs_clips.cog import CogConfig
7
+ from cogames.cogs_vs_clips.config import CvCConfig
10
8
  from cogames.cogs_vs_clips.stations import (
11
- ELEMENTS,
12
- GEAR,
13
- CogsGuardChestConfig,
9
+ CvCChestConfig,
10
+ CvCExtractorConfig,
11
+ CvCGearStationConfig,
12
+ CvCHubConfig,
13
+ CvCJunctionConfig,
14
14
  CvCWallConfig,
15
- GearStationConfig,
16
- HubConfig,
17
- JunctionConfig,
18
- SimpleExtractorConfig,
19
15
  )
20
- from mettagrid.base_config import Config
16
+ from cogames.cogs_vs_clips.team import CogTeam
17
+ from cogames.cogs_vs_clips.variants import NumCogsVariant
18
+ from cogames.core import (
19
+ MAP_MISSION_DELIMITER,
20
+ CoGameMission,
21
+ CoGameMissionVariant,
22
+ CoGameSite,
23
+ )
21
24
  from mettagrid.config.action_config import (
22
25
  ActionsConfig,
23
26
  ChangeVibeActionConfig,
24
27
  MoveActionConfig,
25
28
  NoopActionConfig,
26
29
  )
27
- from mettagrid.config.event_config import EventConfig, periodic
28
- from mettagrid.config.filter import isAlignedTo, isNear
29
30
  from mettagrid.config.game_value import inv
30
- from mettagrid.config.game_value import stat as game_stat
31
- from mettagrid.config.mettagrid_config import (
32
- CollectiveConfig,
33
- GameConfig,
34
- InventoryConfig,
35
- MettaGridConfig,
36
- ResourceLimitsConfig,
37
- )
38
- from mettagrid.config.mutation import alignTo
31
+ from mettagrid.config.mettagrid_config import GameConfig, MettaGridConfig
39
32
  from mettagrid.config.obs_config import GlobalObsConfig, ObsConfig
40
- from mettagrid.config.reward_config import numObjects, reward
41
- from mettagrid.config.tag import typeTag
42
- from mettagrid.config.vibes import Vibe
43
33
  from mettagrid.map_builder.map_builder import AnyMapBuilderConfig
44
34
 
45
- # Type variable for mission types
46
- TMission = TypeVar("TMission", bound="MissionBase")
47
-
48
-
49
- class MissionVariant(Config, ABC):
50
- # Note: we could derive the name from the class name automatically, but it would make it
51
- # harder to find the variant source code based on CLI interactions.
52
- name: str
53
- description: str = Field(default="")
54
-
55
- def modify_mission(self, mission: MissionBase) -> None:
56
- # Override this method to modify the mission.
57
- # Variants are allowed to modify the mission in-place - it's guaranteed to be a one-time only instance.
58
- pass
59
-
60
- def modify_env(self, mission: MissionBase, env: MettaGridConfig) -> None:
61
- # Override this method to modify the produced environment.
62
- # Variants are allowed to modify the environment in-place.
63
- pass
64
-
65
- def compat(self, mission: MissionBase) -> bool:
66
- """Check if this variant is compatible with the given mission.
67
-
68
- Returns True if the variant can be safely applied to the mission.
69
- Override this method to add compatibility checks.
70
- """
71
- return True
72
-
73
- def apply(self, mission: TMission) -> TMission:
74
- mission = mission.model_copy(deep=True)
75
- mission.variants.append(self)
76
- self.modify_mission(mission)
77
- return mission
78
-
79
- # Temporary helper useful as long as we have one-time variants in missions.py file.
80
- def as_mission(self, name: str, description: str, site: Site) -> Mission:
81
- return Mission(
82
- name=name,
83
- description=description,
84
- site=site,
85
- variants=[self],
86
- )
87
-
88
-
89
- class NumCogsVariant(MissionVariant):
90
- name: str = "num_cogs"
91
- description: str = "Set the number of cogs for the mission."
92
- num_cogs: int
93
-
94
- @override
95
- def modify_mission(self, mission: Mission) -> None:
96
- if self.num_cogs < mission.site.min_cogs or self.num_cogs > mission.site.max_cogs:
97
- raise ValueError(
98
- f"Invalid number of cogs for {mission.site.name}: {self.num_cogs}. "
99
- + f"Must be between {mission.site.min_cogs} and {mission.site.max_cogs}"
100
- )
101
-
102
- mission.num_cogs = self.num_cogs
103
-
104
-
105
- class Site(Config):
106
- name: str
107
- description: str
108
- map_builder: AnyMapBuilderConfig
109
-
110
- min_cogs: int = Field(default=1, ge=1)
111
- max_cogs: int = Field(default=1000, ge=1)
112
-
113
-
114
- MAP_MISSION_DELIMITER = "."
115
-
116
-
117
- class MissionBase(Config, ABC):
118
- """Base class for Mission configurations with common fields and methods."""
119
-
120
- name: str
121
- description: str
122
- site: Site
123
- num_cogs: int | None = None
124
-
125
- # Variants are applied to the mission immediately, and to its env when make_env is called
126
- variants: list[MissionVariant] = Field(default_factory=list)
127
-
128
- max_steps: int = Field(default=10000)
129
-
130
- def __init__(self, **kwargs):
131
- super().__init__(**kwargs)
132
- # Can't call `variant.apply` here because it will create a new mission instance
133
- for variant in self.variants:
134
- variant.modify_mission(self)
135
-
136
- def with_variants(self, variants: list[MissionVariant]) -> Self:
137
- mission = self
138
- for variant in variants:
139
- mission = variant.apply(mission)
140
- return mission
141
-
142
- def full_name(self) -> str:
143
- return f"{self.site.name}{MAP_MISSION_DELIMITER}{self.name}"
144
-
145
-
146
- # CogsGuard vibes
147
- COGSGUARD_VIBES = [
148
- Vibe("😐", "default"),
149
- Vibe("❤️", "heart"),
150
- Vibe("⚙️", "gear"),
151
- Vibe("🌀", "scrambler"),
152
- Vibe("🔗", "aligner"),
153
- Vibe("⛏️", "miner"),
154
- Vibe("🔭", "scout"),
35
+ __all__ = [
36
+ "MAP_MISSION_DELIMITER",
37
+ "CoGameMission",
38
+ "CoGameMissionVariant",
39
+ "CoGameSite",
40
+ "CvCMission",
41
+ "NumCogsVariant",
155
42
  ]
156
43
 
157
44
 
158
- class Mission(MissionBase):
45
+ class CvCMission(CoGameMission):
159
46
  """Mission configuration for CogsGuard game mode."""
160
47
 
161
- # Agent configuration
162
- cog: CogConfig = Field(default_factory=CogConfig)
163
-
164
- wealth: int = Field(default=1)
48
+ max_steps: int = Field(default=10000)
165
49
 
166
- # Collective initial resources
167
- collective_initial_carbon: int = Field(default=10)
168
- collective_initial_oxygen: int = Field(default=10)
169
- collective_initial_germanium: int = Field(default=10)
170
- collective_initial_silicon: int = Field(default=10)
171
- collective_initial_heart: int = Field(default=5)
50
+ cog: CogConfig = Field(default_factory=lambda: CogConfig())
51
+ teams: dict[str, CogTeam] = Field(
52
+ default_factory=lambda: {
53
+ "cogs": CogTeam(name="cogs", num_agents=8, wealth=1),
54
+ }
55
+ )
172
56
 
173
- # Clips Behavior - scramble cogs junctions to neutral
174
- # Note: must start after initial_clips fires at timestep 10 (events fire alphabetically)
175
- clips_scramble_start: int = Field(default=50)
176
- clips_scramble_interval: int = Field(default=100)
177
- clips_scramble_radius: int = Field(default=25)
57
+ clips: ClipsConfig = Field(default_factory=lambda: ClipsConfig())
178
58
 
179
- # Clips Behavior - align neutral junctions to clips
180
- clips_align_start: int = Field(default=100)
181
- clips_align_interval: int = Field(default=100)
182
- clips_align_radius: int = Field(default=25)
59
+ @property
60
+ def num_agents(self) -> int:
61
+ if self.num_cogs is not None:
62
+ return self.num_cogs
63
+ return sum(team.num_agents for team in self.teams.values())
183
64
 
184
- # Station configs
185
- wall: CvCWallConfig = Field(default_factory=CvCWallConfig)
65
+ def map_builder(self) -> AnyMapBuilderConfig:
66
+ """Return the map builder config. Override in subclasses for custom map generation."""
67
+ return self.site.map_builder
186
68
 
187
69
  def make_env(self) -> MettaGridConfig:
188
70
  """Create a MettaGridConfig from this mission.
@@ -192,129 +74,41 @@ class Mission(MissionBase):
192
74
  Returns:
193
75
  MettaGridConfig ready for environment creation
194
76
  """
195
- map_builder = self.site.map_builder
196
- num_cogs = self.num_cogs if self.num_cogs is not None else self.site.min_cogs
197
-
198
- gear = GEAR
199
- elements = ELEMENTS
200
- resources_list = ["energy", "heart", "hp", "influence", *elements, *gear]
201
- vibe_names = [vibe.name for vibe in COGSGUARD_VIBES]
202
-
203
- extractor_objects = {
204
- f"{resource}_extractor": SimpleExtractorConfig(resource=resource).station_cfg() for resource in elements
205
- }
206
- gear_objects = {f"{g}_station": GearStationConfig(gear_type=g).station_cfg() for g in gear}
207
-
208
- # Create inventory observations for collective resources
209
- collective_obs = [inv(f"collective.{resource}") for resource in elements]
210
77
 
211
78
  game = GameConfig(
212
- map_builder=map_builder,
79
+ map_builder=self.map_builder(),
213
80
  max_steps=self.max_steps,
214
- num_agents=num_cogs,
215
- resource_names=resources_list,
216
- vibe_names=vibe_names,
217
- obs=ObsConfig(global_obs=GlobalObsConfig(obs=collective_obs, local_position=True)),
81
+ num_agents=self.num_agents,
82
+ resource_names=CvCConfig.RESOURCES,
83
+ vibe_names=CvCConfig.VIBE_NAMES,
84
+ obs=ObsConfig(
85
+ global_obs=GlobalObsConfig(
86
+ obs=[inv(f"collective.{resource}") for resource in CvCConfig.ELEMENTS], local_position=True
87
+ )
88
+ ),
218
89
  actions=ActionsConfig(
219
- move=MoveActionConfig(consumed_resources={"energy": self.cog.move_energy_cost}),
90
+ move=MoveActionConfig(consumed_resources=self.cog.action_cost),
220
91
  noop=NoopActionConfig(),
221
- change_vibe=ChangeVibeActionConfig(vibes=COGSGUARD_VIBES),
222
- ),
223
- agent=self.cog.agent_config(gear=gear, elements=elements).model_copy(
224
- update={
225
- "rewards": {
226
- "aligned_junction_held": reward(
227
- game_stat("collective.aligned.junction.held"),
228
- weight=1.0 / self.max_steps,
229
- denoms=[numObjects("junction")],
230
- ),
231
- },
232
- }
92
+ change_vibe=ChangeVibeActionConfig(vibes=CvCConfig.VIBES),
233
93
  ),
94
+ agent=self.cog.agent_config(team="cogs", max_steps=self.max_steps),
95
+ agents=[self.cog.agent_config(team="cogs", max_steps=self.max_steps) for _ in range(self.num_agents)],
234
96
  objects={
235
- "wall": self.wall.station_cfg(),
236
- "hub": HubConfig(map_name="hub", team="cogs").station_cfg(),
237
- "junction": JunctionConfig(map_name="junction").station_cfg(),
238
- "chest": CogsGuardChestConfig().station_cfg(),
239
- **extractor_objects,
240
- **gear_objects,
97
+ "wall": CvCWallConfig().station_cfg(),
98
+ "hub": CvCHubConfig().station_cfg(team="cogs"),
99
+ "junction": CvCJunctionConfig().station_cfg(),
100
+ "chest": CvCChestConfig().station_cfg(team="cogs"),
101
+ **{
102
+ f"{resource}_extractor": CvCExtractorConfig(resource=resource).station_cfg()
103
+ for resource in CvCConfig.ELEMENTS
104
+ },
105
+ **{f"{g}_station": CvCGearStationConfig(gear_type=g).station_cfg(team="cogs") for g in CvCConfig.GEAR},
241
106
  },
242
107
  collectives={
243
- "cogs": CollectiveConfig(
244
- inventory=InventoryConfig(
245
- limits={
246
- "resources": ResourceLimitsConfig(min=10000, resources=elements),
247
- "hearts": ResourceLimitsConfig(min=65535, resources=["heart"]),
248
- },
249
- initial={
250
- "carbon": self.collective_initial_carbon * self.wealth,
251
- "oxygen": self.collective_initial_oxygen * self.wealth,
252
- "germanium": self.collective_initial_germanium * self.wealth,
253
- "silicon": self.collective_initial_silicon * self.wealth,
254
- "heart": self.collective_initial_heart * self.wealth,
255
- },
256
- ),
257
- ),
258
- "clips": CollectiveConfig(),
259
- },
260
- events={
261
- "initial_clips": EventConfig(
262
- name="initial_clips",
263
- target_tag=typeTag("junction"),
264
- timesteps=[10],
265
- mutations=[alignTo("clips")],
266
- max_targets=1,
267
- ),
268
- "cogs_to_neutral": EventConfig(
269
- name="cogs_to_neutral",
270
- target_tag=typeTag("junction"),
271
- timesteps=periodic(
272
- start=self.clips_scramble_start,
273
- period=self.clips_scramble_interval,
274
- end_period=self.clips_scramble_interval // 5,
275
- end=self.max_steps,
276
- ),
277
- filters=[
278
- isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.clips_scramble_radius),
279
- isAlignedTo("cogs"),
280
- ],
281
- mutations=[alignTo(None)],
282
- max_targets=1,
283
- ),
284
- "neutral_to_clips": EventConfig(
285
- name="neutral_to_clips",
286
- target_tag=typeTag("junction"),
287
- timesteps=periodic(
288
- start=self.clips_align_start,
289
- period=self.clips_align_interval,
290
- end_period=self.clips_align_interval // 5,
291
- end=self.max_steps,
292
- ),
293
- filters=[
294
- isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.clips_align_radius),
295
- isAlignedTo(None),
296
- ],
297
- mutations=[alignTo("clips")],
298
- max_targets=1,
299
- fallback="cogs_to_neutral",
300
- ),
301
- # If the Clips can't find any junctions near them, align a random junction
302
- "presence_check": EventConfig(
303
- name="presence_check",
304
- target_tag=typeTag("junction"),
305
- timesteps=periodic(
306
- start=self.clips_scramble_start,
307
- period=self.clips_scramble_interval,
308
- end=self.max_steps,
309
- ),
310
- filters=[
311
- isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.clips_scramble_radius),
312
- ],
313
- mutations=[],
314
- max_targets=1,
315
- fallback="initial_clips",
316
- ),
108
+ **{team.name: team.collective_config() for team in self.teams.values()},
109
+ "clips": self.clips.collective_config(),
317
110
  },
111
+ events=self.clips.events(max_steps=self.max_steps),
318
112
  )
319
113
 
320
114
  env = MettaGridConfig(game=game)
@@ -328,9 +122,3 @@ class Mission(MissionBase):
328
122
  env.label += f".{variant.name}"
329
123
 
330
124
  return env
331
-
332
-
333
- # Backwards compatibility alias
334
- CogsGuardMission = Mission
335
-
336
- AnyMission = Mission