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.
- cogames/cli/client.py +0 -3
- cogames/cli/docsync/docsync.py +7 -1
- cogames/cli/mission.py +68 -53
- cogames/cli/policy.py +26 -10
- cogames/cli/submit.py +128 -142
- cogames/cli/utils.py +5 -0
- cogames/cogs_vs_clips/clip_difficulty.py +57 -0
- cogames/cogs_vs_clips/clips.py +103 -0
- cogames/cogs_vs_clips/cog.py +29 -11
- cogames/cogs_vs_clips/cogsguard_curriculum.py +122 -0
- cogames/cogs_vs_clips/cogsguard_tutorial.py +15 -16
- cogames/cogs_vs_clips/config.py +38 -0
- cogames/cogs_vs_clips/{cogs_vs_clips_mapgen.md → docs/cogs_vs_clips_mapgen.md} +8 -10
- cogames/cogs_vs_clips/evals/README.md +11 -35
- cogames/cogs_vs_clips/evals/cogsguard_evals.py +21 -6
- cogames/cogs_vs_clips/evals/diagnostic_evals.py +13 -101
- cogames/cogs_vs_clips/evals/difficulty_variants.py +16 -28
- 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 +93 -277
- cogames/cogs_vs_clips/missions.py +17 -27
- cogames/cogs_vs_clips/{cogsguard_reward_variants.py → reward_variants.py} +22 -2
- cogames/cogs_vs_clips/sites.py +41 -30
- cogames/cogs_vs_clips/stations.py +39 -84
- cogames/cogs_vs_clips/team.py +46 -0
- cogames/cogs_vs_clips/{procedural.py → terrain.py} +14 -8
- cogames/cogs_vs_clips/variants.py +201 -107
- cogames/cogs_vs_clips/weather.py +52 -0
- cogames/core.py +87 -0
- cogames/docs/SCRIPTED_AGENT.md +3 -3
- cogames/evaluate.py +4 -2
- cogames/main.py +357 -51
- cogames/maps/canidate1_1000.map +1 -1
- cogames/maps/canidate1_1000_stations.map +2 -2
- cogames/maps/canidate1_500.map +1 -1
- cogames/maps/canidate1_500_stations.map +2 -2
- cogames/maps/canidate2_1000.map +1 -1
- cogames/maps/canidate2_1000_stations.map +2 -2
- cogames/maps/canidate2_500.map +1 -1
- cogames/maps/canidate2_500_stations.map +1 -1
- cogames/maps/canidate3_1000.map +1 -1
- cogames/maps/canidate3_1000_stations.map +2 -2
- cogames/maps/canidate3_500.map +1 -1
- cogames/maps/canidate3_500_stations.map +2 -2
- cogames/maps/canidate4_500.map +1 -1
- cogames/maps/canidate4_500_stations.map +2 -2
- cogames/maps/cave_base_50.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_agile.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_agile_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_charge_up.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_charge_up_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation1.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation1_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation2.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation2_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation3.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation3_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_near.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_search.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_chest_search_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_extract_lab.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_extract_lab_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_memory.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_memory_hard.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_radial.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_radial_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_resource_lab.map +6 -6
- cogames/maps/diagnostic_evals/diagnostic_unclip.map +6 -6
- cogames/maps/evals/eval_balanced_spread.map +6 -6
- cogames/maps/evals/eval_clip_oxygen.map +6 -6
- cogames/maps/evals/eval_collect_resources.map +6 -6
- cogames/maps/evals/eval_collect_resources_hard.map +6 -6
- cogames/maps/evals/eval_collect_resources_medium.map +6 -6
- cogames/maps/evals/eval_divide_and_conquer.map +6 -6
- cogames/maps/evals/eval_energy_starved.map +6 -6
- cogames/maps/evals/eval_multi_coordinated_collect_hard.map +6 -6
- cogames/maps/evals/eval_oxygen_bottleneck.map +6 -6
- cogames/maps/evals/eval_single_use_world.map +6 -6
- cogames/maps/evals/extractor_hub_100x100.map +6 -6
- cogames/maps/evals/extractor_hub_30x30.map +6 -6
- cogames/maps/evals/extractor_hub_50x50.map +6 -6
- cogames/maps/evals/extractor_hub_70x70.map +6 -6
- cogames/maps/evals/extractor_hub_80x80.map +6 -6
- cogames/maps/machina_100_stations.map +2 -2
- cogames/maps/machina_200_stations.map +2 -2
- cogames/maps/machina_200_stations_small.map +2 -2
- cogames/maps/machina_eval_exp01.map +2 -2
- cogames/maps/machina_eval_template_large.map +2 -2
- cogames/maps/machinatrainer4agents.map +2 -2
- cogames/maps/machinatrainer4agentsbase.map +2 -2
- cogames/maps/machinatrainerbig.map +2 -2
- cogames/maps/machinatrainersmall.map +2 -2
- cogames/maps/planky_evals/aligner_avoid_aoe.map +6 -6
- cogames/maps/planky_evals/aligner_full_cycle.map +6 -6
- cogames/maps/planky_evals/aligner_gear.map +6 -6
- cogames/maps/planky_evals/aligner_hearts.map +6 -6
- cogames/maps/planky_evals/aligner_junction.map +6 -6
- cogames/maps/planky_evals/exploration_distant.map +6 -6
- cogames/maps/planky_evals/maze.map +6 -6
- cogames/maps/planky_evals/miner_best_resource.map +6 -6
- cogames/maps/planky_evals/miner_deposit.map +6 -6
- cogames/maps/planky_evals/miner_extract.map +6 -6
- cogames/maps/planky_evals/miner_full_cycle.map +6 -6
- cogames/maps/planky_evals/miner_gear.map +6 -6
- cogames/maps/planky_evals/multi_role.map +6 -6
- cogames/maps/planky_evals/resource_chain.map +6 -6
- cogames/maps/planky_evals/scout_explore.map +6 -6
- cogames/maps/planky_evals/scout_gear.map +6 -6
- cogames/maps/planky_evals/scrambler_full_cycle.map +6 -6
- cogames/maps/planky_evals/scrambler_gear.map +6 -6
- cogames/maps/planky_evals/scrambler_target.map +6 -6
- cogames/maps/planky_evals/stuck_corridor.map +6 -6
- cogames/maps/planky_evals/survive_retreat.map +6 -6
- cogames/maps/training_facility_clipped.map +2 -2
- cogames/maps/training_facility_open_1.map +2 -2
- cogames/maps/training_facility_open_2.map +2 -2
- cogames/maps/training_facility_open_3.map +2 -2
- cogames/maps/training_facility_tight_4.map +2 -2
- cogames/maps/training_facility_tight_5.map +2 -2
- cogames/maps/vanilla_large.map +2 -2
- cogames/maps/vanilla_small.map +2 -2
- cogames/pickup.py +6 -5
- cogames/play.py +14 -16
- cogames/policy/nim_agents/__init__.py +0 -2
- cogames/policy/nim_agents/agents.py +0 -11
- cogames/policy/starter_agent.py +4 -1
- cogames/verbose.py +2 -2
- {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/METADATA +45 -29
- cogames-0.3.68.dist-info/RECORD +160 -0
- metta_alo/scoring.py +7 -7
- cogames/cogs_vs_clips/mission_utils.py +0 -19
- cogames/cogs_vs_clips/tutorial_missions.py +0 -25
- cogames-0.3.64.dist-info/RECORD +0 -159
- metta_alo/job_specs.py +0 -17
- metta_alo/policy.py +0 -16
- metta_alo/pure_single_episode_runner.py +0 -75
- metta_alo/rollout.py +0 -322
- {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/WHEEL +0 -0
- {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/entry_points.txt +0 -0
- {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/licenses/LICENSE +0 -0
- {cogames-0.3.64.dist-info → cogames-0.3.68.dist-info}/top_level.txt +0 -0
cogames/cogs_vs_clips/cog.py
CHANGED
|
@@ -2,14 +2,20 @@ 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 InventoryValue
|
|
8
|
+
from mettagrid.config.game_value import stat as game_stat
|
|
6
9
|
from mettagrid.config.handler_config import Handler
|
|
7
10
|
from mettagrid.config.mettagrid_config import (
|
|
8
11
|
AgentConfig,
|
|
9
12
|
InventoryConfig,
|
|
10
13
|
ResourceLimitsConfig,
|
|
11
14
|
)
|
|
15
|
+
from mettagrid.config.mutation.game_value_mutation import SetGameValueMutation
|
|
16
|
+
from mettagrid.config.mutation.mutation import EntityTarget
|
|
12
17
|
from mettagrid.config.mutation.resource_mutation import updateActor
|
|
18
|
+
from mettagrid.config.reward_config import reward
|
|
13
19
|
|
|
14
20
|
|
|
15
21
|
class CogConfig(Config):
|
|
@@ -32,48 +38,60 @@ class CogConfig(Config):
|
|
|
32
38
|
# Initial inventory
|
|
33
39
|
initial_energy: int = Field(default=100)
|
|
34
40
|
initial_hp: int = Field(default=50)
|
|
41
|
+
initial_solar: int = Field(default=1)
|
|
35
42
|
|
|
36
43
|
# Regen amounts
|
|
37
|
-
energy_regen: int = Field(default=1)
|
|
38
44
|
hp_regen: int = Field(default=-1)
|
|
39
45
|
influence_regen: int = Field(default=-1)
|
|
46
|
+
action_cost: dict[str, int] = Field(default_factory=lambda: {"energy": 4})
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
move_energy_cost: int = Field(default=3)
|
|
43
|
-
|
|
44
|
-
def agent_config(self, gear: list[str], elements: list[str]) -> AgentConfig:
|
|
48
|
+
def agent_config(self, team: str, max_steps: int) -> AgentConfig:
|
|
45
49
|
"""Create an AgentConfig for this cog configuration."""
|
|
46
50
|
return AgentConfig(
|
|
47
|
-
collective=
|
|
51
|
+
collective=team,
|
|
48
52
|
inventory=InventoryConfig(
|
|
49
53
|
limits={
|
|
50
54
|
"hp": ResourceLimitsConfig(min=self.hp_limit, resources=["hp"], modifiers=self.hp_modifiers),
|
|
51
55
|
# when hp == 0, the cog can't hold gear or hearts
|
|
52
|
-
"gear": ResourceLimitsConfig(max=self.gear_limit, resources=
|
|
56
|
+
"gear": ResourceLimitsConfig(max=self.gear_limit, resources=CvCConfig.GEAR, modifiers={"hp": 100}),
|
|
53
57
|
"heart": ResourceLimitsConfig(max=self.heart_limit, resources=["heart"], modifiers={"hp": 100}),
|
|
54
58
|
"energy": ResourceLimitsConfig(
|
|
55
59
|
min=self.energy_limit, resources=["energy"], modifiers=self.energy_modifiers
|
|
56
60
|
),
|
|
57
61
|
"cargo": ResourceLimitsConfig(
|
|
58
|
-
min=self.cargo_limit, resources=
|
|
62
|
+
min=self.cargo_limit, resources=CvCConfig.ELEMENTS, modifiers=self.cargo_modifiers
|
|
59
63
|
),
|
|
60
64
|
"influence": ResourceLimitsConfig(
|
|
61
65
|
min=self.influence_limit, resources=["influence"], modifiers=self.influence_modifiers
|
|
62
66
|
),
|
|
63
67
|
},
|
|
64
|
-
initial={"energy": self.initial_energy, "hp": self.initial_hp},
|
|
68
|
+
initial={"energy": self.initial_energy, "hp": self.initial_hp, "solar": self.initial_solar},
|
|
65
69
|
),
|
|
66
70
|
on_tick={
|
|
67
71
|
"regen": Handler(
|
|
68
72
|
mutations=[
|
|
69
73
|
updateActor(
|
|
70
74
|
{
|
|
71
|
-
"energy": self.energy_regen,
|
|
72
75
|
"hp": self.hp_regen,
|
|
73
76
|
"influence": self.influence_regen,
|
|
74
77
|
}
|
|
75
78
|
)
|
|
76
79
|
]
|
|
77
|
-
)
|
|
80
|
+
),
|
|
81
|
+
"solar_to_energy": Handler(
|
|
82
|
+
mutations=[
|
|
83
|
+
SetGameValueMutation(
|
|
84
|
+
value=InventoryValue(item="energy"),
|
|
85
|
+
source=InventoryValue(item="solar"),
|
|
86
|
+
target=EntityTarget.ACTOR,
|
|
87
|
+
)
|
|
88
|
+
]
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
rewards={
|
|
92
|
+
"aligned_junction_held": reward(
|
|
93
|
+
game_stat("collective.aligned.junction.held"),
|
|
94
|
+
weight=1.0 / max_steps,
|
|
95
|
+
),
|
|
78
96
|
},
|
|
79
97
|
)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Sequence
|
|
6
|
+
|
|
7
|
+
from cogames.cogs_vs_clips.mission import CvCMission
|
|
8
|
+
from cogames.cogs_vs_clips.reward_variants import AVAILABLE_REWARD_VARIANTS
|
|
9
|
+
from cogames.cogs_vs_clips.variants import HIDDEN_VARIANTS, VARIANTS
|
|
10
|
+
from cogames.core import CoGameMissionVariant
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class EventProfile:
|
|
15
|
+
name: str
|
|
16
|
+
clips_overrides: dict[str, object]
|
|
17
|
+
weather_overrides: dict[str, object]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
COGSGUARD_FIXED_MAPS: list[str] = [
|
|
21
|
+
"machina_100_stations.map",
|
|
22
|
+
"machina_200_stations.map",
|
|
23
|
+
"cave_base_50.map",
|
|
24
|
+
"vanilla_large.map",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
DEFAULT_EVENT_PROFILE = EventProfile("events_baseline", {}, {})
|
|
28
|
+
COGSGUARD_EVENT_PROFILES: list[EventProfile] = [
|
|
29
|
+
DEFAULT_EVENT_PROFILE,
|
|
30
|
+
EventProfile(
|
|
31
|
+
"events_fast_clips_short_day",
|
|
32
|
+
{
|
|
33
|
+
"initial_clips_start": 5,
|
|
34
|
+
"initial_clips_spots": 2,
|
|
35
|
+
"scramble_start": 25,
|
|
36
|
+
"scramble_interval": 50,
|
|
37
|
+
"scramble_radius": 35,
|
|
38
|
+
"align_start": 50,
|
|
39
|
+
"align_interval": 50,
|
|
40
|
+
"align_radius": 35,
|
|
41
|
+
},
|
|
42
|
+
{"day_length": 100},
|
|
43
|
+
),
|
|
44
|
+
EventProfile(
|
|
45
|
+
"events_slow_clips_long_day",
|
|
46
|
+
{
|
|
47
|
+
"initial_clips_start": 50,
|
|
48
|
+
"initial_clips_spots": 1,
|
|
49
|
+
"scramble_start": 200,
|
|
50
|
+
"scramble_interval": 200,
|
|
51
|
+
"scramble_radius": 15,
|
|
52
|
+
"align_start": 300,
|
|
53
|
+
"align_interval": 200,
|
|
54
|
+
"align_radius": 15,
|
|
55
|
+
},
|
|
56
|
+
{"day_length": 400},
|
|
57
|
+
),
|
|
58
|
+
EventProfile(
|
|
59
|
+
"events_no_clips",
|
|
60
|
+
{"disabled": True},
|
|
61
|
+
{"day_length": 200},
|
|
62
|
+
),
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def normalize_variant_names(variants: str | Sequence[str] | None) -> list[str]:
|
|
67
|
+
if variants is None:
|
|
68
|
+
return []
|
|
69
|
+
if isinstance(variants, str):
|
|
70
|
+
if variants.startswith("["):
|
|
71
|
+
parsed = json.loads(variants)
|
|
72
|
+
if isinstance(parsed, list):
|
|
73
|
+
return [str(name) for name in parsed]
|
|
74
|
+
return [variants]
|
|
75
|
+
return list(variants)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def split_variants(
|
|
79
|
+
variants: str | Sequence[str] | None,
|
|
80
|
+
) -> tuple[list[CoGameMissionVariant], list[str]]:
|
|
81
|
+
if variants is None:
|
|
82
|
+
names: list[str] = []
|
|
83
|
+
else:
|
|
84
|
+
names = normalize_variant_names(variants)
|
|
85
|
+
all_variants = {variant.name: variant for variant in [*VARIANTS, *HIDDEN_VARIANTS]}
|
|
86
|
+
reward_variants = set(AVAILABLE_REWARD_VARIANTS)
|
|
87
|
+
|
|
88
|
+
resolved: list[CoGameMissionVariant] = []
|
|
89
|
+
resolved_rewards: list[str] = []
|
|
90
|
+
unknown: list[str] = []
|
|
91
|
+
for name in names:
|
|
92
|
+
if name in reward_variants:
|
|
93
|
+
resolved_rewards.append(name)
|
|
94
|
+
continue
|
|
95
|
+
variant = all_variants.get(name)
|
|
96
|
+
if variant is None:
|
|
97
|
+
unknown.append(name)
|
|
98
|
+
continue
|
|
99
|
+
resolved.append(variant)
|
|
100
|
+
|
|
101
|
+
if unknown:
|
|
102
|
+
available_mission = ", ".join(v.name for v in VARIANTS)
|
|
103
|
+
available_reward = ", ".join(AVAILABLE_REWARD_VARIANTS)
|
|
104
|
+
missing = ", ".join(unknown)
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"Unknown variant(s): {missing}. Mission variants: {available_mission}. "
|
|
107
|
+
f"Reward variants: {available_reward}."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return resolved, resolved_rewards
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def resolve_event_profiles(event_profiles: Sequence[EventProfile] | None) -> list[EventProfile]:
|
|
114
|
+
if event_profiles is None:
|
|
115
|
+
return [DEFAULT_EVENT_PROFILE]
|
|
116
|
+
return list(event_profiles)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def filter_compatible_variants(
|
|
120
|
+
mission: CvCMission, variants: Sequence[CoGameMissionVariant]
|
|
121
|
+
) -> list[CoGameMissionVariant]:
|
|
122
|
+
return [variant for variant in variants if variant.compat(mission)]
|
|
@@ -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",
|
|
@@ -16,11 +18,11 @@ def make_cogsguard_tutorial_site() -> Site:
|
|
|
16
18
|
hub_height=15,
|
|
17
19
|
outer_clearance=2,
|
|
18
20
|
stations=[
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"chest",
|
|
21
|
+
"c:aligner",
|
|
22
|
+
"c:scrambler",
|
|
23
|
+
"c:miner",
|
|
24
|
+
"c:scout",
|
|
25
|
+
"c:chest",
|
|
24
26
|
],
|
|
25
27
|
)
|
|
26
28
|
map_builder = MapGen.Config(
|
|
@@ -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", "solar", *_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,21 +333,20 @@ 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:
|
|
343
342
|
|
|
344
343
|
```bash
|
|
345
|
-
uv run
|
|
346
|
-
--policy thinky \
|
|
344
|
+
uv run cogames diagnose thinky \
|
|
347
345
|
--mission-set integrated_evals \
|
|
348
346
|
--cogs 4 \
|
|
349
|
-
--
|
|
347
|
+
--episodes 2
|
|
350
348
|
```
|
|
351
349
|
|
|
352
|
-
Recommendation: When designing new scorable baselines, combine one
|
|
353
|
-
`
|
|
354
|
-
`
|
|
350
|
+
Recommendation: When designing new scorable baselines, combine one "shaping" variant (e.g., `HeartChorusVariant`,
|
|
351
|
+
`PackRatVariant`) with one "constraint" variant (e.g., `DarkSideVariant`, `ResourceBottleneckVariant`,
|
|
352
|
+
`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
|
|
@@ -193,49 +193,25 @@ uv run cogames play --mission hello_world.single_use_swarm --cogs 4 -p baseline
|
|
|
193
193
|
|
|
194
194
|
## Programmatic Evaluation
|
|
195
195
|
|
|
196
|
-
### Using
|
|
196
|
+
### Using cogames diagnose / cogames run
|
|
197
197
|
|
|
198
198
|
For systematic evaluation across multiple missions and configurations:
|
|
199
199
|
|
|
200
200
|
```bash
|
|
201
201
|
# Evaluate on integrated eval suite
|
|
202
|
-
uv run
|
|
203
|
-
--policy thinky \
|
|
202
|
+
uv run cogames diagnose thinky \
|
|
204
203
|
--mission-set integrated_evals \
|
|
205
204
|
--cogs 4 \
|
|
206
|
-
--
|
|
205
|
+
--episodes 2
|
|
207
206
|
|
|
208
|
-
# Evaluate specific agent
|
|
209
|
-
uv run
|
|
207
|
+
# Evaluate specific agent with structured output
|
|
208
|
+
uv run cogames run \
|
|
209
|
+
--mission-set integrated_evals \
|
|
210
210
|
--policy baseline \
|
|
211
|
-
--
|
|
212
|
-
--
|
|
211
|
+
--episodes 10 \
|
|
212
|
+
--format json
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
### Using in Curriculum Training
|
|
216
|
-
|
|
217
|
-
Both diagnostic and integrated missions can be used in curriculum training via `mission_variant_curriculum.py`:
|
|
218
|
-
|
|
219
|
-
```python
|
|
220
|
-
from recipes.experiment.cvc import mission_variant_curriculum
|
|
221
|
-
|
|
222
|
-
# Train on diagnostic missions
|
|
223
|
-
mission_variant_curriculum.train(
|
|
224
|
-
base_missions=["diagnostic_missions"],
|
|
225
|
-
num_cogs=4,
|
|
226
|
-
variants="all"
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
# Train on specific integrated missions
|
|
230
|
-
mission_variant_curriculum.train(
|
|
231
|
-
base_missions=["oxygen_bottleneck", "energy_starved"],
|
|
232
|
-
num_cogs=4,
|
|
233
|
-
variants=["compass", "pack_rat"]
|
|
234
|
-
)
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
215
|
## Design Philosophy
|
|
240
216
|
|
|
241
217
|
### Diagnostic Missions
|
|
@@ -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,
|
|
@@ -1,54 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from pathlib import Path
|
|
4
3
|
from typing import Dict
|
|
5
4
|
|
|
6
5
|
from pydantic import Field
|
|
7
6
|
|
|
8
7
|
from cogames.cogs_vs_clips.cog import CogConfig
|
|
9
|
-
from cogames.cogs_vs_clips.mission import
|
|
10
|
-
from
|
|
8
|
+
from cogames.cogs_vs_clips.mission import CvCMission
|
|
9
|
+
from cogames.cogs_vs_clips.sites import EVALS, get_map
|
|
10
|
+
from cogames.core import CoGameSite
|
|
11
11
|
from mettagrid.config.handler_config import Handler
|
|
12
12
|
from mettagrid.config.mettagrid_config import (
|
|
13
|
-
ChestConfig,
|
|
14
13
|
MettaGridConfig,
|
|
15
|
-
ResourceLimitsConfig,
|
|
16
14
|
)
|
|
17
15
|
from mettagrid.config.mutation.resource_mutation import updateActor
|
|
18
|
-
from mettagrid.config.reward_config import reward
|
|
19
|
-
from mettagrid.map_builder.map_builder import MapBuilderConfig
|
|
20
|
-
from mettagrid.mapgen.mapgen import MapGen
|
|
21
16
|
|
|
22
17
|
RESOURCE_NAMES: tuple[str, ...] = ("carbon", "oxygen", "germanium", "silicon")
|
|
23
18
|
|
|
24
|
-
MAPS_DIR = Path(__file__).resolve().parent.parent.parent / "maps"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_map(map_name: str) -> MapBuilderConfig:
|
|
28
|
-
"""Load a map builder configuration from the local diagnostics directory."""
|
|
29
|
-
normalized = map_name
|
|
30
|
-
if normalized.startswith("evals/"):
|
|
31
|
-
normalized = f"diagnostic_evals/{normalized.split('/', 1)[1]}"
|
|
32
|
-
map_path = MAPS_DIR / normalized
|
|
33
|
-
if not map_path.exists():
|
|
34
|
-
raise FileNotFoundError(f"Diagnostic map not found: {map_path}")
|
|
35
|
-
# Wrap AsciiMapBuilderConfig in MapGen.Config to match standard get_map() behavior
|
|
36
|
-
return MapGen.Config(
|
|
37
|
-
instance=MapBuilderConfig.from_uri(str(map_path)),
|
|
38
|
-
instances=1, # Force single instance - use spawn points from ASCII map directly
|
|
39
|
-
fixed_spawn_order=False,
|
|
40
|
-
instance_border_width=0, # Don't add border - maps already have borders built in
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
EVALS = Site(
|
|
45
|
-
name="evals",
|
|
46
|
-
description="Diagnostic evaluation arenas.",
|
|
47
|
-
map_builder=get_map("evals/diagnostic_radial.map"),
|
|
48
|
-
min_cogs=1,
|
|
49
|
-
max_cogs=4,
|
|
50
|
-
)
|
|
51
|
-
|
|
52
19
|
|
|
53
20
|
# Generous cog config for diagnostic missions: high limits and full energy regen
|
|
54
21
|
_GENEROUS_COG = CogConfig(
|
|
@@ -59,30 +26,15 @@ _GENEROUS_COG = CogConfig(
|
|
|
59
26
|
cargo_limit=255,
|
|
60
27
|
initial_energy=255,
|
|
61
28
|
initial_hp=100,
|
|
62
|
-
energy_regen=255,
|
|
63
|
-
hp_regen=0,
|
|
64
|
-
influence_regen=0,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
# Same but without generous energy regen (for charge-up diagnostics)
|
|
68
|
-
_MODEST_COG = CogConfig(
|
|
69
|
-
gear_limit=255,
|
|
70
|
-
hp_limit=255,
|
|
71
|
-
heart_limit=255,
|
|
72
|
-
energy_limit=255,
|
|
73
|
-
cargo_limit=255,
|
|
74
|
-
initial_energy=255,
|
|
75
|
-
initial_hp=100,
|
|
76
|
-
energy_regen=1,
|
|
77
29
|
hp_regen=0,
|
|
78
30
|
influence_regen=0,
|
|
79
31
|
)
|
|
80
32
|
|
|
81
33
|
|
|
82
|
-
class _DiagnosticMissionBase(
|
|
34
|
+
class _DiagnosticMissionBase(CvCMission):
|
|
83
35
|
"""Base class for minimal diagnostic evaluation missions."""
|
|
84
36
|
|
|
85
|
-
site:
|
|
37
|
+
site: CoGameSite = EVALS
|
|
86
38
|
cog: CogConfig = Field(default_factory=lambda: _GENEROUS_COG.model_copy())
|
|
87
39
|
|
|
88
40
|
map_name: str = Field(default="evals/diagnostic_eval_template.map")
|
|
@@ -90,15 +42,9 @@ class _DiagnosticMissionBase(Mission):
|
|
|
90
42
|
required_agents: int | None = Field(default=None)
|
|
91
43
|
|
|
92
44
|
inventory_seed: Dict[str, int] = Field(default_factory=dict)
|
|
93
|
-
communal_chest_hearts: int | None = Field(default=None)
|
|
94
|
-
resource_chest_stock: Dict[str, int] = Field(default_factory=dict)
|
|
95
45
|
# If True, give agents high energy capacity and regen (overridden by specific missions)
|
|
96
46
|
generous_energy: bool = Field(default=True)
|
|
97
47
|
|
|
98
|
-
# Disable clips events for diagnostic evals
|
|
99
|
-
clips_scramble_start: int = Field(default=99999)
|
|
100
|
-
clips_align_start: int = Field(default=99999)
|
|
101
|
-
|
|
102
48
|
def configure_env(self, cfg: MettaGridConfig) -> None: # pragma: no cover - hook for subclasses
|
|
103
49
|
"""Hook for mission-specific environment alterations."""
|
|
104
50
|
|
|
@@ -114,10 +60,6 @@ class _DiagnosticMissionBase(Mission):
|
|
|
114
60
|
cfg.game.map_builder = forced_map
|
|
115
61
|
cfg.game.max_steps = self.max_steps
|
|
116
62
|
self._apply_inventory_seed(cfg)
|
|
117
|
-
self._apply_communal_chest(cfg)
|
|
118
|
-
self._apply_resource_chests(cfg)
|
|
119
|
-
# Finally, normalize rewards so a single deposited heart yields at most 1 reward.
|
|
120
|
-
self._apply_heart_reward_cap(cfg)
|
|
121
63
|
self.configure_env(cfg)
|
|
122
64
|
return cfg
|
|
123
65
|
finally:
|
|
@@ -134,45 +76,15 @@ class _DiagnosticMissionBase(Mission):
|
|
|
134
76
|
seed = dict(cfg.game.agent.inventory.initial)
|
|
135
77
|
seed.update(self.inventory_seed)
|
|
136
78
|
cfg.game.agent.inventory.initial = seed
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if isinstance(chest, ChestConfig):
|
|
143
|
-
chest.inventory.initial = {"heart": self.communal_chest_hearts}
|
|
144
|
-
|
|
145
|
-
def _apply_resource_chests(self, cfg: MettaGridConfig) -> None:
|
|
146
|
-
if not self.resource_chest_stock:
|
|
147
|
-
return
|
|
148
|
-
for resource, amount in self.resource_chest_stock.items():
|
|
149
|
-
chest_cfg = cfg.game.objects.get(f"chest_{resource}")
|
|
150
|
-
if isinstance(chest_cfg, ChestConfig):
|
|
151
|
-
chest_cfg.inventory.initial = {resource: amount}
|
|
152
|
-
|
|
153
|
-
def _apply_heart_reward_cap(self, cfg: MettaGridConfig) -> None:
|
|
154
|
-
"""Normalize diagnostics so a single deposited heart yields at most 1 reward per episode.
|
|
155
|
-
|
|
156
|
-
- Make each agent-deposited heart worth exactly 1.0 reward (credited only to the depositor).
|
|
157
|
-
- Ensure all chests can store at most 1 heart so total reward per episode cannot exceed 1.
|
|
158
|
-
"""
|
|
159
|
-
agent_cfg = cfg.game.agent
|
|
160
|
-
rewards = dict(agent_cfg.rewards)
|
|
161
|
-
rewards["chest_heart_deposited_by_agent"] = reward(stat("chest.heart.deposited_by_agent"))
|
|
162
|
-
agent_cfg.rewards = rewards
|
|
163
|
-
|
|
164
|
-
# Cap heart capacity for every chest used in diagnostics (communal or resource-specific).
|
|
165
|
-
for _name, obj in cfg.game.objects.items():
|
|
166
|
-
if not isinstance(obj, ChestConfig):
|
|
167
|
-
continue
|
|
168
|
-
# Find existing heart limit or create new one
|
|
169
|
-
heart_limit = obj.inventory.limits.get("heart", ResourceLimitsConfig(min=1, resources=["heart"]))
|
|
170
|
-
heart_limit.min = 1
|
|
171
|
-
obj.inventory.limits["heart"] = heart_limit
|
|
79
|
+
# Also apply to per-agent configs (used by CvCMission)
|
|
80
|
+
for agent_cfg in cfg.game.agents:
|
|
81
|
+
agent_seed = dict(agent_cfg.inventory.initial)
|
|
82
|
+
agent_seed.update(self.inventory_seed)
|
|
83
|
+
agent_cfg.inventory.initial = agent_seed
|
|
172
84
|
|
|
173
85
|
|
|
174
86
|
# ----------------------------------------------------------------------
|
|
175
|
-
#
|
|
87
|
+
# Diagnostic missions (no assemblers)
|
|
176
88
|
# ----------------------------------------------------------------------
|
|
177
89
|
|
|
178
90
|
|
|
@@ -234,7 +146,7 @@ class DiagnosticChargeUp(_DiagnosticMissionBase):
|
|
|
234
146
|
max_steps: int = Field(default=250)
|
|
235
147
|
|
|
236
148
|
def configure_env(self, cfg: MettaGridConfig) -> None:
|
|
237
|
-
# Set starting energy to
|
|
149
|
+
# Set starting energy to 60 and no regen
|
|
238
150
|
agent = cfg.game.agent
|
|
239
151
|
agent.inventory.initial = dict(agent.inventory.initial)
|
|
240
152
|
agent.inventory.initial["energy"] = 60
|
|
@@ -302,7 +214,7 @@ class DiagnosticChargeUpHard(_DiagnosticMissionBase):
|
|
|
302
214
|
max_steps: int = Field(default=350)
|
|
303
215
|
|
|
304
216
|
def configure_env(self, cfg: MettaGridConfig) -> None:
|
|
305
|
-
# Set starting energy to
|
|
217
|
+
# Set starting energy to 60 and no regen
|
|
306
218
|
agent = cfg.game.agent
|
|
307
219
|
agent.inventory.initial = dict(agent.inventory.initial)
|
|
308
220
|
agent.inventory.initial["energy"] = 60
|