cogames 0.3.64__py3-none-any.whl → 0.3.65__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cogames/cli/mission.py +25 -35
- cogames/cli/submit.py +1 -1
- cogames/cogs_vs_clips/clips.py +86 -0
- cogames/cogs_vs_clips/cog.py +14 -7
- cogames/cogs_vs_clips/cogsguard_tutorial.py +10 -11
- cogames/cogs_vs_clips/config.py +38 -0
- cogames/cogs_vs_clips/{cogs_vs_clips_mapgen.md → docs/cogs_vs_clips_mapgen.md} +6 -7
- cogames/cogs_vs_clips/evals/README.md +4 -4
- cogames/cogs_vs_clips/evals/cogsguard_evals.py +21 -6
- cogames/cogs_vs_clips/evals/diagnostic_evals.py +13 -100
- cogames/cogs_vs_clips/evals/difficulty_variants.py +9 -18
- cogames/cogs_vs_clips/evals/integrated_evals.py +8 -60
- cogames/cogs_vs_clips/evals/spanning_evals.py +48 -54
- cogames/cogs_vs_clips/mission.py +65 -277
- cogames/cogs_vs_clips/missions.py +16 -26
- cogames/cogs_vs_clips/sites.py +35 -25
- cogames/cogs_vs_clips/stations.py +33 -82
- cogames/cogs_vs_clips/team.py +44 -0
- cogames/cogs_vs_clips/{procedural.py → terrain.py} +12 -6
- cogames/cogs_vs_clips/variants.py +41 -118
- cogames/core.py +87 -0
- cogames/verbose.py +2 -2
- {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/METADATA +2 -2
- {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/RECORD +28 -27
- cogames/cogs_vs_clips/cogsguard_reward_variants.py +0 -153
- cogames/cogs_vs_clips/mission_utils.py +0 -19
- cogames/cogs_vs_clips/tutorial_missions.py +0 -25
- {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/WHEEL +0 -0
- {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/entry_points.txt +0 -0
- {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/licenses/LICENSE +0 -0
- {cogames-0.3.64.dist-info → cogames-0.3.65.dist-info}/top_level.txt +0 -0
|
@@ -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=
|
|
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) ->
|
|
99
|
-
return
|
|
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
|
|
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=
|
|
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
|
|
102
|
+
mutations=[collectiveDeposit({resource: 100 for resource in CvCConfig.ELEMENTS})],
|
|
152
103
|
),
|
|
153
104
|
"align": Handler(
|
|
154
|
-
filters=[isNeutral(), actorHas({"aligner": 1, "influence": 1, **
|
|
155
|
-
mutations=[updateActor(_neg(
|
|
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, **
|
|
159
|
-
mutations=[removeAlignment(), updateActor(_neg(
|
|
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
|
|
166
|
-
"""
|
|
116
|
+
class CvCHubConfig(CvCStationConfig):
|
|
117
|
+
"""Hub station that provides AOE influence/attack and accepts deposits."""
|
|
167
118
|
|
|
168
|
-
|
|
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=
|
|
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
|
|
197
|
-
"""Chest for heart management
|
|
150
|
+
class CvCChestConfig(CvCStationConfig):
|
|
151
|
+
"""Chest station for heart management."""
|
|
198
152
|
|
|
199
|
-
|
|
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=
|
|
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
|
|
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
|
-
|
|
229
|
-
|
|
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
|
-
|
|
236
|
-
|
|
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.
|
|
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](
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
@@ -1,95 +1,74 @@
|
|
|
1
|
-
from
|
|
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.
|
|
5
|
-
from cogames.
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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,
|
|
125
|
+
center1_x=0.92,
|
|
149
126
|
center1_y=0.08,
|
|
150
|
-
center2_x=0.08,
|
|
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,
|
|
133
|
+
center1_x=0.08,
|
|
157
134
|
center1_y=0.08,
|
|
158
|
-
center2_x=0.92,
|
|
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),
|
|
184
|
-
(0.75, 0.25),
|
|
185
|
-
(0.25, 0.75),
|
|
186
|
-
(0.75, 0.75),
|
|
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
|
-
|
|
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
|
-
|
|
306
|
-
HIDDEN_VARIANTS: list[MissionVariant] = [
|
|
307
|
-
# Example: ExperimentalVariant(), # keep empty by default
|
|
308
|
-
]
|
|
231
|
+
HIDDEN_VARIANTS: list[CoGameMissionVariant] = []
|