cogames 0.3.49__py3-none-any.whl → 0.3.64__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 +60 -6
- cogames/cli/docsync/__init__.py +0 -0
- cogames/cli/docsync/_nb_md_directive_processing.py +180 -0
- cogames/cli/docsync/_nb_md_sync.py +103 -0
- cogames/cli/docsync/_nb_py_sync.py +122 -0
- cogames/cli/docsync/_three_way_sync.py +115 -0
- cogames/cli/docsync/_utils.py +76 -0
- cogames/cli/docsync/docsync.py +156 -0
- cogames/cli/leaderboard.py +112 -28
- cogames/cli/mission.py +64 -53
- cogames/cli/policy.py +46 -10
- cogames/cli/submit.py +268 -67
- cogames/cogs_vs_clips/cog.py +79 -0
- cogames/cogs_vs_clips/cogs_vs_clips_mapgen.md +19 -16
- cogames/cogs_vs_clips/cogsguard_reward_variants.py +153 -0
- cogames/cogs_vs_clips/cogsguard_tutorial.py +56 -0
- cogames/cogs_vs_clips/evals/README.md +10 -16
- cogames/cogs_vs_clips/evals/cogsguard_evals.py +81 -0
- cogames/cogs_vs_clips/evals/diagnostic_evals.py +49 -444
- cogames/cogs_vs_clips/evals/difficulty_variants.py +13 -326
- cogames/cogs_vs_clips/evals/integrated_evals.py +5 -45
- cogames/cogs_vs_clips/evals/spanning_evals.py +9 -180
- cogames/cogs_vs_clips/mission.py +187 -146
- cogames/cogs_vs_clips/missions.py +46 -137
- cogames/cogs_vs_clips/procedural.py +8 -8
- cogames/cogs_vs_clips/sites.py +107 -3
- cogames/cogs_vs_clips/stations.py +198 -186
- cogames/cogs_vs_clips/tutorial_missions.py +1 -1
- cogames/cogs_vs_clips/variants.py +25 -476
- cogames/device.py +13 -1
- cogames/{policy/scripted_agent/README.md → docs/SCRIPTED_AGENT.md} +82 -58
- cogames/evaluate.py +18 -30
- cogames/main.py +1434 -243
- 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 +2 -2
- 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 +2 -2
- cogames/maps/diagnostic_evals/diagnostic_charge_up_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation1.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation1_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation2.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation2_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation3.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_navigation3_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_near.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_search.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_chest_search_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_extract_lab.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_extract_lab_hard.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_memory.map +2 -2
- cogames/maps/diagnostic_evals/diagnostic_memory_hard.map +2 -2
- 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 +2 -2
- cogames/maps/diagnostic_evals/diagnostic_unclip.map +2 -2
- cogames/maps/evals/eval_balanced_spread.map +9 -5
- cogames/maps/evals/eval_clip_oxygen.map +9 -5
- cogames/maps/evals/eval_collect_resources.map +9 -5
- cogames/maps/evals/eval_collect_resources_hard.map +9 -5
- cogames/maps/evals/eval_collect_resources_medium.map +9 -5
- cogames/maps/evals/eval_divide_and_conquer.map +9 -5
- cogames/maps/evals/eval_energy_starved.map +9 -5
- cogames/maps/evals/eval_multi_coordinated_collect_hard.map +9 -5
- cogames/maps/evals/eval_oxygen_bottleneck.map +9 -5
- cogames/maps/evals/eval_single_use_world.map +9 -5
- cogames/maps/evals/extractor_hub_100x100.map +9 -5
- cogames/maps/evals/extractor_hub_30x30.map +9 -5
- cogames/maps/evals/extractor_hub_50x50.map +9 -5
- cogames/maps/evals/extractor_hub_70x70.map +9 -5
- cogames/maps/evals/extractor_hub_80x80.map +9 -5
- 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 +28 -0
- cogames/maps/planky_evals/aligner_full_cycle.map +28 -0
- cogames/maps/planky_evals/aligner_gear.map +24 -0
- cogames/maps/planky_evals/aligner_hearts.map +24 -0
- cogames/maps/planky_evals/aligner_junction.map +26 -0
- cogames/maps/planky_evals/exploration_distant.map +28 -0
- cogames/maps/planky_evals/maze.map +32 -0
- cogames/maps/planky_evals/miner_best_resource.map +26 -0
- cogames/maps/planky_evals/miner_deposit.map +24 -0
- cogames/maps/planky_evals/miner_extract.map +26 -0
- cogames/maps/planky_evals/miner_full_cycle.map +28 -0
- cogames/maps/planky_evals/miner_gear.map +24 -0
- cogames/maps/planky_evals/multi_role.map +28 -0
- cogames/maps/planky_evals/resource_chain.map +30 -0
- cogames/maps/planky_evals/scout_explore.map +32 -0
- cogames/maps/planky_evals/scout_gear.map +24 -0
- cogames/maps/planky_evals/scrambler_full_cycle.map +28 -0
- cogames/maps/planky_evals/scrambler_gear.map +24 -0
- cogames/maps/planky_evals/scrambler_target.map +26 -0
- cogames/maps/planky_evals/stuck_corridor.map +32 -0
- cogames/maps/planky_evals/survive_retreat.map +26 -0
- 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 +183 -0
- cogames/play.py +166 -33
- cogames/policy/chaos_monkey.py +54 -0
- cogames/policy/nim_agents/__init__.py +27 -10
- cogames/policy/nim_agents/agents.py +121 -60
- cogames/policy/nim_agents/thinky_eval.py +35 -222
- cogames/policy/pufferlib_policy.py +67 -32
- cogames/policy/starter_agent.py +184 -0
- cogames/policy/trainable_policy_template.py +4 -1
- cogames/train.py +51 -13
- cogames/verbose.py +2 -2
- cogames-0.3.64.dist-info/METADATA +1842 -0
- cogames-0.3.64.dist-info/RECORD +159 -0
- cogames-0.3.64.dist-info/licenses/LICENSE +21 -0
- cogames-0.3.64.dist-info/top_level.txt +2 -0
- metta_alo/__init__.py +0 -0
- metta_alo/job_specs.py +17 -0
- metta_alo/policy.py +16 -0
- metta_alo/pure_single_episode_runner.py +75 -0
- metta_alo/py.typed +0 -0
- metta_alo/rollout.py +322 -0
- metta_alo/scoring.py +168 -0
- cogames/maps/diagnostic_evals/diagnostic_assembler_near.map +0 -49
- cogames/maps/diagnostic_evals/diagnostic_assembler_search.map +0 -49
- cogames/maps/diagnostic_evals/diagnostic_assembler_search_hard.map +0 -89
- cogames/policy/nim_agents/common.nim +0 -887
- cogames/policy/nim_agents/install.sh +0 -1
- cogames/policy/nim_agents/ladybug_agent.nim +0 -984
- cogames/policy/nim_agents/nim_agents.nim +0 -55
- cogames/policy/nim_agents/nim_agents.nims +0 -14
- cogames/policy/nim_agents/nimby.lock +0 -3
- cogames/policy/nim_agents/racecar_agents.nim +0 -884
- cogames/policy/nim_agents/random_agents.nim +0 -68
- cogames/policy/nim_agents/test_agents.py +0 -53
- cogames/policy/nim_agents/thinky_agents.nim +0 -717
- cogames/policy/scripted_agent/baseline_agent.py +0 -1049
- cogames/policy/scripted_agent/demo_policy.py +0 -244
- cogames/policy/scripted_agent/pathfinding.py +0 -126
- cogames/policy/scripted_agent/starter_agent.py +0 -136
- cogames/policy/scripted_agent/types.py +0 -235
- cogames/policy/scripted_agent/unclipping_agent.py +0 -476
- cogames/policy/scripted_agent/utils.py +0 -385
- cogames-0.3.49.dist-info/METADATA +0 -406
- cogames-0.3.49.dist-info/RECORD +0 -136
- cogames-0.3.49.dist-info/top_level.txt +0 -1
- {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/WHEEL +0 -0
- {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/entry_points.txt +0 -0
|
@@ -1,81 +1,25 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import override
|
|
2
2
|
|
|
3
3
|
from cogames.cogs_vs_clips.evals.difficulty_variants import DIFFICULTY_VARIANTS
|
|
4
|
-
from cogames.cogs_vs_clips.mission import MissionVariant
|
|
4
|
+
from cogames.cogs_vs_clips.mission import Mission, MissionVariant
|
|
5
5
|
from cogames.cogs_vs_clips.procedural import BaseHubVariant, MachinaArenaVariant
|
|
6
|
-
from mettagrid.config.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ProtocolConfig,
|
|
10
|
-
ResourceLimitsConfig,
|
|
11
|
-
VibeTransfer,
|
|
12
|
-
)
|
|
6
|
+
from mettagrid.config.action_config import VibeTransfer
|
|
7
|
+
from mettagrid.config.game_value import stat
|
|
8
|
+
from mettagrid.config.reward_config import reward
|
|
13
9
|
from mettagrid.map_builder.map_builder import MapBuilderConfig
|
|
14
10
|
from mettagrid.mapgen.mapgen import MapGen
|
|
15
11
|
from mettagrid.mapgen.scenes.base_hub import DEFAULT_EXTRACTORS as HUB_EXTRACTORS
|
|
16
12
|
from mettagrid.mapgen.scenes.building_distributions import DistributionConfig, DistributionType
|
|
17
13
|
|
|
18
14
|
|
|
19
|
-
class MinedOutVariant(MissionVariant):
|
|
20
|
-
name: str = "mined_out"
|
|
21
|
-
description: str = "All resources are depleted. You must be efficient to survive."
|
|
22
|
-
|
|
23
|
-
@override
|
|
24
|
-
def modify_mission(self, mission):
|
|
25
|
-
# Clamp efficiency to minimum of 50 to prevent negative values
|
|
26
|
-
mission.carbon_extractor.max_uses = 2
|
|
27
|
-
mission.oxygen_extractor.max_uses = 2
|
|
28
|
-
mission.silicon_extractor.max_uses = 2
|
|
29
|
-
|
|
30
|
-
|
|
31
15
|
class DarkSideVariant(MissionVariant):
|
|
32
16
|
name: str = "dark_side"
|
|
33
17
|
description: str = "You're on the dark side of the asteroid. You recharge slower."
|
|
34
18
|
|
|
35
19
|
@override
|
|
36
20
|
def modify_mission(self, mission):
|
|
37
|
-
mission
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class LonelyHeartVariant(MissionVariant):
|
|
41
|
-
name: str = "lonely_heart"
|
|
42
|
-
description: str = "Making hearts for one agent is easy."
|
|
43
|
-
|
|
44
|
-
@override
|
|
45
|
-
def modify_mission(self, mission):
|
|
46
|
-
mission.assembler.first_heart_cost = 1
|
|
47
|
-
mission.assembler.additional_heart_cost = 0
|
|
48
|
-
mission.heart_capacity = max(mission.heart_capacity, 255)
|
|
49
|
-
|
|
50
|
-
@override
|
|
51
|
-
def modify_env(self, mission, env):
|
|
52
|
-
simplified_inputs = {"carbon": 1, "oxygen": 1, "germanium": 1, "silicon": 1, "energy": 1}
|
|
53
|
-
|
|
54
|
-
assembler = env.game.objects["assembler"]
|
|
55
|
-
if not isinstance(assembler, AssemblerConfig):
|
|
56
|
-
raise TypeError("Expected 'assembler' to be AssemblerConfig")
|
|
57
|
-
|
|
58
|
-
for idx, proto in enumerate(assembler.protocols):
|
|
59
|
-
if proto.output_resources.get("heart", 0) == 0:
|
|
60
|
-
continue
|
|
61
|
-
updated = proto.model_copy(deep=True)
|
|
62
|
-
updated.input_resources = dict(simplified_inputs)
|
|
63
|
-
assembler.protocols[idx] = updated
|
|
64
|
-
|
|
65
|
-
germanium = env.game.objects["germanium_extractor"]
|
|
66
|
-
if not isinstance(germanium, AssemblerConfig):
|
|
67
|
-
raise TypeError("Expected 'germanium_extractor' to be AssemblerConfig")
|
|
68
|
-
germanium.max_uses = 0
|
|
69
|
-
updated_protocols: list[ProtocolConfig] = []
|
|
70
|
-
for proto in germanium.protocols:
|
|
71
|
-
new_proto = proto.model_copy(deep=True)
|
|
72
|
-
output = dict(new_proto.output_resources)
|
|
73
|
-
output["germanium"] = max(output.get("germanium", 0), 1)
|
|
74
|
-
new_proto.output_resources = output
|
|
75
|
-
new_proto.cooldown = max(new_proto.cooldown, 1)
|
|
76
|
-
updated_protocols.append(new_proto)
|
|
77
|
-
if updated_protocols:
|
|
78
|
-
germanium.protocols = updated_protocols
|
|
21
|
+
assert isinstance(mission, Mission)
|
|
22
|
+
mission.cog.energy_regen = 0
|
|
79
23
|
|
|
80
24
|
|
|
81
25
|
class SuperChargedVariant(MissionVariant):
|
|
@@ -84,7 +28,8 @@ class SuperChargedVariant(MissionVariant):
|
|
|
84
28
|
|
|
85
29
|
@override
|
|
86
30
|
def modify_mission(self, mission):
|
|
87
|
-
mission
|
|
31
|
+
assert isinstance(mission, Mission)
|
|
32
|
+
mission.cog.energy_regen += 2
|
|
88
33
|
|
|
89
34
|
|
|
90
35
|
class RoughTerrainVariant(MissionVariant):
|
|
@@ -93,54 +38,8 @@ class RoughTerrainVariant(MissionVariant):
|
|
|
93
38
|
|
|
94
39
|
@override
|
|
95
40
|
def modify_mission(self, mission):
|
|
96
|
-
mission
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class SolarFlareVariant(MissionVariant):
|
|
100
|
-
name: str = "solar_flare"
|
|
101
|
-
description: str = "Chargers have been damaged by the solar flare."
|
|
102
|
-
|
|
103
|
-
@override
|
|
104
|
-
def modify_mission(self, mission):
|
|
105
|
-
# Clamp efficiency to minimum of 1 to prevent negative values
|
|
106
|
-
mission.charger.efficiency = max(1, mission.charger.efficiency - 50)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class TrainingVariant(MissionVariant):
|
|
110
|
-
name: str = "training"
|
|
111
|
-
description: str = "Training-friendly: max cargo, fast extractors, chest only deposits hearts."
|
|
112
|
-
|
|
113
|
-
@override
|
|
114
|
-
def modify_mission(self, mission):
|
|
115
|
-
mission.cargo_capacity = 255 # Maximum cargo for easier resource collection
|
|
116
|
-
|
|
117
|
-
@override
|
|
118
|
-
def modify_env(self, mission, env):
|
|
119
|
-
# Set all extractor cooldowns to 5ms (fast)
|
|
120
|
-
for extractor_name in ["carbon_extractor", "oxygen_extractor", "germanium_extractor", "silicon_extractor"]:
|
|
121
|
-
extractor = env.game.objects.get(extractor_name)
|
|
122
|
-
if isinstance(extractor, AssemblerConfig):
|
|
123
|
-
updated_protocols = []
|
|
124
|
-
for proto in extractor.protocols:
|
|
125
|
-
updated_proto = proto.model_copy(deep=True)
|
|
126
|
-
updated_proto.cooldown = 5
|
|
127
|
-
updated_protocols.append(updated_proto)
|
|
128
|
-
extractor.protocols = updated_protocols
|
|
129
|
-
|
|
130
|
-
# Modify chest to only deposit hearts by default (not all resources)
|
|
131
|
-
chest = env.game.objects.get("chest")
|
|
132
|
-
if isinstance(chest, ChestConfig):
|
|
133
|
-
chest.vibe_transfers = {
|
|
134
|
-
"heart_b": {"heart": 1},
|
|
135
|
-
"carbon_a": {"carbon": -10},
|
|
136
|
-
"carbon_b": {"carbon": 10},
|
|
137
|
-
"oxygen_a": {"oxygen": -10},
|
|
138
|
-
"oxygen_b": {"oxygen": 10},
|
|
139
|
-
"germanium_a": {"germanium": -1},
|
|
140
|
-
"germanium_b": {"germanium": 1},
|
|
141
|
-
"silicon_a": {"silicon": -25},
|
|
142
|
-
"silicon_b": {"silicon": 25},
|
|
143
|
-
}
|
|
41
|
+
assert isinstance(mission, Mission)
|
|
42
|
+
mission.cog.move_energy_cost += 2
|
|
144
43
|
|
|
145
44
|
|
|
146
45
|
class PackRatVariant(MissionVariant):
|
|
@@ -149,10 +48,11 @@ class PackRatVariant(MissionVariant):
|
|
|
149
48
|
|
|
150
49
|
@override
|
|
151
50
|
def modify_mission(self, mission):
|
|
152
|
-
|
|
153
|
-
mission.
|
|
154
|
-
mission.
|
|
155
|
-
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)
|
|
156
56
|
|
|
157
57
|
|
|
158
58
|
class EnergizedVariant(MissionVariant):
|
|
@@ -161,148 +61,18 @@ class EnergizedVariant(MissionVariant):
|
|
|
161
61
|
|
|
162
62
|
@override
|
|
163
63
|
def modify_mission(self, mission):
|
|
164
|
-
|
|
165
|
-
mission.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
class ResourceBottleneckVariant(MissionVariant):
|
|
169
|
-
name: str = "resource_bottleneck"
|
|
170
|
-
description: str = "A resource is the limiting factor. Agents must prioritize it over other resources."
|
|
171
|
-
resource: Sequence[str] | str = ("oxygen", "germanium", "silicon", "carbon")
|
|
172
|
-
|
|
173
|
-
@override
|
|
174
|
-
def modify_mission(self, mission):
|
|
175
|
-
# Accept either a single resource or an iterable of resources to bottleneck
|
|
176
|
-
if isinstance(self.resource, str):
|
|
177
|
-
resources: Iterable[str] = [self.resource]
|
|
178
|
-
else:
|
|
179
|
-
resources = list(self.resource)
|
|
180
|
-
|
|
181
|
-
for resource in resources:
|
|
182
|
-
if resource in {"carbon", "oxygen", "germanium", "silicon"}:
|
|
183
|
-
extractor_attr = f"{resource}_extractor"
|
|
184
|
-
elif resource == "energy":
|
|
185
|
-
extractor_attr = "charger"
|
|
186
|
-
else:
|
|
187
|
-
raise ValueError(f"Unsupported resource for bottleneck: {resource}")
|
|
188
|
-
|
|
189
|
-
extractor = getattr(mission, extractor_attr, None)
|
|
190
|
-
if extractor is None:
|
|
191
|
-
raise AttributeError(f"Mission has no extractor attribute '{extractor_attr}'")
|
|
192
|
-
|
|
193
|
-
# Clamp efficiency to minimum of 1 to prevent negative values
|
|
194
|
-
extractor.efficiency = max(1, int(extractor.efficiency) - 50)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
class SingleToolUnclipVariant(MissionVariant):
|
|
198
|
-
name: str = "single_tool_unclip"
|
|
199
|
-
description: str = "Only one tool is available: the decoder."
|
|
200
|
-
resource: str = "carbon"
|
|
201
|
-
|
|
202
|
-
@override
|
|
203
|
-
def modify_env(self, mission, env):
|
|
204
|
-
# Restrict assembler to a single generic gear recipe: carbon -> decoder (no vibes required)
|
|
205
|
-
# Since the protocol doesn't require vibes, agents won't need to change vibes
|
|
206
|
-
assembler = env.game.objects.get("assembler")
|
|
207
|
-
if isinstance(assembler, AssemblerConfig):
|
|
208
|
-
assembler.protocols = [
|
|
209
|
-
ProtocolConfig(vibes=[], input_resources={self.resource: 1}, output_resources={"decoder": 1})
|
|
210
|
-
]
|
|
64
|
+
assert isinstance(mission, Mission)
|
|
65
|
+
mission.cog.energy_limit = max(mission.cog.energy_limit, 255)
|
|
66
|
+
mission.cog.energy_regen = mission.cog.energy_limit
|
|
211
67
|
|
|
212
68
|
|
|
213
69
|
class CompassVariant(MissionVariant):
|
|
214
70
|
name: str = "compass"
|
|
215
|
-
description: str = "Enable compass observation
|
|
216
|
-
|
|
217
|
-
@override
|
|
218
|
-
def modify_mission(self, mission):
|
|
219
|
-
mission.compass_enabled = True
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
class HeartChorusVariant(MissionVariant):
|
|
223
|
-
name: str = "heart_chorus"
|
|
224
|
-
description: str = "Heart-centric reward shaping with gentle resource bonuses."
|
|
71
|
+
description: str = "Enable compass observation."
|
|
225
72
|
|
|
226
73
|
@override
|
|
227
74
|
def modify_env(self, mission, env):
|
|
228
|
-
|
|
229
|
-
rewards = dict(env.game.agent.rewards.stats)
|
|
230
|
-
rewards.update(
|
|
231
|
-
{
|
|
232
|
-
"assembler.heart.created": 1.0,
|
|
233
|
-
"chest.heart.deposited_by_agent": 1.0,
|
|
234
|
-
"chest.heart.withdrawn_by_agent": -1.0,
|
|
235
|
-
"inventory.diversity.ge.2": 0.17,
|
|
236
|
-
"inventory.diversity.ge.3": 0.18,
|
|
237
|
-
"inventory.diversity.ge.4": 0.60,
|
|
238
|
-
"inventory.diversity.ge.5": 0.97,
|
|
239
|
-
}
|
|
240
|
-
)
|
|
241
|
-
env.game.agent.rewards.stats = rewards
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
class TinyHeartProtocolsVariant(MissionVariant):
|
|
245
|
-
"""Prepend low-cost heart/red-heart assembler protocols for easy hearts."""
|
|
246
|
-
|
|
247
|
-
name: str = "tiny_heart_protocols"
|
|
248
|
-
description: str = "Prepend low-cost heart/red-heart assembler protocols."
|
|
249
|
-
|
|
250
|
-
# Allow customization if ever needed; defaults match prior inline block.
|
|
251
|
-
carbon_cost: int = 2
|
|
252
|
-
oxygen_cost: int = 2
|
|
253
|
-
germanium_cost: int = 1
|
|
254
|
-
silicon_cost: int = 3
|
|
255
|
-
energy_cost: int = 2
|
|
256
|
-
|
|
257
|
-
@override
|
|
258
|
-
def modify_env(self, mission, env) -> None:
|
|
259
|
-
assembler = env.game.objects.get("assembler")
|
|
260
|
-
if not isinstance(assembler, AssemblerConfig):
|
|
261
|
-
raise TypeError("Expected 'assembler' to be AssemblerConfig")
|
|
262
|
-
|
|
263
|
-
tiny_inputs = {
|
|
264
|
-
"carbon": self.carbon_cost,
|
|
265
|
-
"oxygen": self.oxygen_cost,
|
|
266
|
-
"germanium": self.germanium_cost,
|
|
267
|
-
"silicon": self.silicon_cost,
|
|
268
|
-
"energy": self.energy_cost,
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
tiny_protocols = [
|
|
272
|
-
ProtocolConfig(
|
|
273
|
-
vibes=[vibe] * (i + 1),
|
|
274
|
-
input_resources=tiny_inputs,
|
|
275
|
-
output_resources={"heart": i + 1},
|
|
276
|
-
)
|
|
277
|
-
for vibe in ("heart_a", "red-heart")
|
|
278
|
-
for i in range(4)
|
|
279
|
-
]
|
|
280
|
-
tiny_keys = {(tuple(p.vibes), p.min_agents) for p in tiny_protocols}
|
|
281
|
-
existing = [p for p in assembler.protocols if (tuple(p.vibes), p.min_agents) not in tiny_keys]
|
|
282
|
-
assembler.protocols = [*tiny_protocols, *existing]
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
class VibeCheckMin2Variant(MissionVariant):
|
|
286
|
-
name: str = "vibe_check_min_2"
|
|
287
|
-
description: str = "Require at least 2 heart vibes to craft a heart."
|
|
288
|
-
min_vibes: int = 2
|
|
289
|
-
|
|
290
|
-
@override
|
|
291
|
-
def modify_env(self, mission, env):
|
|
292
|
-
assembler = env.game.objects["assembler"]
|
|
293
|
-
if not isinstance(assembler, AssemblerConfig):
|
|
294
|
-
raise TypeError("Expected 'assembler' to be AssemblerConfig")
|
|
295
|
-
|
|
296
|
-
filtered: list[ProtocolConfig] = []
|
|
297
|
-
for proto in assembler.protocols:
|
|
298
|
-
# Keep non-heart protocols as-is (e.g., gear recipes)
|
|
299
|
-
if proto.output_resources.get("heart", 0) == 0:
|
|
300
|
-
filtered.append(proto)
|
|
301
|
-
continue
|
|
302
|
-
# Keep only heart protocols that require >= 2 'heart' vibes
|
|
303
|
-
if len(proto.vibes) >= 2 and all(v == "heart_a" for v in proto.vibes):
|
|
304
|
-
filtered.append(proto)
|
|
305
|
-
assembler.protocols = filtered
|
|
75
|
+
env.game.obs.global_obs.compass = True
|
|
306
76
|
|
|
307
77
|
|
|
308
78
|
class Small50Variant(MissionVariant):
|
|
@@ -319,153 +89,6 @@ class Small50Variant(MissionVariant):
|
|
|
319
89
|
env.game.map_builder = map_builder.model_copy(update={"width": 50, "height": 50})
|
|
320
90
|
|
|
321
91
|
|
|
322
|
-
class InventoryHeartTuneVariant(MissionVariant):
|
|
323
|
-
name: str = "inventory_heart_tune"
|
|
324
|
-
description: str = "Tune starting agent inventory to N hearts worth of inputs; optional heart capacity."
|
|
325
|
-
hearts: int = 1
|
|
326
|
-
heart_capacity: int | None = None
|
|
327
|
-
|
|
328
|
-
@override
|
|
329
|
-
def modify_env(self, mission, env) -> None:
|
|
330
|
-
hearts = max(0, int(self.hearts))
|
|
331
|
-
if hearts == 0 and self.heart_capacity is None:
|
|
332
|
-
return
|
|
333
|
-
|
|
334
|
-
heart_cost = mission.assembler.first_heart_cost
|
|
335
|
-
per_heart = {
|
|
336
|
-
"carbon": heart_cost,
|
|
337
|
-
"oxygen": heart_cost,
|
|
338
|
-
"germanium": max(heart_cost // 10, 1),
|
|
339
|
-
"silicon": 3 * heart_cost,
|
|
340
|
-
"energy": 0,
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if hearts > 0:
|
|
344
|
-
agent_cfg = env.game.agent
|
|
345
|
-
agent_cfg.inventory.initial = dict(agent_cfg.inventory.initial)
|
|
346
|
-
|
|
347
|
-
def _limit_for(resource: str) -> int:
|
|
348
|
-
return agent_cfg.inventory.get_limit(resource)
|
|
349
|
-
|
|
350
|
-
for resource_name, per_heart_value in per_heart.items():
|
|
351
|
-
current = int(agent_cfg.inventory.initial.get(resource_name, 0))
|
|
352
|
-
target = current + per_heart_value * hearts
|
|
353
|
-
cap = _limit_for(resource_name)
|
|
354
|
-
agent_cfg.inventory.initial[resource_name] = min(cap, target)
|
|
355
|
-
|
|
356
|
-
if self.heart_capacity is not None:
|
|
357
|
-
agent_cfg = env.game.agent
|
|
358
|
-
hearts_limit = agent_cfg.inventory.limits.get("heart")
|
|
359
|
-
if hearts_limit is None:
|
|
360
|
-
hearts_limit = ResourceLimitsConfig(limit=self.heart_capacity, resources=["heart"])
|
|
361
|
-
hearts_limit.limit = max(int(hearts_limit.limit), int(self.heart_capacity))
|
|
362
|
-
agent_cfg.inventory.limits["heart"] = hearts_limit
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
class ChestHeartTuneVariant(MissionVariant):
|
|
366
|
-
name: str = "chest_heart_tune"
|
|
367
|
-
description: str = "Tune chest starting inventory to N hearts worth of inputs."
|
|
368
|
-
hearts: int = 2
|
|
369
|
-
|
|
370
|
-
@override
|
|
371
|
-
def modify_env(self, mission, env) -> None:
|
|
372
|
-
hearts = max(0, int(self.hearts))
|
|
373
|
-
if hearts == 0:
|
|
374
|
-
return
|
|
375
|
-
heart_cost = mission.assembler.first_heart_cost
|
|
376
|
-
per_heart = {
|
|
377
|
-
"carbon": heart_cost,
|
|
378
|
-
"oxygen": heart_cost,
|
|
379
|
-
"germanium": max(heart_cost // 10, 1),
|
|
380
|
-
"silicon": 3 * heart_cost,
|
|
381
|
-
}
|
|
382
|
-
chest_cfg = env.game.objects["chest"]
|
|
383
|
-
if not isinstance(chest_cfg, ChestConfig):
|
|
384
|
-
raise TypeError("Expected 'chest' to be ChestConfig")
|
|
385
|
-
start = dict(chest_cfg.inventory.initial)
|
|
386
|
-
for k, v in per_heart.items():
|
|
387
|
-
start[k] = start.get(k, 0) + v * hearts
|
|
388
|
-
chest_cfg.inventory.initial = start
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
class ExtractorHeartTuneVariant(MissionVariant):
|
|
392
|
-
name: str = "extractor_heart_tune"
|
|
393
|
-
description: str = "Tune extractors for N hearts production capability."
|
|
394
|
-
hearts: int = 1
|
|
395
|
-
|
|
396
|
-
@override
|
|
397
|
-
def modify_mission(self, mission):
|
|
398
|
-
hearts = max(0, int(self.hearts))
|
|
399
|
-
if hearts == 0:
|
|
400
|
-
return
|
|
401
|
-
heart_cost = mission.assembler.first_heart_cost
|
|
402
|
-
one_heart = {
|
|
403
|
-
"carbon": heart_cost,
|
|
404
|
-
"oxygen": heart_cost,
|
|
405
|
-
"germanium": max(heart_cost // 10, 1),
|
|
406
|
-
"silicon": 3 * heart_cost,
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
# Carbon per-use depends on efficiency
|
|
410
|
-
carbon_per_use = max(1, 4 * mission.carbon_extractor.efficiency // 100)
|
|
411
|
-
carbon_needed = one_heart["carbon"] * hearts
|
|
412
|
-
mission.carbon_extractor.max_uses = (carbon_needed + carbon_per_use - 1) // carbon_per_use
|
|
413
|
-
|
|
414
|
-
# Oxygen is 20 per use
|
|
415
|
-
oxygen_per_use = 20
|
|
416
|
-
oxygen_needed = one_heart["oxygen"] * hearts
|
|
417
|
-
mission.oxygen_extractor.max_uses = (oxygen_needed + oxygen_per_use - 1) // oxygen_per_use
|
|
418
|
-
|
|
419
|
-
# Silicon is ~25 per use (scaled by efficiency); silicon extractor divides by 10 internally
|
|
420
|
-
silicon_per_use = max(1, int(25 * mission.silicon_extractor.efficiency // 100))
|
|
421
|
-
silicon_needed = one_heart["silicon"] * hearts
|
|
422
|
-
silicon_uses = (silicon_needed + silicon_per_use - 1) // silicon_per_use
|
|
423
|
-
mission.silicon_extractor.max_uses = max(1, silicon_uses * 10)
|
|
424
|
-
|
|
425
|
-
# Germanium: fixed one use producing all required
|
|
426
|
-
mission.germanium_extractor.efficiency = int(one_heart["germanium"] * hearts)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
class CyclicalUnclipVariant(MissionVariant):
|
|
430
|
-
name: str = "cyclical_unclip"
|
|
431
|
-
description: str = "Required resources for unclipping recipes are cyclical. \
|
|
432
|
-
So Germanium extractors require silicon-based unclipping recipes."
|
|
433
|
-
|
|
434
|
-
@override
|
|
435
|
-
def modify_env(self, mission, env):
|
|
436
|
-
if env.game.clipper is not None:
|
|
437
|
-
env.game.clipper.unclipping_protocols = [
|
|
438
|
-
ProtocolConfig(input_resources={"scrambler": 1}, cooldown=1),
|
|
439
|
-
ProtocolConfig(input_resources={"resonator": 1}, cooldown=1),
|
|
440
|
-
ProtocolConfig(input_resources={"modulator": 1}, cooldown=1),
|
|
441
|
-
ProtocolConfig(input_resources={"decoder": 1}, cooldown=1),
|
|
442
|
-
]
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
class ClipHubStationsVariant(MissionVariant):
|
|
446
|
-
name: str = "clip_hub_stations"
|
|
447
|
-
description: str = "Clip the specified base stations (by name)."
|
|
448
|
-
# Valid names: "carbon_extractor", "oxygen_extractor", "germanium_extractor", "silicon_extractor", "charger"
|
|
449
|
-
clip: list[str] = ["carbon_extractor", "oxygen_extractor", "germanium_extractor", "silicon_extractor", "charger"]
|
|
450
|
-
|
|
451
|
-
@override
|
|
452
|
-
def modify_mission(self, mission):
|
|
453
|
-
for station_name in self.clip:
|
|
454
|
-
station = getattr(mission, station_name, None)
|
|
455
|
-
if station is not None:
|
|
456
|
-
station.start_clipped = True
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
class ClipPeriodOnVariant(MissionVariant):
|
|
460
|
-
name: str = "clip_period_on"
|
|
461
|
-
description: str = "Enable global clipping with a small non-zero clip period."
|
|
462
|
-
clip_period: int = 50
|
|
463
|
-
|
|
464
|
-
@override
|
|
465
|
-
def modify_mission(self, mission):
|
|
466
|
-
mission.clip_period = self.clip_period
|
|
467
|
-
|
|
468
|
-
|
|
469
92
|
# Biome variants (weather) for procedural maps
|
|
470
93
|
class DesertVariant(MachinaArenaVariant):
|
|
471
94
|
name: str = "desert"
|
|
@@ -498,7 +121,6 @@ class CityVariant(MachinaArenaVariant):
|
|
|
498
121
|
node.density_scale = 1.0
|
|
499
122
|
node.biome_count = 1
|
|
500
123
|
node.max_biome_zone_fraction = 0.95
|
|
501
|
-
# Tighten the city grid itself
|
|
502
124
|
|
|
503
125
|
|
|
504
126
|
class CavesVariant(MachinaArenaVariant):
|
|
@@ -547,34 +169,6 @@ class DistantResourcesVariant(MachinaArenaVariant):
|
|
|
547
169
|
node.distribution = DistributionConfig(type=DistributionType.UNIFORM)
|
|
548
170
|
|
|
549
171
|
|
|
550
|
-
class SingleUseSwarmVariant(MissionVariant):
|
|
551
|
-
name: str = "single_use_swarm"
|
|
552
|
-
description: str = "Everything is single use; agents must fan out and reconverge."
|
|
553
|
-
building_coverage: float = 0.03
|
|
554
|
-
|
|
555
|
-
@override
|
|
556
|
-
def modify_mission(self, mission):
|
|
557
|
-
# Make each extractor single-use
|
|
558
|
-
for res in ("carbon", "oxygen", "silicon"):
|
|
559
|
-
extractor = getattr(mission, f"{res}_extractor", None)
|
|
560
|
-
if extractor is not None:
|
|
561
|
-
extractor.max_uses = 1
|
|
562
|
-
|
|
563
|
-
@override
|
|
564
|
-
def modify_env(self, mission, env):
|
|
565
|
-
# Ensure charger is also single-use (its Config defaults to unlimited)
|
|
566
|
-
charger = env.game.objects.get("charger")
|
|
567
|
-
if isinstance(charger, AssemblerConfig):
|
|
568
|
-
charger.max_uses = 1
|
|
569
|
-
|
|
570
|
-
# Increase building coverage a bit to create many single-use points
|
|
571
|
-
map_builder = getattr(env.game, "map_builder", None)
|
|
572
|
-
instance = getattr(map_builder, "instance", None)
|
|
573
|
-
if instance is not None and hasattr(instance, "building_coverage"):
|
|
574
|
-
current = float(getattr(instance, "building_coverage", 0.01))
|
|
575
|
-
instance.building_coverage = max(current, float(self.building_coverage))
|
|
576
|
-
|
|
577
|
-
|
|
578
172
|
class QuadrantBuildingsVariant(MachinaArenaVariant):
|
|
579
173
|
name: str = "quadrant_buildings"
|
|
580
174
|
description: str = "Place buildings in the four quadrants of the map."
|
|
@@ -635,35 +229,6 @@ class EmptyBaseVariant(BaseHubVariant):
|
|
|
635
229
|
node.corner_bundle = "custom"
|
|
636
230
|
|
|
637
231
|
|
|
638
|
-
class AssemblerDrawsFromChestsVariant(BaseHubVariant):
|
|
639
|
-
name: str = "assembler_draws_from_chests"
|
|
640
|
-
description: str = "Assembler draws from chests."
|
|
641
|
-
|
|
642
|
-
# It would be better if this were configurable, but we use variants in places where that's hard.
|
|
643
|
-
# This needs to not overlap with the default (heart) chest.
|
|
644
|
-
chest_distance: int = 2
|
|
645
|
-
|
|
646
|
-
@override
|
|
647
|
-
def modify_node(self, node):
|
|
648
|
-
node.cross_objects = ["chest_carbon", "chest_oxygen", "chest_germanium", "chest_silicon"]
|
|
649
|
-
node.cross_bundle = "custom"
|
|
650
|
-
node.cross_distance = self.chest_distance
|
|
651
|
-
|
|
652
|
-
@override
|
|
653
|
-
def modify_env(self, mission, env):
|
|
654
|
-
super().modify_env(mission, env)
|
|
655
|
-
assembler = env.game.objects["assembler"]
|
|
656
|
-
assert isinstance(assembler, AssemblerConfig)
|
|
657
|
-
assembler.chest_search_distance = self.chest_distance
|
|
658
|
-
chest = env.game.objects["chest"]
|
|
659
|
-
assert isinstance(chest, ChestConfig)
|
|
660
|
-
chest.vibe_transfers = {
|
|
661
|
-
"default": {
|
|
662
|
-
"heart": 255,
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
|
|
667
232
|
class BalancedCornersVariant(MachinaArenaVariant):
|
|
668
233
|
"""Enable corner balancing to ensure fair spawn distances."""
|
|
669
234
|
|
|
@@ -710,46 +275,30 @@ class SharedRewardsVariant(MissionVariant):
|
|
|
710
275
|
@override
|
|
711
276
|
def modify_env(self, mission, env):
|
|
712
277
|
num_cogs = mission.num_cogs if mission.num_cogs is not None else mission.site.min_cogs
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
env.game.agent.rewards.
|
|
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)
|
|
717
282
|
|
|
718
283
|
|
|
719
284
|
# TODO - validate that all variant names are unique
|
|
720
285
|
VARIANTS: list[MissionVariant] = [
|
|
721
|
-
AssemblerDrawsFromChestsVariant(),
|
|
722
286
|
CavesVariant(),
|
|
723
|
-
ChestHeartTuneVariant(),
|
|
724
287
|
CityVariant(),
|
|
725
|
-
ClipHubStationsVariant(),
|
|
726
|
-
ClipPeriodOnVariant(),
|
|
727
288
|
CompassVariant(),
|
|
728
|
-
CyclicalUnclipVariant(),
|
|
729
289
|
DarkSideVariant(),
|
|
730
290
|
DesertVariant(),
|
|
731
291
|
EmptyBaseVariant(),
|
|
732
292
|
EnergizedVariant(),
|
|
733
|
-
ExtractorHeartTuneVariant(),
|
|
734
293
|
ForestVariant(),
|
|
735
|
-
HeartChorusVariant(),
|
|
736
|
-
InventoryHeartTuneVariant(),
|
|
737
|
-
LonelyHeartVariant(),
|
|
738
|
-
MinedOutVariant(),
|
|
739
294
|
PackRatVariant(),
|
|
740
295
|
QuadrantBuildingsVariant(),
|
|
741
|
-
ResourceBottleneckVariant(),
|
|
742
296
|
RoughTerrainVariant(),
|
|
743
297
|
SharedRewardsVariant(),
|
|
744
298
|
SingleResourceUniformVariant(),
|
|
745
|
-
SingleToolUnclipVariant(),
|
|
746
299
|
Small50Variant(),
|
|
747
|
-
SolarFlareVariant(),
|
|
748
300
|
SuperChargedVariant(),
|
|
749
301
|
TraderVariant(),
|
|
750
|
-
TinyHeartProtocolsVariant(),
|
|
751
|
-
TrainingVariant(),
|
|
752
|
-
VibeCheckMin2Variant(),
|
|
753
302
|
*DIFFICULTY_VARIANTS,
|
|
754
303
|
]
|
|
755
304
|
|
cogames/device.py
CHANGED
|
@@ -15,10 +15,18 @@ def resolve_training_device(console: Console, requested: str) -> torch.device:
|
|
|
15
15
|
return False
|
|
16
16
|
return torch.cuda.is_available()
|
|
17
17
|
|
|
18
|
+
def mps_usable() -> bool:
|
|
19
|
+
mps_backend = getattr(torch.backends, "mps", None)
|
|
20
|
+
if mps_backend is None or not mps_backend.is_built():
|
|
21
|
+
return False
|
|
22
|
+
return mps_backend.is_available()
|
|
23
|
+
|
|
18
24
|
if normalized == "auto":
|
|
19
25
|
if cuda_usable():
|
|
20
26
|
return torch.device("cuda")
|
|
21
|
-
|
|
27
|
+
if mps_usable():
|
|
28
|
+
return torch.device("mps")
|
|
29
|
+
console.print("[yellow]CUDA/MPS not available; falling back to CPU for training.[/yellow]")
|
|
22
30
|
return torch.device("cpu")
|
|
23
31
|
|
|
24
32
|
try:
|
|
@@ -31,4 +39,8 @@ def resolve_training_device(console: Console, requested: str) -> torch.device:
|
|
|
31
39
|
console.print("[yellow]CUDA requested but unavailable. Training will run on CPU instead.[/yellow]")
|
|
32
40
|
return torch.device("cpu")
|
|
33
41
|
|
|
42
|
+
if candidate.type == "mps" and not mps_usable():
|
|
43
|
+
console.print("[yellow]MPS requested but unavailable. Training will run on CPU instead.[/yellow]")
|
|
44
|
+
return torch.device("cpu")
|
|
45
|
+
|
|
34
46
|
return candidate
|