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,95 +1,74 @@
1
- from typing import override
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, override
2
4
 
3
5
  from cogames.cogs_vs_clips.evals.difficulty_variants import DIFFICULTY_VARIANTS
4
- from cogames.cogs_vs_clips.mission import Mission, MissionVariant
5
- from cogames.cogs_vs_clips.procedural import BaseHubVariant, MachinaArenaVariant
6
- from mettagrid.config.action_config import VibeTransfer
7
- from mettagrid.config.game_value import stat
8
- from mettagrid.config.reward_config import reward
6
+ from cogames.cogs_vs_clips.terrain import BaseHubVariant, MachinaArenaVariant
7
+ from cogames.core import CoGameMissionVariant
9
8
  from mettagrid.map_builder.map_builder import MapBuilderConfig
10
9
  from mettagrid.mapgen.mapgen import MapGen
11
10
  from mettagrid.mapgen.scenes.base_hub import DEFAULT_EXTRACTORS as HUB_EXTRACTORS
12
11
  from mettagrid.mapgen.scenes.building_distributions import DistributionConfig, DistributionType
13
12
 
13
+ if TYPE_CHECKING:
14
+ from cogames.cogs_vs_clips.mission import CvCMission
15
+
16
+
17
+ class NumCogsVariant(CoGameMissionVariant):
18
+ name: str = "num_cogs"
19
+ description: str = "Set the number of cogs for the mission."
20
+ num_cogs: int
14
21
 
15
- class DarkSideVariant(MissionVariant):
22
+ @override
23
+ def modify_mission(self, mission: CvCMission) -> None:
24
+ if self.num_cogs < mission.site.min_cogs or self.num_cogs > mission.site.max_cogs:
25
+ raise ValueError(
26
+ f"Invalid number of cogs for {mission.site.name}: {self.num_cogs}. "
27
+ + f"Must be between {mission.site.min_cogs} and {mission.site.max_cogs}"
28
+ )
29
+
30
+ mission.num_cogs = self.num_cogs
31
+
32
+
33
+ class DarkSideVariant(CoGameMissionVariant):
16
34
  name: str = "dark_side"
17
35
  description: str = "You're on the dark side of the asteroid. You recharge slower."
18
36
 
19
37
  @override
20
- def modify_mission(self, mission):
21
- assert isinstance(mission, Mission)
38
+ def modify_mission(self, mission: CvCMission) -> None:
22
39
  mission.cog.energy_regen = 0
23
40
 
24
41
 
25
- class SuperChargedVariant(MissionVariant):
42
+ class SuperChargedVariant(CoGameMissionVariant):
26
43
  name: str = "super_charged"
27
44
  description: str = "The sun is shining on you. You recharge faster."
28
45
 
29
46
  @override
30
- def modify_mission(self, mission):
31
- assert isinstance(mission, Mission)
47
+ def modify_mission(self, mission: CvCMission) -> None:
32
48
  mission.cog.energy_regen += 2
33
49
 
34
50
 
35
- class RoughTerrainVariant(MissionVariant):
36
- name: str = "rough_terrain"
37
- description: str = "The terrain is rough. Moving is more energy intensive."
38
-
39
- @override
40
- def modify_mission(self, mission):
41
- assert isinstance(mission, Mission)
42
- mission.cog.move_energy_cost += 2
43
-
44
-
45
- class PackRatVariant(MissionVariant):
46
- name: str = "pack_rat"
47
- description: str = "Raise heart, cargo, energy, and gear caps to 255."
48
-
49
- @override
50
- def modify_mission(self, mission):
51
- assert isinstance(mission, Mission)
52
- mission.cog.heart_limit = max(mission.cog.heart_limit, 255)
53
- mission.cog.energy_limit = max(mission.cog.energy_limit, 255)
54
- mission.cog.cargo_limit = max(mission.cog.cargo_limit, 255)
55
- mission.cog.gear_limit = max(mission.cog.gear_limit, 255)
56
-
57
-
58
- class EnergizedVariant(MissionVariant):
51
+ class EnergizedVariant(CoGameMissionVariant):
59
52
  name: str = "energized"
