cogames 0.3.64__py3-none-any.whl → 0.3.68__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 (141) hide show
  1. cogames/cli/client.py +0 -3
  2. cogames/cli/docsync/docsync.py +7 -1
  3. cogames/cli/mission.py +68 -53
  4. cogames/cli/policy.py +26 -10
  5. cogames/cli/submit.py +128 -142
  6. cogames/cli/utils.py +5 -0
  7. cogames/cogs_vs_clips/clip_difficulty.py +57 -0
  8. cogames/cogs_vs_clips/clips.py +103 -0
  9. cogames/cogs_vs_clips/cog.py +29 -11
  10. cogames/cogs_vs_clips/cogsguard_curriculum.py +122 -0
  11. cogames/cogs_vs_clips/cogsguard_tutorial.py +15 -16
  12. cogames/cogs_vs_clips/config.py +38 -0
  13. cogames/cogs_vs_clips/{cogs_vs_clips_mapgen.md → docs/cogs_vs_clips_mapgen.md} +8 -10
  14. cogames/cogs_vs_clips/evals/README.md +11 -35
  15. cogames/cogs_vs_clips/evals/cogsguard_evals.py +21 -6
  16. cogames/cogs_vs_clips/evals/diagnostic_evals.py +13 -101
  17. cogames/cogs_vs_clips/evals/difficulty_variants.py +16 -28
  18. cogames/cogs_vs_clips/evals/integrated_evals.py +8 -60
  19. cogames/cogs_vs_clips/evals/spanning_evals.py +48 -54
  20. cogames/cogs_vs_clips/mission.py +93 -277
  21. cogames/cogs_vs_clips/missions.py +17 -27
  22. cogames/cogs_vs_clips/{cogsguard_reward_variants.py → reward_variants.py} +22 -2
  23. cogames/cogs_vs_clips/sites.py +41 -30
  24. cogames/cogs_vs_clips/stations.py +39 -84
  25. cogames/cogs_vs_clips/team.py +46 -0
  26. cogames/cogs_vs_clips/{procedural.py → terrain.py} +14 -8
  27. cogames/cogs_vs_clips/variants.py +201 -107
  28. cogames/cogs_vs_clips/weather.py +52 -0
  29. cogames/core.py +87 -0
  30. cogames/docs/SCRIPTED_AGENT.md +3 -3
  31. cogames/evaluate.py +4 -2
  32. cogames/main.py +357 -51
  33. cogames/maps/canidate1_1000.map +1 -1
  34. cogames/maps/canidate1_1000_stations.map +2 -2
  35. cogames/maps/canidate1_500.map +1 -1
  36. cogames/maps/canidate1_500_stations.map +2 -2
  37. cogames/maps/canidate2_1000.map +1 -1
  38. cogames/maps/canidate2_1000_stations.map +2 -2
  39. cogames/maps/canidate2_500.map +1 -1
  40. cogames/maps/canidate2_500_stations.map +1 -1
  41. cogames/maps/canidate3_1000.map +1 -1
  42. cogames/maps/canidate3_1000_stations.map +2 -2
  43. cogames/maps/canidate3_500.map +1 -1
  44. cogames/maps/canidate3_500_stations.map +2 -2
  45. cogames/maps/canidate4_500.map +1 -1
  46. cogames/maps/canidate4_500_stations.map +2 -2
  47. cogames/maps/cave_base_50.map +2 -2
  48. cogames/maps/diagnostic_evals/diagnostic_agile.map +2 -2
  49. cogames/maps/diagnostic_evals/diagnostic_agile_hard.map +2 -2
  50. cogames/maps/diagnostic_evals/diagnostic_charge_up.map +6 -6
  51. cogames/maps/diagnostic_evals/diagnostic_charge_up_hard.map +6 -6
  52. cogames/maps/diagnostic_evals/diagnostic_chest_navigation1.map +6 -6
  53. cogames/maps/diagnostic_evals/diagnostic_chest_navigation1_hard.map +6 -6
  54. cogames/maps/diagnostic_evals/diagnostic_chest_navigation2.map +6 -6
  55. cogames/maps/diagnostic_evals/diagnostic_chest_navigation2_hard.map +6 -6
  56. cogames/maps/diagnostic_evals/diagnostic_chest_navigation3.map +6 -6
  57. cogames/maps/diagnostic_evals/diagnostic_chest_navigation3_hard.map +6 -6
  58. cogames/maps/diagnostic_evals/diagnostic_chest_near.map +6 -6
  59. cogames/maps/diagnostic_evals/diagnostic_chest_search.map +6 -6
  60. cogames/maps/diagnostic_evals/diagnostic_chest_search_hard.map +6 -6
  61. cogames/maps/diagnostic_evals/diagnostic_extract_lab.map +6 -6
  62. cogames/maps/diagnostic_evals/diagnostic_extract_lab_hard.map +6 -6
  63. cogames/maps/diagnostic_evals/diagnostic_memory.map +6 -6
  64. cogames/maps/diagnostic_evals/diagnostic_memory_hard.map +6 -6
  65. cogames/maps/diagnostic_evals/diagnostic_radial.map +2 -2
  66. cogames/maps/diagnostic_evals/diagnostic_radial_hard.map +2 -2
  67. cogames/maps/diagnostic_evals/diagnostic_resource_lab.map +6 -6
  68. cogames/maps/diagnostic_evals/diagnostic_unclip.map +6 -6
  69. cogames/maps/evals/eval_balanced_spread.map +6 -6
  70. cogames/maps/evals/eval_clip_oxygen.map +6 -6
  71. cogames/maps/evals/eval_collect_resources.map +6 -6
  72. cogames/maps/evals/eval_collect_resources_hard.map +6 -6
  73. cogames/maps/evals/eval_collect_resources_medium.map +6 -6
  74. cogames/maps/evals/eval_divide_and_conquer.map +6 -6
  75. cogames/maps/evals/eval_energy_starved.map +6 -6
  76. cogames/maps/evals/eval_multi_coordinated_collect_hard.map +6 -6
  77. cogames/maps/evals/eval_oxygen_bottleneck.map +6 -6
  78. cogames/maps/evals/eval_single_use_world.map +6 -6
  79. cogames/maps/evals/extractor_hub_100x100.map +6 -6
  80. cogames/maps/evals/extractor_hub_30x30.map +6 -6
  81. cogames/maps/evals/extractor_hub_50x50.map +6 -6
  82. cogames/maps/evals/extractor_hub_70x70.map +6 -6
  83. cogames/maps/evals/extractor_hub_80x80.map +6 -6
  84. cogames/maps/machina_100_stations.map +2 -2
  85. cogames/maps/machina_200_stations.map +2 -2
  86. cogames/maps/machina_200_stations_small.map +2 -2
  87. cogames/maps/machina_eval_exp01.map +2 -2
  88. cogames/maps/machina_eval_template_large.map +2 -2
  89. cogames/maps/machinatrainer4agents.map +2 -2
  90. cogames/maps/machinatrainer4agentsbase.map +2 -2
  91. cogames/maps/machinatrainerbig.map +2 -2
  92. cogames/maps/machinatrainersmall.map +2 -2
  93. cogames/maps/planky_evals/aligner_avoid_aoe.map +6 -6
  94. cogames/maps/planky_evals/aligner_full_cycle.map +6 -6
  95. cogames/maps/planky_evals/aligner_gear.map +6 -6
  96. cogames/maps/planky_evals/aligner_hearts.map +6 -6
  97. cogames/maps/planky_evals/aligner_junction.map +6 -6
  98. cogames/maps/planky_evals/exploration_distant.map +6 -6
  99. cogames/maps/planky_evals/maze.map +6 -6
  100. cogames/maps/planky_evals/miner_best_resource.map +6 -6
  101. cogames/maps/planky_evals/miner_deposit.map +6 -6
  102. cogames/maps/planky_evals/miner_extract.map +6 -6
  103. cogames/maps/planky_evals/miner_full_cycle.map +6 -6
  104. cogames/maps/planky_evals/miner_gear.map +6 -6
  105. cogames/maps/planky_evals/multi_role.map +6 -6
  106. cogames/maps/planky_evals/resource_chain.map +6 -6
  107. cogames/maps/planky_evals/scout_explore.map +6 -6
  108. cogames/maps/planky_evals/scout_gear.map +6 -6
  109. cogames/maps/planky_evals/scrambler_full_cycle.map +6 -6
  110. cogames/maps/planky_evals/scrambler_gear.map +6 -6
  111. cogames/maps/planky_evals/scrambler_target.map +6 -6
  112. cogames/maps/planky_evals/stuck_corridor.map +6 -6
  113. cogames/maps/planky_evals/survive_retreat.map +6 -6
  114. cogames/maps/training_facility_clipped.map +2 -2
  115. cogames/maps/training_facility_open_1.map +2 -2
  116. cogames/maps/training_facility_open_2.map +2 -2
  117. cogames/maps/training_facility_open_3.map +2 -2
  118. cogames/maps/training_facility_tight_4.map +2 -2
  119. cogames/maps/training_facility_tight_5.map +2 -2
  120. cogames/maps/vanilla_large.map +2 -2
  121. cogames/maps/vanilla_small.map +2 -2
  122. cogames/pickup.py +6 -5
  123. cogames/play.py +14 -16
  124. cogames/policy/nim_agents/__init__.py +0 -2
  125. cogames/policy/nim_agents/agents.py +0 -11
  126. cogames/policy/starter_agent.py +4 -1
  127. cogames/verbose.py +2 -2
  128. {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/METADATA +45 -29
  129. cogames-0.3.68.dist-info/RECORD +160 -0
  130. metta_alo/scoring.py +7 -7
  131. cogames/cogs_vs_clips/mission_utils.py +0 -19
  132. cogames/cogs_vs_clips/tutorial_missions.py +0 -25
  133. cogames-0.3.64.dist-info/RECORD +0 -159
  134. metta_alo/job_specs.py +0 -17
  135. metta_alo/policy.py +0 -16
  136. metta_alo/pure_single_episode_runner.py +0 -75
  137. metta_alo/rollout.py +0 -322
  138. {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/WHEEL +0 -0
  139. {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/entry_points.txt +0 -0
  140. {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/licenses/LICENSE +0 -0
  141. {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/top_level.txt +0 -0
@@ -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)),
@@ -51,22 +72,24 @@ MACHINA_1 = Site(
51
72
 
52
73
  def _cogsguard_hub_config() -> BaseHubConfig:
53
74
  return BaseHubConfig(
75
+ hub_object="c:hub",
54
76
  corner_bundle="extractors",
55
77
  cross_bundle="none",
56
78
  cross_distance=7,
79
+ junction_object="junction",
80
+ heart_chest_object="c:chest",
57
81
  stations=[
58
- "aligner_station",
59
- "scrambler_station",
60
- "miner_station",
61
- "scout_station",
62
- "chest",
82
+ "c:aligner",
83
+ "c:scrambler",
84
+ "c:miner",
85
+ "c:scout",
63
86
  ],
64
87
  )
65
88
 
66
89
 
67
90
  # Evals site used by diagnostic evaluation missions
68
91
  # Note: Individual diagnostic missions override this with their own specific maps
69
- EVALS = Site(
92
+ EVALS = CoGameSite(
70
93
  name="evals",
71
94
  description="Diagnostic evaluation arenas.",
72
95
  map_builder=get_map("diagnostic_evals/diagnostic_radial.map"), # Default map (rarely used)
@@ -75,7 +98,7 @@ EVALS = Site(
75
98
  )
76
99
 
77
100
 
78
- def make_cogsguard_arena_site(num_agents: int = 10) -> Site:
101
+ def make_cogsguard_arena_site(num_agents: int = 10) -> CoGameSite:
79
102
  """Create a CogsGuard arena site with configurable agent count."""
80
103
  map_builder = MapGen.Config(
81
104
  width=50,
@@ -86,7 +109,7 @@ def make_cogsguard_arena_site(num_agents: int = 10) -> Site:
86
109
  hub=_cogsguard_hub_config(),
87
110
  ),
88
111
  )
89
- return Site(
112
+ return CoGameSite(
90
113
  name="cogsguard_arena",
91
114
  description="CogsGuard arena map",
92
115
  map_builder=map_builder,
@@ -111,9 +134,9 @@ def _build_cogsguard_machina1_map_builder(spawn_count: int) -> MapGenConfig:
111
134
  )
112
135
 
113
136
 
114
- def make_cogsguard_machina1_site(num_agents: int = 10) -> Site:
137
+ def make_cogsguard_machina1_site(num_agents: int = 10) -> CoGameSite:
115
138
  """Create a CogsGuard Machina1 site with configurable agent count."""
116
- return Site(
139
+ return CoGameSite(
117
140
  name="cogsguard_machina_1",
118
141
  description="CogsGuard Machina1 layout with gear stations.",
119
142
  map_builder=_build_cogsguard_machina1_map_builder(num_agents),
@@ -123,7 +146,7 @@ def make_cogsguard_machina1_site(num_agents: int = 10) -> Site:
123
146
 
124
147
 
125
148
  # Default CogsGuard Machina1 site with flexible agent count
126
- COGSGUARD_MACHINA_1 = Site(
149
+ COGSGUARD_MACHINA_1 = CoGameSite(
127
150
  name="cogsguard_machina_1",
128
151
  description="CogsGuard Machina1 layout with gear stations.",
129
152
  map_builder=_build_cogsguard_machina1_map_builder(20),
@@ -132,7 +155,7 @@ COGSGUARD_MACHINA_1 = Site(
132
155
  )
133
156
 
134
157
  # Default CogsGuard arena site with flexible agent count
135
- COGSGUARD_ARENA = Site(
158
+ COGSGUARD_ARENA = CoGameSite(
136
159
  name="cogsguard_arena",
137
160
  description="CogsGuard arena - compact training map with gear stations.",
138
161
  map_builder=MapGen.Config(
@@ -149,20 +172,8 @@ COGSGUARD_ARENA = Site(
149
172
  )
150
173
 
151
174
 
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
175
  SITES = [
164
176
  COGSGUARD_MACHINA_1,
165
177
  COGSGUARD_ARENA,
166
178
  EVALS,
167
- *(_LEGACY_SITES if _INCLUDE_LEGACY_SITES else []),
168
179
  ]
@@ -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,19 @@ 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,
85
+ render_name="junction",
134
86
  render_symbol="📦",
135
- collective=self.team,
87
+ collective=team,
136
88
  aoes={
137
89
  "influence": AOEConfig(
138
90
  radius=self.aoe_range,
@@ -148,30 +100,34 @@ class JunctionConfig(CvCStationConfig):
148
100
  on_use_handlers={
149
101
  "deposit": Handler(
150
102
  filters=[isAlignedToActor()],
151
- mutations=[collectiveDeposit({resource: 100 for resource in self.elements})],
103
+ mutations=[collectiveDeposit({resource: 100 for resource in CvCConfig.ELEMENTS})],
152
104
  ),
153
105
  "align": Handler(
154
- filters=[isNeutral(), actorHas({"aligner": 1, "influence": 1, **self.align_cost})],
155
- mutations=[updateActor(_neg(self.align_cost)), alignToActor()],
106
+ filters=[isNeutral(), actorHas({"aligner": 1, "influence": 1, **CvCConfig.ALIGN_COST})],
107
+ mutations=[updateActor(_neg(CvCConfig.ALIGN_COST)), alignToActor()],
156
108
  ),
157
109
  "scramble": Handler(
158
- filters=[isEnemy(), actorHas({"scrambler": 1, **self.scramble_cost})],
159
- mutations=[removeAlignment(), updateActor(_neg(self.scramble_cost))],
110
+ filters=[isEnemy(), actorHas({"scrambler": 1, **CvCConfig.SCRAMBLE_COST})],
111
+ mutations=[removeAlignment(), updateActor(_neg(CvCConfig.SCRAMBLE_COST))],
160
112
  ),
161
113
  },
162
114
  )
163
115
 
164
116
 
165
- class HubConfig(JunctionConfig):
166
- """Main hub with influence AOE effect. A junction without align/scramble handlers."""
117
+ class CvCHubConfig(CvCStationConfig):
118
+ """Hub station that provides AOE influence/attack and accepts deposits."""
167
119
 
168
- def station_cfg(self) -> GridObjectConfig:
120
+ aoe_range: int = Field(default=10, description="Range for AOE effects")
121
+ influence_deltas: dict[str, int] = Field(default_factory=lambda: {"influence": 10, "energy": 100, "hp": 100})
122
+ attack_deltas: dict[str, int] = Field(default_factory=lambda: {"hp": -1, "influence": -100})
123
+ elements: list[str] = Field(default_factory=lambda: CvCConfig.ELEMENTS)
124
+
125
+ def station_cfg(self, team: str, collective: str | None = None) -> GridObjectConfig:
169
126
  return GridObjectConfig(
170
- name="hub",
171
- map_name=self.map_name,
127
+ name=f"{team}:hub",
172
128
  render_name="hub",
173
129
  render_symbol="📦",
174
- collective=self.team,
130
+ collective=collective or team,
175
131
  aoes={
176
132
  "influence": AOEConfig(
177
133
  radius=self.aoe_range,
@@ -193,18 +149,17 @@ class HubConfig(JunctionConfig):
193
149
  )
194
150
 
195
151
 
196
- class CogsGuardChestConfig(CvCStationConfig):
197
- """Chest for heart management in CogsGuard."""
152
+ class CvCChestConfig(CvCStationConfig):
153
+ """Chest station for heart management."""
198
154
 
199
- collective: str = Field(default="cogs", description="Collective this chest belongs to")
200
- heart_cost: dict[str, int] = Field(default_factory=lambda: HEART_COST)
155
+ heart_cost: dict[str, int] = Field(default_factory=lambda: CvCConfig.HEART_COST)
201
156
 
202
- def station_cfg(self) -> GridObjectConfig:
157
+ def station_cfg(self, team: str, collective: str | None = None) -> GridObjectConfig:
203
158
  return GridObjectConfig(
204
- name="chest",
205
- map_name="chest",
159
+ name=f"{team}:chest",
160
+ render_name="chest",
206
161
  render_symbol="📦",
207
- collective=self.collective,
162
+ collective=collective or team,
208
163
  on_use_handlers={
209
164
  "get_heart": Handler(
210
165
  filters=[isAlignedToActor(), targetCollectiveHas({"heart": 1})],
@@ -221,20 +176,20 @@ class CogsGuardChestConfig(CvCStationConfig):
221
176
  )
222
177
 
223
178
 
224
- class GearStationConfig(CvCStationConfig):
179
+ class CvCGearStationConfig(CvCStationConfig):
225
180
  """Gear station that clears all gear and adds the specified gear type."""
226
181
 
227
182
  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)
183
+ gear_costs: dict[str, dict[str, int]] = Field(default_factory=lambda: CvCConfig.GEAR_COSTS)
184
+ gear_symbols: dict[str, str] = Field(default_factory=lambda: CvCConfig.GEAR_SYMBOLS)
230
185
 
231
- def station_cfg(self) -> GridObjectConfig:
186
+ def station_cfg(self, team: str, collective: str | None = None) -> GridObjectConfig:
232
187
  cost = self.gear_costs.get(self.gear_type, {})
233
188
  return GridObjectConfig(
234
- 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,
189
+ name=f"{team}:{self.gear_type}",
190
+ render_name=f"{self.gear_type}_station",
191
+ render_symbol=self.gear_symbols[self.gear_type],
192
+ collective=collective or team,
238
193
  on_use_handlers={
239
194
  "keep_gear": Handler(
240
195
  filters=[isAlignedToActor(), actorHas({self.gear_type: 1})],
@@ -0,0 +1,46 @@
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 used for collectives and alignment")
21
+ short_name: str = Field(default="c", description="Short prefix used for map object names")
22
+ wealth: int = Field(default=1, description="Wealth multiplier for initial resources")
23
+ num_agents: int = Field(default=8, ge=1, description="Number of agents in the team")
24
+
25
+ def collective_config(self) -> CollectiveConfig:
26
+ """Create a CollectiveConfig for this team.
27
+
28
+ Returns:
29
+ CollectiveConfig with resource limits and initial inventory.
30
+ """
31
+ return CollectiveConfig(
32
+ name=self.name,
33
+ inventory=InventoryConfig(
34
+ limits={
35
+ "resources": ResourceLimitsConfig(min=10000, resources=CvCConfig.ELEMENTS),
36
+ "hearts": ResourceLimitsConfig(min=65535, resources=["heart"]),
37
+ },
38
+ initial={
39
+ "carbon": 10 * self.wealth,
40
+ "oxygen": 10 * self.wealth,
41
+ "germanium": 10 * self.wealth,
42
+ "silicon": 10 * self.wealth,
43
+ "heart": 5 * self.wealth,
44
+ },
45
+ ),
46
+ )
@@ -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.
@@ -112,7 +118,7 @@ class MachinaArena(Scene[MachinaArenaConfig]):
112
118
 
113
119
  # Building weights
114
120
  default_building_weights = {
115
- "chest": 0.0,
121
+ "c:chest": 0.0,
116
122
  "junction": 0.7,
117
123
  "germanium_extractor": 0.3,
118
124
  "silicon_extractor": 0.3,
@@ -371,7 +377,7 @@ class SequentialMachinaArena(Scene[SequentialMachinaArenaConfig]):
371
377
  raise ValueError(f"Unknown base_biome '{cfg.base_biome}'. Valid: {sorted(biome_map.keys())}")
372
378
  base_cfg: SceneConfig = BaseCfgModel.model_validate(cfg.base_biome_config or {})
373
379
  default_building_weights = {
374
- "chest": 0.0,
380
+ "c:chest": 0.0,
375
381
  "junction": 0.6,
376
382
  "germanium_extractor": 0.2,
377
383
  "silicon_extractor": 0.2,
@@ -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