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
cogames/cli/mission.py
CHANGED
|
@@ -9,51 +9,43 @@ from rich.table import Table
|
|
|
9
9
|
|
|
10
10
|
from cogames.cli.base import console
|
|
11
11
|
from cogames.cogs_vs_clips.mission import (
|
|
12
|
-
|
|
13
|
-
AnyMission,
|
|
14
|
-
Mission,
|
|
15
|
-
MissionVariant,
|
|
12
|
+
CvCMission,
|
|
16
13
|
NumCogsVariant,
|
|
17
|
-
Site,
|
|
18
14
|
)
|
|
19
|
-
from cogames.cogs_vs_clips.procedural import MachinaArena
|
|
20
15
|
from cogames.cogs_vs_clips.sites import SITES
|
|
16
|
+
from cogames.cogs_vs_clips.terrain import MachinaArena
|
|
21
17
|
from cogames.cogs_vs_clips.variants import HIDDEN_VARIANTS, VARIANTS
|
|
18
|
+
from cogames.core import (
|
|
19
|
+
MAP_MISSION_DELIMITER,
|
|
20
|
+
CoGameMissionVariant,
|
|
21
|
+
CoGameSite,
|
|
22
|
+
)
|
|
22
23
|
from cogames.game import load_mission_config, load_mission_config_from_python
|
|
23
24
|
from mettagrid import MettaGridConfig
|
|
24
25
|
from mettagrid.mapgen.mapgen import MapGen
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
@lru_cache(maxsize=1)
|
|
28
|
-
def _get_core_missions() -> list[
|
|
29
|
+
def _get_core_missions() -> list[CvCMission]:
|
|
29
30
|
from cogames.cogs_vs_clips.missions import get_core_missions
|
|
30
31
|
|
|
31
32
|
return get_core_missions()
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
@lru_cache(maxsize=1)
|
|
35
|
-
def
|
|
36
|
-
from cogames.cogs_vs_clips.missions import get_legacy_missions
|
|
37
|
-
|
|
38
|
-
return get_legacy_missions()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@lru_cache(maxsize=1)
|
|
42
|
-
def _get_eval_missions_all() -> list[AnyMission]:
|
|
43
|
-
from cogames.cogs_vs_clips.evals.cogsguard_evals import COGSGUARD_EVAL_MISSIONS
|
|
36
|
+
def _get_eval_missions_all() -> list[CvCMission]:
|
|
44
37
|
from cogames.cogs_vs_clips.evals.diagnostic_evals import DIAGNOSTIC_EVALS
|
|
45
38
|
from cogames.cogs_vs_clips.evals.integrated_evals import EVAL_MISSIONS as INTEGRATED_EVAL_MISSIONS
|
|
46
39
|
from cogames.cogs_vs_clips.evals.spanning_evals import EVAL_MISSIONS as SPANNING_EVAL_MISSIONS
|
|
47
40
|
|
|
48
|
-
missions: list[
|
|
49
|
-
missions.extend(COGSGUARD_EVAL_MISSIONS)
|
|
41
|
+
missions: list[CvCMission] = []
|
|
50
42
|
missions.extend(INTEGRATED_EVAL_MISSIONS)
|
|
51
43
|
missions.extend(SPANNING_EVAL_MISSIONS)
|
|
52
44
|
missions.extend(mission_cls() for mission_cls in DIAGNOSTIC_EVALS) # type: ignore[call-arg]
|
|
53
45
|
return missions
|
|
54
46
|
|
|
55
47
|
|
|
56
|
-
def load_mission_set(mission_set: str) -> list[
|
|
48
|
+
def load_mission_set(mission_set: str) -> list[CvCMission]:
|
|
57
49
|
"""Load a predefined set of evaluation missions.
|
|
58
50
|
|
|
59
51
|
Args:
|
|
@@ -70,7 +62,7 @@ def load_mission_set(mission_set: str) -> list[AnyMission]:
|
|
|
70
62
|
Raises:
|
|
71
63
|
ValueError: If mission_set name is unknown
|
|
72
64
|
"""
|
|
73
|
-
missions_list: list[
|
|
65
|
+
missions_list: list[CvCMission]
|
|
74
66
|
if mission_set == "all":
|
|
75
67
|
# All missions: eval missions + integrated + spanning + diagnostic + core missions
|
|
76
68
|
missions_list = list(_get_eval_missions_all())
|
|
@@ -104,14 +96,14 @@ def load_mission_set(mission_set: str) -> list[AnyMission]:
|
|
|
104
96
|
return missions_list
|
|
105
97
|
|
|
106
98
|
|
|
107
|
-
def parse_variants(variants_arg: Optional[list[str]]) -> list[
|
|
99
|
+
def parse_variants(variants_arg: Optional[list[str]]) -> list[CoGameMissionVariant]:
|
|
108
100
|
"""Parse variant specifications from command line.
|
|
109
101
|
|
|
110
102
|
Args:
|
|
111
103
|
variants_arg: List of variant names like ["solar_flare", "dark_side"]
|
|
112
104
|
|
|
113
105
|
Returns:
|
|
114
|
-
List of configured
|
|
106
|
+
List of configured CoGameMissionVariant instances
|
|
115
107
|
|
|
116
108
|
Raises:
|
|
117
109
|
ValueError: If variant name is unknown
|
|
@@ -119,11 +111,11 @@ def parse_variants(variants_arg: Optional[list[str]]) -> list[MissionVariant]:
|
|
|
119
111
|
if not variants_arg:
|
|
120
112
|
return []
|
|
121
113
|
|
|
122
|
-
variants: list[
|
|
114
|
+
variants: list[CoGameMissionVariant] = []
|
|
123
115
|
all_variants = [*VARIANTS, *HIDDEN_VARIANTS]
|
|
124
116
|
for name in variants_arg:
|
|
125
117
|
# Find matching variant class by instantiating and checking the name
|
|
126
|
-
variant:
|
|
118
|
+
variant: CoGameMissionVariant | None = None
|
|
127
119
|
for v in all_variants:
|
|
128
120
|
if v.name == name:
|
|
129
121
|
variant = v
|
|
@@ -150,7 +142,7 @@ def get_all_eval_missions() -> list[str]:
|
|
|
150
142
|
return [mission.full_name() for mission in _get_eval_missions_all()]
|
|
151
143
|
|
|
152
144
|
|
|
153
|
-
def get_site_by_name(site_name: str) ->
|
|
145
|
+
def get_site_by_name(site_name: str) -> CoGameSite:
|
|
154
146
|
"""Get a site by name.
|
|
155
147
|
|
|
156
148
|
Raises:
|
|
@@ -166,7 +158,7 @@ def get_site_by_name(site_name: str) -> Site:
|
|
|
166
158
|
|
|
167
159
|
def get_mission_name_and_config(
|
|
168
160
|
ctx: typer.Context, mission_arg: Optional[str], variants_arg: Optional[list[str]] = None, cogs: Optional[int] = None
|
|
169
|
-
) -> tuple[str, MettaGridConfig, Optional[
|
|
161
|
+
) -> tuple[str, MettaGridConfig, Optional[CvCMission]]:
|
|
170
162
|
if not mission_arg:
|
|
171
163
|
console.print(ctx.get_help())
|
|
172
164
|
console.print("[yellow]Missing: --mission / -m[/yellow]\n")
|
|
@@ -253,12 +245,10 @@ def find_mission(
|
|
|
253
245
|
*,
|
|
254
246
|
include_evals: bool = False,
|
|
255
247
|
include_legacy: bool = False,
|
|
256
|
-
) ->
|
|
257
|
-
missions: list[
|
|
248
|
+
) -> CvCMission:
|
|
249
|
+
missions: list[CvCMission] = list(_get_core_missions())
|
|
258
250
|
if include_evals:
|
|
259
251
|
missions = [*missions, *_get_eval_missions_all()]
|
|
260
|
-
if include_legacy:
|
|
261
|
-
missions = [*missions, *_get_legacy_missions()]
|
|
262
252
|
|
|
263
253
|
found_site = False
|
|
264
254
|
for mission in missions:
|
|
@@ -288,7 +278,7 @@ def get_mission(
|
|
|
288
278
|
variants_arg: Optional[list[str]] = None,
|
|
289
279
|
cogs: Optional[int] = None,
|
|
290
280
|
include_legacy: bool = False,
|
|
291
|
-
) -> tuple[str, MettaGridConfig, Optional[
|
|
281
|
+
) -> tuple[str, MettaGridConfig, Optional[CvCMission]]:
|
|
292
282
|
"""Get a specific mission configuration by name or file path.
|
|
293
283
|
|
|
294
284
|
Args:
|
|
@@ -298,7 +288,7 @@ def get_mission(
|
|
|
298
288
|
include_legacy: Whether to include legacy (pre-CogsGuard) missions
|
|
299
289
|
|
|
300
290
|
Returns:
|
|
301
|
-
Tuple of (mission name, MettaGridConfig,
|
|
291
|
+
Tuple of (mission name, MettaGridConfig, CvCMission or None)
|
|
302
292
|
|
|
303
293
|
Raises:
|
|
304
294
|
ValueError: If mission not found or file cannot be loaded
|
|
@@ -330,7 +320,7 @@ def get_mission(
|
|
|
330
320
|
else:
|
|
331
321
|
site_name, mission_name = mission_arg.split(MAP_MISSION_DELIMITER)
|
|
332
322
|
|
|
333
|
-
mission:
|
|
323
|
+
mission: CvCMission = find_mission(site_name, mission_name, include_evals=True, include_legacy=include_legacy)
|
|
334
324
|
|
|
335
325
|
if variants:
|
|
336
326
|
mission = mission.with_variants(variants)
|
|
@@ -454,7 +444,7 @@ def list_evals() -> None:
|
|
|
454
444
|
return
|
|
455
445
|
|
|
456
446
|
# Group missions by site
|
|
457
|
-
missions_by_site: dict[str, list[
|
|
447
|
+
missions_by_site: dict[str, list[CvCMission]] = {}
|
|
458
448
|
for m in evals:
|
|
459
449
|
missions_by_site.setdefault(m.site.name, []).append(m)
|
|
460
450
|
|
|
@@ -506,7 +496,7 @@ def list_evals() -> None:
|
|
|
506
496
|
console.print(" [bold]cogames play[/bold] --mission [blue]evals.divide_and_conquer[/blue]")
|
|
507
497
|
|
|
508
498
|
|
|
509
|
-
def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cfg:
|
|
499
|
+
def describe_mission(mission_name: str, game_config: MettaGridConfig, mission_cfg: CvCMission | None = None) -> None:
|
|
510
500
|
"""Print detailed information about a specific mission.
|
|
511
501
|
|
|
512
502
|
Args:
|
cogames/cli/submit.py
CHANGED
|
@@ -290,6 +290,7 @@ def validate_policy_in_isolation(
|
|
|
290
290
|
"run",
|
|
291
291
|
"cogames",
|
|
292
292
|
"validate-policy",
|
|
293
|
+
"--policy",
|
|
293
294
|
policy_arg,
|
|
294
295
|
"--season",
|
|
295
296
|
season,
|
|
@@ -298,7 +299,6 @@ def validate_policy_in_isolation(
|
|
|
298
299
|
]
|
|
299
300
|
if setup_script:
|
|
300
301
|
validate_cmd.extend(["--setup-script", setup_script])
|
|
301
|
-
validate_cmd.extend(["--policy", policy_arg])
|
|
302
302
|
|
|
303
303
|
_run_from_tmp_dir(validate_cmd)
|
|
304
304
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Clips behavior events for CogsGuard missions.
|
|
2
|
+
|
|
3
|
+
Clips are a non-player faction that gradually takes over neutral junctions.
|
|
4
|
+
These events create the spreading/scrambling behavior that pressures players.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from mettagrid.base_config import Config
|
|
10
|
+
from mettagrid.config.event_config import EventConfig, once, periodic
|
|
11
|
+
from mettagrid.config.filter import isAlignedTo, isNear
|
|
12
|
+
from mettagrid.config.filter.alignment_filter import isNeutral, isNotAlignedTo, isNotNeutral
|
|
13
|
+
from mettagrid.config.mettagrid_config import CollectiveConfig
|
|
14
|
+
from mettagrid.config.mutation import alignTo, removeAlignment
|
|
15
|
+
from mettagrid.config.tag import typeTag
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ClipsConfig(Config):
|
|
19
|
+
"""Configuration for clips behavior in CogsGuard game mode."""
|
|
20
|
+
|
|
21
|
+
# Clips Behavior - scramble cogs junctions to neutral
|
|
22
|
+
initial_clips_start: int = Field(default=10)
|
|
23
|
+
initial_clips_spots: int = Field(default=1)
|
|
24
|
+
|
|
25
|
+
scramble_start: int = Field(default=50)
|
|
26
|
+
scramble_interval: int = Field(default=100)
|
|
27
|
+
scramble_radius: int = Field(default=25)
|
|
28
|
+
|
|
29
|
+
# Clips Behavior - align neutral junctions to clips
|
|
30
|
+
align_start: int = Field(default=100)
|
|
31
|
+
align_interval: int = Field(default=100)
|
|
32
|
+
align_radius: int = Field(default=25)
|
|
33
|
+
|
|
34
|
+
def events(self, max_steps: int) -> dict[str, EventConfig]:
|
|
35
|
+
"""Create all clips events for a mission.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Dictionary of event name to EventConfig.
|
|
39
|
+
"""
|
|
40
|
+
return {
|
|
41
|
+
"initial_clips": EventConfig(
|
|
42
|
+
name="initial_clips",
|
|
43
|
+
target_tag=typeTag("junction"),
|
|
44
|
+
timesteps=once(self.initial_clips_start),
|
|
45
|
+
mutations=[alignTo("clips")],
|
|
46
|
+
max_targets=self.initial_clips_spots,
|
|
47
|
+
),
|
|
48
|
+
"cogs_to_neutral": EventConfig(
|
|
49
|
+
name="cogs_to_neutral",
|
|
50
|
+
target_tag=typeTag("junction"),
|
|
51
|
+
timesteps=periodic(start=self.scramble_start, period=self.scramble_interval, end=max_steps),
|
|
52
|
+
# near a clips-aligned junction
|
|
53
|
+
filters=[
|
|
54
|
+
isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.scramble_radius),
|
|
55
|
+
isNotAlignedTo("clips"),
|
|
56
|
+
isNotNeutral(),
|
|
57
|
+
],
|
|
58
|
+
mutations=[removeAlignment()],
|
|
59
|
+
max_targets=1,
|
|
60
|
+
),
|
|
61
|
+
"neutral_to_clips": EventConfig(
|
|
62
|
+
name="neutral_to_clips",
|
|
63
|
+
target_tag=typeTag("junction"),
|
|
64
|
+
timesteps=periodic(start=self.align_start, period=self.align_interval, end=max_steps),
|
|
65
|
+
# neutral junctions near a clips-aligned junction
|
|
66
|
+
filters=[
|
|
67
|
+
isNear(typeTag("junction"), [isAlignedTo("clips")], radius=self.align_radius),
|
|
68
|
+
isNeutral(),
|
|
69
|
+
],
|
|
70
|
+
mutations=[alignTo("clips")],
|
|
71
|
+
max_targets=1,
|
|
72
|
+
),
|
|
73
|
+
# If there are no clips-aligned junctions, re-invade
|
|
74
|
+
"presence_check": EventConfig(
|
|
75
|
+
name="presence_check",
|
|
76
|
+
target_tag=typeTag("junction"),
|
|
77
|
+
timesteps=periodic(start=self.initial_clips_start, period=self.scramble_interval * 2, end=max_steps),
|
|
78
|
+
filters=[isNear(typeTag("junction"), [isAlignedTo("clips")], radius=1000)],
|
|
79
|
+
max_targets=1,
|
|
80
|
+
fallback="initial_clips",
|
|
81
|
+
),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
def collective_config(self) -> CollectiveConfig:
|
|
85
|
+
"""Create a CollectiveConfig for this clips configuration."""
|
|
86
|
+
return CollectiveConfig(name="clips")
|
cogames/cogs_vs_clips/cog.py
CHANGED
|
@@ -2,7 +2,9 @@ from __future__ import annotations
|
|
|
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
|
|
7
|
+
from mettagrid.config.game_value import stat as game_stat
|
|
6
8
|
from mettagrid.config.handler_config import Handler
|
|
7
9
|
from mettagrid.config.mettagrid_config import (
|
|
8
10
|
AgentConfig,
|
|
@@ -10,6 +12,7 @@ from mettagrid.config.mettagrid_config import (
|
|
|
10
12
|
ResourceLimitsConfig,
|
|
11
13
|
)
|
|
12
14
|
from mettagrid.config.mutation.resource_mutation import updateActor
|
|
15
|
+
from mettagrid.config.reward_config import reward
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
class CogConfig(Config):
|
|
@@ -37,25 +40,23 @@ class CogConfig(Config):
|
|
|
37
40
|
energy_regen: int = Field(default=1)
|
|
38
41
|
hp_regen: int = Field(default=-1)
|
|
39
42
|
influence_regen: int = Field(default=-1)
|
|
43
|
+
action_cost: dict[str, int] = Field(default_factory=lambda: {"energy": 3})
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
move_energy_cost: int = Field(default=3)
|
|
43
|
-
|
|
44
|
-
def agent_config(self, gear: list[str], elements: list[str]) -> AgentConfig:
|
|
45
|
+
def agent_config(self, team: str, max_steps: int) -> AgentConfig:
|
|
45
46
|
"""Create an AgentConfig for this cog configuration."""
|
|
46
47
|
return AgentConfig(
|
|
47
|
-
collective=
|
|
48
|
+
collective=team,
|
|
48
49
|
inventory=InventoryConfig(
|
|
49
50
|
limits={
|
|
50
51
|
"hp": ResourceLimitsConfig(min=self.hp_limit, resources=["hp"], modifiers=self.hp_modifiers),
|
|
51
52
|
# when hp == 0, the cog can't hold gear or hearts
|
|
52
|
-
"gear": ResourceLimitsConfig(max=self.gear_limit, resources=
|
|
53
|
+
"gear": ResourceLimitsConfig(max=self.gear_limit, resources=CvCConfig.GEAR, modifiers={"hp": 100}),
|
|
53
54
|
"heart": ResourceLimitsConfig(max=self.heart_limit, resources=["heart"], modifiers={"hp": 100}),
|
|
54
55
|
"energy": ResourceLimitsConfig(
|
|
55
56
|
min=self.energy_limit, resources=["energy"], modifiers=self.energy_modifiers
|
|
56
57
|
),
|
|
57
58
|
"cargo": ResourceLimitsConfig(
|
|
58
|
-
min=self.cargo_limit, resources=
|
|
59
|
+
min=self.cargo_limit, resources=CvCConfig.ELEMENTS, modifiers=self.cargo_modifiers
|
|
59
60
|
),
|
|
60
61
|
"influence": ResourceLimitsConfig(
|
|
61
62
|
min=self.influence_limit, resources=["influence"], modifiers=self.influence_modifiers
|
|
@@ -76,4 +77,10 @@ class CogConfig(Config):
|
|
|
76
77
|
]
|
|
77
78
|
)
|
|
78
79
|
},
|
|
80
|
+
rewards={
|
|
81
|
+
"aligned_junction_held": reward(
|
|
82
|
+
game_stat("collective.aligned.junction.held"),
|
|
83
|
+
weight=1.0 / max_steps,
|
|
84
|
+
),
|
|
85
|
+
},
|
|
79
86
|
)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""CogsGuard tutorial mission configuration."""
|
|
2
2
|
|
|
3
|
-
from cogames.cogs_vs_clips.mission import
|
|
4
|
-
from cogames.cogs_vs_clips.
|
|
3
|
+
from cogames.cogs_vs_clips.mission import CvCMission
|
|
4
|
+
from cogames.cogs_vs_clips.team import CogTeam
|
|
5
|
+
from cogames.cogs_vs_clips.terrain import MachinaArena
|
|
6
|
+
from cogames.core import CoGameSite
|
|
5
7
|
from mettagrid.mapgen.mapgen import MapGen
|
|
6
8
|
from mettagrid.mapgen.scenes.base_hub import BaseHubConfig
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
def make_cogsguard_tutorial_site() ->
|
|
11
|
+
def make_cogsguard_tutorial_site() -> CoGameSite:
|
|
10
12
|
"""Create a smaller, simpler CogsGuard arena for the tutorial."""
|
|
11
13
|
hub_config = BaseHubConfig(
|
|
12
14
|
corner_bundle="extractors",
|
|
@@ -32,7 +34,7 @@ def make_cogsguard_tutorial_site() -> Site:
|
|
|
32
34
|
hub=hub_config,
|
|
33
35
|
),
|
|
34
36
|
)
|
|
35
|
-
return
|
|
37
|
+
return CoGameSite(
|
|
36
38
|
name="cogsguard_tutorial",
|
|
37
39
|
description="CogsGuard tutorial arena - small map for learning",
|
|
38
40
|
map_builder=map_builder,
|
|
@@ -41,16 +43,13 @@ def make_cogsguard_tutorial_site() -> Site:
|
|
|
41
43
|
)
|
|
42
44
|
|
|
43
45
|
|
|
44
|
-
CogsGuardTutorialMission =
|
|
46
|
+
CogsGuardTutorialMission = CvCMission(
|
|
45
47
|
name="tutorial",
|
|
46
48
|
description="Learn the basics of CogsGuard: Roles, Resources, and Territory Control.",
|
|
47
49
|
site=make_cogsguard_tutorial_site(),
|
|
48
50
|
num_cogs=1,
|
|
49
51
|
max_steps=2000,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
collective_initial_germanium=50,
|
|
54
|
-
collective_initial_silicon=50,
|
|
55
|
-
collective_initial_heart=10,
|
|
52
|
+
teams={
|
|
53
|
+
"cogs": CogTeam(name="cogs", num_agents=1, wealth=1),
|
|
54
|
+
},
|
|
56
55
|
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from types import SimpleNamespace
|
|
2
|
+
|
|
3
|
+
from mettagrid.config.vibes import Vibe
|
|
4
|
+
|
|
5
|
+
_GEAR = ["aligner", "scrambler", "miner", "scout"]
|
|
6
|
+
_ELEMENTS = ["oxygen", "carbon", "germanium", "silicon"]
|
|
7
|
+
_VIBES = [
|
|
8
|
+
Vibe("😐", "default"),
|
|
9
|
+
Vibe("❤️", "heart"),
|
|
10
|
+
Vibe("⚙️", "gear"),
|
|
11
|
+
Vibe("🌀", "scrambler"),
|
|
12
|
+
Vibe("🔗", "aligner"),
|
|
13
|
+
Vibe("⛏️", "miner"),
|
|
14
|
+
Vibe("🔭", "scout"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
CvCConfig = SimpleNamespace(
|
|
18
|
+
GEAR=_GEAR,
|
|
19
|
+
ELEMENTS=_ELEMENTS,
|
|
20
|
+
HEART_COST={e: 10 for e in _ELEMENTS},
|
|
21
|
+
ALIGN_COST={"heart": 1},
|
|
22
|
+
SCRAMBLE_COST={"heart": 1},
|
|
23
|
+
GEAR_COSTS={
|
|
24
|
+
"aligner": {"carbon": 3, "oxygen": 1, "germanium": 1, "silicon": 1},
|
|
25
|
+
"scrambler": {"carbon": 1, "oxygen": 3, "germanium": 1, "silicon": 1},
|
|
26
|
+
"miner": {"carbon": 1, "oxygen": 1, "germanium": 3, "silicon": 1},
|
|
27
|
+
"scout": {"carbon": 1, "oxygen": 1, "germanium": 1, "silicon": 3},
|
|
28
|
+
},
|
|
29
|
+
GEAR_SYMBOLS={
|
|
30
|
+
"aligner": "🔗",
|
|
31
|
+
"scrambler": "🌀",
|
|
32
|
+
"miner": "⛏️",
|
|
33
|
+
"scout": "🔭",
|
|
34
|
+
},
|
|
35
|
+
RESOURCES=["energy", "heart", "hp", "influence", *_ELEMENTS, *_GEAR],
|
|
36
|
+
VIBES=_VIBES,
|
|
37
|
+
VIBE_NAMES=[vibe.name for vibe in _VIBES],
|
|
38
|
+
)
|
|
@@ -324,7 +324,6 @@ Key design choices:
|
|
|
324
324
|
|
|
325
325
|
- Pack the base hub lightly (`EmptyBaseVariant`) where appropriate to encourage early exploration without
|
|
326
326
|
over-constraining.
|
|
327
|
-
- Add guidance (`CompassVariant`) on distance-heavy tasks to reduce pure exploration failure modes.
|
|
328
327
|
- Raise agent caps modestly (`PackRatVariant`) to avoid early inventory stalls but keep routing relevant.
|
|
329
328
|
- Shape reward on vibe missions (`HeartChorusVariant`) so partial progress is scored.
|
|
330
329
|
- Keep vibe mechanics intact unless the mission explicitly focuses on vibe manipulation.
|
|
@@ -334,9 +333,9 @@ Included missions and variants:
|
|
|
334
333
|
- oxygen_bottleneck: `EmptyBaseVariant(missing=["oxygen_extractor"])`, `ResourceBottleneckVariant(["oxygen"])`,
|
|
335
334
|
`SingleResourceUniformVariant("oxygen_extractor")`, `PackRatVariant`
|
|
336
335
|
- energy_starved: `EmptyBaseVariant`, `DarkSideVariant`, `PackRatVariant`
|
|
337
|
-
- distant_resources: `EmptyBaseVariant`, `
|
|
338
|
-
- quadrant_buildings: `EmptyBaseVariant`, `QuadrantBuildingsVariant
|
|
339
|
-
- single_use_swarm: `EmptyBaseVariant`, `SingleUseSwarmVariant`, `
|
|
336
|
+
- distant_resources: `EmptyBaseVariant`, `DistantResourcesVariant`
|
|
337
|
+
- quadrant_buildings: `EmptyBaseVariant`, `QuadrantBuildingsVariant`
|
|
338
|
+
- single_use_swarm: `EmptyBaseVariant`, `SingleUseSwarmVariant`, `PackRatVariant`
|
|
340
339
|
- vibe_check: `HeartChorusVariant`, `VibeCheckMin2Variant`
|
|
341
340
|
|
|
342
341
|
Usage example:
|
|
@@ -349,6 +348,6 @@ uv run python packages/cogames/scripts/run_evaluation.py \
|
|
|
349
348
|
--repeats 2
|
|
350
349
|
```
|
|
351
350
|
|
|
352
|
-
Recommendation: When designing new scorable baselines, combine one
|
|
353
|
-
`
|
|
354
|
-
`
|
|
351
|
+
Recommendation: When designing new scorable baselines, combine one "shaping" variant (e.g., `HeartChorusVariant`,
|
|
352
|
+
`PackRatVariant`) with one "constraint" variant (e.g., `DarkSideVariant`, `ResourceBottleneckVariant`,
|
|
353
|
+
`SingleUseSwarmVariant`) to keep tasks legible yet challenging.
|
|
@@ -179,11 +179,11 @@ uv run cogames play --mission hello_world.easy_hearts --cogs 2
|
|
|
179
179
|
You can apply additional variants on top of the mission's built-in variants:
|
|
180
180
|
|
|
181
181
|
```bash
|
|
182
|
-
# Add
|
|
183
|
-
uv run cogames play --mission hello_world.oxygen_bottleneck --cogs 2 --variant
|
|
182
|
+
# Add pack_rat variant
|
|
183
|
+
uv run cogames play --mission hello_world.oxygen_bottleneck --cogs 2 --variant pack_rat
|
|
184
184
|
|
|
185
185
|
# Add multiple variants
|
|
186
|
-
uv run cogames play --mission hello_world.energy_starved --cogs 2 --variant
|
|
186
|
+
uv run cogames play --mission hello_world.energy_starved --cogs 2 --variant pack_rat --variant small_50
|
|
187
187
|
|
|
188
188
|
# With policy
|
|
189
189
|
uv run cogames play --mission hello_world.single_use_swarm --cogs 4 -p baseline
|
|
@@ -230,7 +230,7 @@ mission_variant_curriculum.train(
|
|
|
230
230
|
mission_variant_curriculum.train(
|
|
231
231
|
base_missions=["oxygen_bottleneck", "energy_starved"],
|
|
232
232
|
num_cogs=4,
|
|
233
|
-
variants=["
|
|
233
|
+
variants=["pack_rat", "energized"]
|
|
234
234
|
)
|
|
235
235
|
```
|
|
236
236
|
|
|
@@ -2,15 +2,30 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from cogames.cogs_vs_clips.mission import
|
|
6
|
-
from cogames.
|
|
5
|
+
from cogames.cogs_vs_clips.mission import CvCMission
|
|
6
|
+
from cogames.core import CoGameSite as Site
|
|
7
|
+
from mettagrid.map_builder.map_builder import MapBuilderConfig
|
|
8
|
+
from mettagrid.mapgen.mapgen import MapGen, MapGenConfig
|
|
7
9
|
|
|
8
10
|
MAPS_DIR = Path(__file__).resolve().parent.parent.parent / "maps"
|
|
9
11
|
|
|
12
|
+
|
|
13
|
+
def _load_map(map_name: str) -> MapGenConfig:
|
|
14
|
+
map_path = MAPS_DIR / map_name
|
|
15
|
+
if not map_path.exists():
|
|
16
|
+
raise FileNotFoundError(f"Map not found: {map_path}")
|
|
17
|
+
return MapGen.Config(
|
|
18
|
+
instance=MapBuilderConfig.from_uri(str(map_path)),
|
|
19
|
+
instances=1,
|
|
20
|
+
fixed_spawn_order=False,
|
|
21
|
+
instance_border_width=0,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
10
25
|
COGSGUARD_EVALS_BASE = Site(
|
|
11
26
|
name="cogsguard_evals",
|
|
12
27
|
description="CogsGuard evaluation arenas.",
|
|
13
|
-
map_builder=
|
|
28
|
+
map_builder=_load_map("evals/eval_balanced_spread.map"),
|
|
14
29
|
min_cogs=1,
|
|
15
30
|
max_cogs=20,
|
|
16
31
|
)
|
|
@@ -30,7 +45,7 @@ def _count_spawn_pads(map_path: Path) -> int:
|
|
|
30
45
|
def _make_eval_site(map_name: str, num_cogs: int) -> Site:
|
|
31
46
|
site = COGSGUARD_EVALS_BASE.model_copy(
|
|
32
47
|
update={
|
|
33
|
-
"map_builder":
|
|
48
|
+
"map_builder": _load_map(map_name),
|
|
34
49
|
"min_cogs": num_cogs,
|
|
35
50
|
"max_cogs": num_cogs,
|
|
36
51
|
}
|
|
@@ -66,13 +81,13 @@ COGSGUARD_EVAL_MAPS: list[str] = [
|
|
|
66
81
|
|
|
67
82
|
COGSGUARD_EVAL_COGS = {map_name: _count_spawn_pads(MAPS_DIR / map_name) for map_name in COGSGUARD_EVAL_MAPS}
|
|
68
83
|
|
|
69
|
-
COGSGUARD_EVAL_MISSIONS: list[
|
|
84
|
+
COGSGUARD_EVAL_MISSIONS: list[CvCMission] = []
|
|
70
85
|
for map_name in COGSGUARD_EVAL_MAPS:
|
|
71
86
|
stem = Path(map_name).stem
|
|
72
87
|
num_cogs = COGSGUARD_EVAL_COGS[map_name]
|
|
73
88
|
site = _make_eval_site(map_name, num_cogs)
|
|
74
89
|
COGSGUARD_EVAL_MISSIONS.append(
|
|
75
|
-
|
|
90
|
+
CvCMission(
|
|
76
91
|
name=stem,
|
|
77
92
|
description=_description_from_stem(stem),
|
|
78
93
|
site=site,
|