60
53
  description: str = "Max energy and full regen so agents never run dry."
61
54
 
62
55
  @override
63
- def modify_mission(self, mission):
64
- assert isinstance(mission, Mission)
56
+ def modify_mission(self, mission: CvCMission) -> None:
65
57
  mission.cog.energy_limit = max(mission.cog.energy_limit, 255)
66
58
  mission.cog.energy_regen = mission.cog.energy_limit
67
59
 
68
60
 
69
- class CompassVariant(MissionVariant):
70
- name: str = "compass"
71
- description: str = "Enable compass observation."
72
-
73
- @override
74
- def modify_env(self, mission, env):
75
- env.game.obs.global_obs.compass = True
76
-
77
-
78
- class Small50Variant(MissionVariant):
61
+ class Small50Variant(CoGameMissionVariant):
79
62
  name: str = "small_50"
80
63
  description: str = "Set map size to 50x50 for quick runs."
81
64
 
82
65
  def modify_env(self, mission, env) -> None:
83
66
  map_builder = env.game.map_builder
84
- # Only set width/height if instance is a SceneConfig, not a MapBuilderConfig
85
- # When instance is a MapBuilderConfig, width and height must be None
86
67
  if isinstance(map_builder, MapGen.Config) and isinstance(map_builder.instance, MapBuilderConfig):
87
- # Skip setting width/height for MapBuilderConfig instances
88
68
  return
89
69
  env.game.map_builder = map_builder.model_copy(update={"width": 50, "height": 50})
90
70
 
91
71
 
92
- # Biome variants (weather) for procedural maps
93
72
  class DesertVariant(MachinaArenaVariant):
94
73
  name: str = "desert"
95
74
  description: str = "The desert sands make navigation challenging."
@@ -117,7 +96,6 @@ class CityVariant(MachinaArenaVariant):
117
96
  def modify_node(self, node):
118
97
  node.biome_weights = {"city": 1.0, "caves": 0.0, "desert": 0.0, "forest": 0.0}
119
98
  node.base_biome = "city"
120
- # Fill almost the entire map with the city layer
121
99
  node.density_scale = 1.0
122
100
  node.biome_count = 1
123
101
  node.max_biome_zone_fraction = 0.95
@@ -140,32 +118,29 @@ class DistantResourcesVariant(MachinaArenaVariant):
140
118
 
141
119
  @override
142
120
  def modify_node(self, node):
143
- # Bias buildings toward the map edges using bimodal clusters centered at
144
121
  node.building_coverage = 0.01
145
122
 
146
123
  vertical_edges = DistributionConfig(
147
124
  type=DistributionType.BIMODAL,
148
- center1_x=0.92, # top right corner
125
+ center1_x=0.92,
149
126
  center1_y=0.08,
150
- center2_x=0.08, # bottom left corner
127
+ center2_x=0.08,
151
128
  center2_y=0.92,
152
129
  cluster_std=0.18,
153
130
  )
154
131
  horizontal_edges = DistributionConfig(
155
132
  type=DistributionType.BIMODAL,
156
- center1_x=0.08, # top left corner
133
+ center1_x=0.08,
157
134
  center1_y=0.08,
158
- center2_x=0.92, # bottom right corner
135
+ center2_x=0.92,
159
136
  center2_y=0.92,
160
137
  cluster_std=0.18,
161
138
  )
162
139
 
163
- # Apply edge-biased distributions to extractors; other buildings follow the global distribution
164
140
  names = list(self.building_names)
165
141
  node.building_distributions = {
166
142
  name: (vertical_edges if i % 2 == 0 else horizontal_edges) for i, name in enumerate(names)
167
143
  }
168
- # Fallback for any unspecified building types
169
144
  node.distribution = DistributionConfig(type=DistributionType.UNIFORM)
170
145
 
171
146
 
@@ -180,10 +155,10 @@ class QuadrantBuildingsVariant(MachinaArenaVariant):
180
155
 
181
156
  names = list(node.building_names or self.building_names)
182
157
  centers = [
183
- (0.25, 0.25), # top-left
184
- (0.75, 0.25), # top-right
185
- (0.25, 0.75), # bottom-left
186
- (0.75, 0.75), # bottom-right
158
+ (0.25, 0.25),
159
+ (0.75, 0.25),
160
+ (0.25, 0.75),
161
+ (0.75, 0.75),
187
162
  ]
188
163
  dists: dict[str, DistributionConfig] = {}
189
164
  for i, name in enumerate(names):
@@ -206,8 +181,6 @@ class SingleResourceUniformVariant(MachinaArenaVariant):
206
181
 
207
182
  @override
208
183
  def modify_node(self, node):
209
- # Resolve resource to a concrete building name
210
- # Restrict building set to only the chosen building and enforce uniform distribution
211
184
  node.building_names = [self.building_name]
212
185
  node.building_weights = {self.building_name: 1.0}
213
186
  node.building_distributions = None
@@ -217,12 +190,10 @@ class SingleResourceUniformVariant(MachinaArenaVariant):
217
190
  class EmptyBaseVariant(BaseHubVariant):
218
191
  name: str = "empty_base"
219
192
  description: str = "Base hub with extractors removed from the four corners."
220
- # Extractor object names to remove, e.g., ["oxygen_extractor"]
221
193
  missing: list[str] = list(HUB_EXTRACTORS)
222
194
 
223
195
  @override
224
196
  def modify_node(self, node):
225
- # Use the default extractor order and blank out any that are missing
226
197
  missing_set = set(self.missing or [])
227
198
  corner_objects = [name if name not in missing_set else "" for name in HUB_EXTRACTORS]
228
199
  node.corner_objects = corner_objects
@@ -230,8 +201,6 @@ class EmptyBaseVariant(BaseHubVariant):
230
201
 
231
202
 
232
203
  class BalancedCornersVariant(MachinaArenaVariant):
233
- """Enable corner balancing to ensure fair spawn distances."""
234
-
235
204
  name: str = "balanced_corners"
236
205
  description: str = "Balance path distances from center to corners for fair spawns."
237
206
  balance_tolerance: float = 1.5
@@ -244,65 +213,19 @@ class BalancedCornersVariant(MachinaArenaVariant):
244
213
  node.max_balance_shortcuts = self.max_balance_shortcuts
245
214
 
246
215
 
247
- class TraderVariant(MissionVariant):
248
- name: str = "trader"
249
- description: str = "Agents can trade resources with each other."
250
-
251
- @override
252
- def modify_env(self, mission, env):
253
- # Define vibe transfers for trading resources (actor gives, target receives)
254
- trade_transfers = [
255
- VibeTransfer(vibe="carbon_a", target={"carbon": 1}, actor={"carbon": -1}),
256
- VibeTransfer(vibe="carbon_b", target={"carbon": 10}, actor={"carbon": -10}),
257
- VibeTransfer(vibe="oxygen_a", target={"oxygen": 1}, actor={"oxygen": -1}),
258
- VibeTransfer(vibe="oxygen_b", target={"oxygen": 10}, actor={"oxygen": -10}),
259
- VibeTransfer(vibe="germanium_a", target={"germanium": 1}, actor={"germanium": -1}),
260
- VibeTransfer(vibe="germanium_b", target={"germanium": 4}, actor={"germanium": -4}),
261
- VibeTransfer(vibe="silicon_a", target={"silicon": 10}, actor={"silicon": -10}),
262
- VibeTransfer(vibe="silicon_b", target={"silicon": 50}, actor={"silicon": -50}),
263
- VibeTransfer(vibe="heart_a", target={"heart": 1}, actor={"heart": -1}),
264
- VibeTransfer(vibe="heart_b", target={"heart": 4}, actor={"heart": -4}),
265
- ]
266
- # Enable transfer action with these vibes
267
- env.game.actions.transfer.enabled = True
268
- env.game.actions.transfer.vibe_transfers.extend(trade_transfers)
269
-
270
-
271
- class SharedRewardsVariant(MissionVariant):
272
- name: str = "shared_rewards"
273
- description: str = "Rewards for deposited hearts are shared among all agents."
274
-
275
- @override
276
- def modify_env(self, mission, env):
277
- num_cogs = mission.num_cogs if mission.num_cogs is not None else mission.site.min_cogs
278
- env.game.agent.rewards["chest_heart_deposited_by_agent"] = reward(
279
- stat("chest.heart.deposited_by_agent"), weight=0
280
- )
281
- env.game.agent.rewards["chest_heart_amount"] = reward(stat("chest.heart.amount"), weight=1 / num_cogs)
282
-
283
-
284
- # TODO - validate that all variant names are unique
285
- VARIANTS: list[MissionVariant] = [
216
+ VARIANTS: list[CoGameMissionVariant] = [
286
217
  CavesVariant(),
287
218
  CityVariant(),
288
- CompassVariant(),
289
219
  DarkSideVariant(),
290
220
  DesertVariant(),
291
221
  EmptyBaseVariant(),
292
222
  EnergizedVariant(),
293
223
  ForestVariant(),
294
- PackRatVariant(),
295
224
  QuadrantBuildingsVariant(),
296
- RoughTerrainVariant(),
297
- SharedRewardsVariant(),
298
225
  SingleResourceUniformVariant(),
299
226
  Small50Variant(),
300
227
  SuperChargedVariant(),
301
- TraderVariant(),
302
228
  *DIFFICULTY_VARIANTS,
303
229
  ]
304
230
 
305
- # Hidden variants registry: Remains usable but will NOT appear in `cogames variants` listing
306
- HIDDEN_VARIANTS: list[MissionVariant] = [
307
- # Example: ExperimentalVariant(), # keep empty by default
308
- ]
231
+ HIDDEN_VARIANTS: list[CoGameMissionVariant] = []
cogames/core.py ADDED
@@ -0,0 +1,87 @@
1
+ """Core base classes for CoGame missions and variants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC
6
+ from typing import TypeVar
7
+
8
+ from pydantic import Field
9
+ from typing_extensions import Self
10
+
11
+ from mettagrid.base_config import Config
12
+ from mettagrid.config.mettagrid_config import MettaGridConfig
13
+ from mettagrid.map_builder.map_builder import AnyMapBuilderConfig
14
+
15
+ # Type variable for mission types
16
+ TMission = TypeVar("TMission", bound="CoGameMission")
17
+
18
+ MAP_MISSION_DELIMITER = "."
19
+
20
+
21
+ class CoGameMissionVariant(Config, ABC):
22
+ # Note: we could derive the name from the class name automatically, but it would make it
23
+ # harder to find the variant source code based on CLI interactions.
24
+ name: str
25
+ description: str = Field(default="")
26
+
27
+ def modify_mission(self, mission: CoGameMission) -> None:
28
+ # Override this method to modify the mission.
29
+ # Variants are allowed to modify the mission in-place - it's guaranteed to be a one-time only instance.
30
+ pass
31
+
32
+ def modify_env(self, mission: CoGameMission, env: MettaGridConfig) -> None:
33
+ # Override this method to modify the produced environment.
34
+ # Variants are allowed to modify the environment in-place.
35
+ pass
36
+
37
+ def compat(self, mission: CoGameMission) -> bool:
38
+ """Check if this variant is compatible with the given mission.
39
+
40
+ Returns True if the variant can be safely applied to the mission.
41
+ Override this method to add compatibility checks.
42
+ """
43
+ return True
44
+
45
+ def apply(self, mission: TMission) -> TMission:
46
+ mission = mission.model_copy(deep=True)
47
+ mission.variants.append(self)
48
+ self.modify_mission(mission)
49
+ return mission
50
+
51
+
52
+ class CoGameSite(Config):
53
+ name: str
54
+ description: str
55
+ map_builder: AnyMapBuilderConfig
56
+
57
+ min_cogs: int = Field(default=1, ge=1)
58
+ max_cogs: int = Field(default=1000, ge=1)
59
+
60
+
61
+ class CoGameMission(Config, ABC):
62
+ """Base class for Mission configurations with common fields and methods."""
63
+
64
+ name: str
65
+ description: str
66
+ site: CoGameSite
67
+ num_cogs: int | None = None
68
+
69
+ # Variants are applied to the mission immediately, and to its env when make_env is called
70
+ variants: list[CoGameMissionVariant] = Field(default_factory=list)
71
+
72
+ max_steps: int = Field(default=10000)
73
+
74
+ def __init__(self, **kwargs):
75
+ super().__init__(**kwargs)
76
+ # Can't call `variant.apply` here because it will create a new mission instance
77
+ for variant in self.variants:
78
+ variant.modify_mission(self)
79
+
80
+ def with_variants(self, variants: list[CoGameMissionVariant]) -> Self:
81
+ mission = self
82
+ for variant in variants:
83
+ mission = variant.apply(mission)
84
+ return mission
85
+
86
+ def full_name(self) -> str:
87
+ return f"{self.site.name}{MAP_MISSION_DELIMITER}{self.name}"
cogames/main.py CHANGED
@@ -109,13 +109,17 @@ def _register_policies() -> None:
109
109
  discover_and_register_policies()
110
110
 
111
111
 
112
+ def _register_policies_callback() -> None:
113
+ _register_policies()
114
+
115
+
112
116
  app = typer.Typer(
113
117
  help="CoGames - Multi-agent cooperative and competitive games",
114
118
  context_settings={"help_option_names": ["-h", "--help"]},
115
119
  no_args_is_help=True,
116
120
  rich_markup_mode="rich",
117
121
  pretty_exceptions_show_locals=False,
118
- callback=_register_policies,
122
+ callback=_register_policies_callback,
119
123
  )
120
124
 
121
125
  tutorial_app = typer.Typer(
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.C....O......................#
16
16
  #C.......#.......................G.....#
17
17
  #......C.#.....###.....###.............#
18
- #........#.....#O.......G#...........O.#
19
- #.....#........#....=....#......#......#
20
- #.....#.....G...................##.....#
18
+ #........#.....#O...m...G#...........O.#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.....G.......t...........##.....#
21
21
  #....C#.............&....S.......##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#....C...........................#
@@ -51,3 +51,7 @@ char_to_map_name:
51
51
  "@": agent.agent
52
52
  "&": hub
53
53
  "=": chest
54
+ "a": aligner_station
55
+ "r": scrambler_station
56
+ "m": miner_station
57
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #........#.............................#
17
17
  #........#.....###.....###.............#
18
- #........#.....#O.......G#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#.........................##.....#
18
+ #........#.....#O...m...G#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.............t...........##.....#
21
21
  #.....#.............&............##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#........S.......................#
@@ -53,3 +53,7 @@ char_to_map_name:
53
53
  "=": chest
54
54
 
55
55
 
56
+ "a": aligner_station
57
+ "r": scrambler_station
58
+ "m": miner_station
59
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####...........C.................#
16
16
  #....S...#........................O....#
17
17
  #........#.....###.....###.............#
18
- #........#.....#O.......G#.............#
19
- #.....#........#.@..=..@.#......#......#
20
- #.....#................@........##.....#
18
+ #........#.....#O...m...G#.............#
19
+ #.....#........#.@.a=r.@.#......#......#
20
+ #.....#.............t..@........##.....#
21
21
  #.....#...C......@..&............##....#
22
22
  #.....#.............@..@..........#....#
23
23
  #.....#..........@.......@.............#
@@ -53,3 +53,7 @@ char_to_map_name:
53
53
  "=": chest
54
54
 
55
55
 
56
+ "a": aligner_station
57
+ "r": scrambler_station
58
+ "m": miner_station
59
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #........#.............................#
17
17
  #........#.....###.....###.............#
18
- #........#.....#.........#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#.........................##.....#
18
+ #........#.....#....m....#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.............t...........##.....#
21
21
  #.....#.............&............##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#................................#
@@ -53,3 +53,7 @@ char_to_map_name:
53
53
  "=": chest
54
54
 
55
55
 
56
+ "a": aligner_station
57
+ "r": scrambler_station
58
+ "m": miner_station
59
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #........#.............................#
17
17
  #........#.....###.....###...........G.#
18
- #........#.....#.........#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#......C..................##.....#
18
+ #........#.....#....m....#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#......C......t...........##.....#
21
21
  #.....#.............&............##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#................................#
@@ -53,3 +53,7 @@ char_to_map_name:
53
53
  "=": chest
54
54
 
55
55
 
56
+ "a": aligner_station
57
+ "r": scrambler_station
58
+ "m": miner_station
59
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #........#.............................#
17
17
  #........#.....###.....###.............#
18
- #........#.....#.........#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#.........................##.....#
18
+ #........#.....#....m....#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.............t...........##.....#
21
21
  #.....#.............&............##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#................................#
@@ -51,3 +51,7 @@ char_to_map_name:
51
51
  "@": agent.agent
52
52
  "&": hub
53
53
  "=": chest
54
+ "a": aligner_station
55
+ "r": scrambler_station
56
+ "m": miner_station
57
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #...S....#........................S....#
17
17
  #........#.....###.....###.............#
18
- #........#.....#.........#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#.........................##.....#
18
+ #........#.....#....m....#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.............t...........##.....#
21
21
  #.....#...O.........&............##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#................................#
@@ -51,3 +51,7 @@ char_to_map_name:
51
51
  "@": agent.agent
52
52
  "&": hub
53
53
  "=": chest
54
+ "a": aligner_station
55
+ "r": scrambler_station
56
+ "m": miner_station
57
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #........#.............................#
17
17
  #........#.....###.....###.............#
18
- #........#.....#.........#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#.........................##.....#
18
+ #........#.....#....m....#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.............t...........##.....#
21
21
  #.....#.............&....@.......##....#
22
22
  #.....#.............@.............#....#
23
23
  #.....#.........@.......@..............#
@@ -54,3 +54,7 @@ char_to_map_name:
54
54
 
55
55
 
56
56
 
57
+ "a": aligner_station
58
+ "r": scrambler_station
59
+ "m": miner_station
60
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.............................#
16
16
  #........#.............................#
17
17
  #........#.....###.....###.............#
18
- #........#.....#........G#.............#
19
- #.....#........#....=....#......#......#
20
- #.....#.........................##.....#
18
+ #........#.....#....m...G#.............#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.............t...........##.....#
21
21
  #.....#.............&............##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#................................#
@@ -53,3 +53,7 @@ char_to_map_name:
53
53
  "=": chest
54
54
 
55
55
 
56
+ "a": aligner_station
57
+ "r": scrambler_station
58
+ "m": miner_station
59
+ "t": scout_station
@@ -15,9 +15,9 @@ map_data: |-
15
15
  #.....####.C....O......................#
16
16
  #C.......#.......................G.....#
17
17
  #......C.#.....###.....###.............#
18
- #........#.....#O.......G#...........O.#
19
- #.....#........#....=....#......#......#
20
- #.....#.....G...................##.....#
18
+ #........#.....#O...m...G#...........O.#
19
+ #.....#........#...a=r...#......#......#
20
+ #.....#.....G.......t...........##.....#
21
21
  #....C#.............&....S.......##....#
22
22
  #.....#.............@@@@@@@@......#....#
23
23
  #.....#................................#
@@ -53,3 +53,7 @@ char_to_map_name:
53
53
  "=": chest
54
54
 
55
55
 
56
+ "a": aligner_station
57
+ "r": scrambler_station
58
+ "m": miner_station
59
+ "t": scout_station
@@ -55,10 +55,10 @@ map_data: |-
55
55
  #####............###............#...###.....#.........@.........#...#.......#.....#..###...##...#.##...#######
56
56
  ########..#..###.##.#.#.....#.S....#.##..##.......................#.#....#..#.G.......##.#####.###.....#######
57
57
  ######........##......#..#..#......#.#.....#........@.&.@........##...##.#.##.........##....#..##.#....#######
58
- #####....##.C.#........###..#.#...##.###..#......................#...#......##......C.####...#.#..#.....######
58
+ #####....##.C.#........###..#.#...##.###..#...........m..........#...#......##......C.####...#.#..#.....######
59
59
  ######...#........#...........#.......#..####.........@.........#.#..#..####.#........####.....##.......######
60
- #######.........#..##.G.#...#.####....#.....#.........=.........#..##..#####......+.#...#.##..##....S...######
61
- ########.#.##..#...##.....###.###.......##..#...................#.......#....#............#.............######
60
+ #######.........#..##.G.#...#.####....#.....#........a=r........#..##..#####......+.#...#.##..##....S...######
61
+ ########.#.##..#...##.....###.###.......##..#.........t.........#.......#....#............#.............######
62
62
  #######.#######....##..#.######.....##..##..#...................####..G.##.....##...###..#..###....#..########
63
63
  ######......#....#..#.####.###..###....####.#...................#####...##.##.##...##.....#######.#..#########
64
64
  #####.....O...O.....#.###...##.##.....#..####...................#...#...#####...#..##.##....#..####..##..#####
@@ -121,3 +121,7 @@ char_to_map_name:
121
121
  "S": silicon_extractor
122
122
  "O": oxygen_extractor
123
123
  "C": carbon_extractor
124
+ "a": aligner_station
125
+ "r": scrambler_station
126
+ "m": miner_station
127
+ "t": scout_station
@@ -20,10 +20,10 @@ map_data: |-
20
20
  ########.#......@..@....@....#....######
21
21
  ########........................+..#####
22
22
  ######...........@.&.@.............#####
23
- ######..#..........................#####
23
+ ######..#..........m...............#####
24
24
  #####..###......@..@.@.......###..######
25
- #####..###.........=.........##..#######
26
- #####..###...................#..########
25
+ #####..###........a=r........##..#######
26
+ #####..###.........t.........#..########
27
27
  #####...##...................#..########
28
28
  ######..##...................#.#########
29
29
  #######..#...................#.....#####
@@ -51,3 +51,7 @@ char_to_map_name:
51
51
  "S": silicon_extractor
52
52
  "O": oxygen_extractor
53
53
  "C": carbon_extractor
54
+ "a": aligner_station
55
+ "r": scrambler_station
56
+ "m": miner_station
57
+ "t": scout_station
@@ -30,10 +30,10 @@ map_data: |-
30
30
  #####...#....#.....#..@......@.....@...#.#..#.......########
31
31
  #####.....#.............................##..##.....#########
32
32
  #####..........#...........@.&.@..............##.###########
33
- ######..##.G..###........................C....##.###########
33
+ ######..##.G..###............m...........C....##.###########
34
34
  ######.###.....##..#.....@...@.........#..........#...######
35
- #####..##....G.....#.........=.........#........##.....#####
36
- #####..#...........#...................#........##.....#####
35
+ #####..##....G.....#........a=r........#........##.....#####
36
+ #####..#...........#.........t.........#........##.....#####
37
37
  #####...#.....#....#...................#......#..#.O..######
38
38
  #####....####.##...#...................#..............######
39
39
  #####....##........#...................#..............######
@@ -71,3 +71,7 @@ char_to_map_name:
71
71
  "S": silicon_extractor
72
72
  "O": oxygen_extractor
73
73
  "C": carbon_extractor
74
+ "a": aligner_station
75
+ "r": scrambler_station
76
+ "m": miner_station
77
+ "t": scout_station