cogames-agents 0.0.0.7__cp312-cp312-macosx_11_0_arm64.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_agents/__init__.py +0 -0
- cogames_agents/evals/__init__.py +5 -0
- cogames_agents/evals/planky_evals.py +415 -0
- cogames_agents/policy/__init__.py +0 -0
- cogames_agents/policy/evolution/__init__.py +0 -0
- cogames_agents/policy/evolution/cogsguard/__init__.py +0 -0
- cogames_agents/policy/evolution/cogsguard/evolution.py +695 -0
- cogames_agents/policy/evolution/cogsguard/evolutionary_coordinator.py +540 -0
- cogames_agents/policy/nim_agents/__init__.py +20 -0
- cogames_agents/policy/nim_agents/agents.py +98 -0
- cogames_agents/policy/nim_agents/bindings/generated/libnim_agents.dylib +0 -0
- cogames_agents/policy/nim_agents/bindings/generated/nim_agents.py +215 -0
- cogames_agents/policy/nim_agents/cogsguard_agents.nim +555 -0
- cogames_agents/policy/nim_agents/cogsguard_align_all_agents.nim +569 -0
- cogames_agents/policy/nim_agents/common.nim +1054 -0
- cogames_agents/policy/nim_agents/install.sh +1 -0
- cogames_agents/policy/nim_agents/ladybug_agent.nim +954 -0
- cogames_agents/policy/nim_agents/nim_agents.nim +68 -0
- cogames_agents/policy/nim_agents/nim_agents.nims +14 -0
- cogames_agents/policy/nim_agents/nimby.lock +3 -0
- cogames_agents/policy/nim_agents/racecar_agents.nim +844 -0
- cogames_agents/policy/nim_agents/random_agents.nim +68 -0
- cogames_agents/policy/nim_agents/test_agents.py +53 -0
- cogames_agents/policy/nim_agents/thinky_agents.nim +677 -0
- cogames_agents/policy/nim_agents/thinky_eval.py +230 -0
- cogames_agents/policy/scripted_agent/README.md +360 -0
- cogames_agents/policy/scripted_agent/__init__.py +0 -0
- cogames_agents/policy/scripted_agent/baseline_agent.py +1031 -0
- cogames_agents/policy/scripted_agent/cogas/__init__.py +5 -0
- cogames_agents/policy/scripted_agent/cogas/context.py +68 -0
- cogames_agents/policy/scripted_agent/cogas/entity_map.py +152 -0
- cogames_agents/policy/scripted_agent/cogas/goal.py +115 -0
- cogames_agents/policy/scripted_agent/cogas/goals/__init__.py +27 -0
- cogames_agents/policy/scripted_agent/cogas/goals/aligner.py +160 -0
- cogames_agents/policy/scripted_agent/cogas/goals/gear.py +197 -0
- cogames_agents/policy/scripted_agent/cogas/goals/miner.py +441 -0
- cogames_agents/policy/scripted_agent/cogas/goals/scout.py +40 -0
- cogames_agents/policy/scripted_agent/cogas/goals/scrambler.py +174 -0
- cogames_agents/policy/scripted_agent/cogas/goals/shared.py +160 -0
- cogames_agents/policy/scripted_agent/cogas/goals/stem.py +60 -0
- cogames_agents/policy/scripted_agent/cogas/goals/survive.py +100 -0
- cogames_agents/policy/scripted_agent/cogas/navigator.py +401 -0
- cogames_agents/policy/scripted_agent/cogas/obs_parser.py +238 -0
- cogames_agents/policy/scripted_agent/cogas/policy.py +525 -0
- cogames_agents/policy/scripted_agent/cogas/trace.py +69 -0
- cogames_agents/policy/scripted_agent/cogsguard/CLAUDE.md +517 -0
- cogames_agents/policy/scripted_agent/cogsguard/README.md +252 -0
- cogames_agents/policy/scripted_agent/cogsguard/__init__.py +74 -0
- cogames_agents/policy/scripted_agent/cogsguard/aligned_junction_held_investigation.md +152 -0
- cogames_agents/policy/scripted_agent/cogsguard/aligner.py +333 -0
- cogames_agents/policy/scripted_agent/cogsguard/behavior_hooks.py +44 -0
- cogames_agents/policy/scripted_agent/cogsguard/control_agent.py +323 -0
- cogames_agents/policy/scripted_agent/cogsguard/debug_agent.py +533 -0
- cogames_agents/policy/scripted_agent/cogsguard/miner.py +589 -0
- cogames_agents/policy/scripted_agent/cogsguard/options.py +67 -0
- cogames_agents/policy/scripted_agent/cogsguard/parity_metrics.py +36 -0
- cogames_agents/policy/scripted_agent/cogsguard/policy.py +1967 -0
- cogames_agents/policy/scripted_agent/cogsguard/prereq_trace.py +33 -0
- cogames_agents/policy/scripted_agent/cogsguard/role_trace.py +50 -0
- cogames_agents/policy/scripted_agent/cogsguard/roles.py +31 -0
- cogames_agents/policy/scripted_agent/cogsguard/rollout_trace.py +40 -0
- cogames_agents/policy/scripted_agent/cogsguard/scout.py +69 -0
- cogames_agents/policy/scripted_agent/cogsguard/scrambler.py +350 -0
- cogames_agents/policy/scripted_agent/cogsguard/targeted_agent.py +418 -0
- cogames_agents/policy/scripted_agent/cogsguard/teacher.py +224 -0
- cogames_agents/policy/scripted_agent/cogsguard/types.py +381 -0
- cogames_agents/policy/scripted_agent/cogsguard/v2_agent.py +49 -0
- cogames_agents/policy/scripted_agent/common/__init__.py +0 -0
- cogames_agents/policy/scripted_agent/common/geometry.py +24 -0
- cogames_agents/policy/scripted_agent/common/roles.py +34 -0
- cogames_agents/policy/scripted_agent/common/tag_utils.py +48 -0
- cogames_agents/policy/scripted_agent/demo_policy.py +242 -0
- cogames_agents/policy/scripted_agent/pathfinding.py +126 -0
- cogames_agents/policy/scripted_agent/pinky/DESIGN.md +317 -0
- cogames_agents/policy/scripted_agent/pinky/__init__.py +5 -0
- cogames_agents/policy/scripted_agent/pinky/behaviors/__init__.py +17 -0
- cogames_agents/policy/scripted_agent/pinky/behaviors/aligner.py +400 -0
- cogames_agents/policy/scripted_agent/pinky/behaviors/base.py +119 -0
- cogames_agents/policy/scripted_agent/pinky/behaviors/miner.py +632 -0
- cogames_agents/policy/scripted_agent/pinky/behaviors/scout.py +138 -0
- cogames_agents/policy/scripted_agent/pinky/behaviors/scrambler.py +433 -0
- cogames_agents/policy/scripted_agent/pinky/policy.py +570 -0
- cogames_agents/policy/scripted_agent/pinky/services/__init__.py +7 -0
- cogames_agents/policy/scripted_agent/pinky/services/map_tracker.py +808 -0
- cogames_agents/policy/scripted_agent/pinky/services/navigator.py +864 -0
- cogames_agents/policy/scripted_agent/pinky/services/safety.py +189 -0
- cogames_agents/policy/scripted_agent/pinky/state.py +299 -0
- cogames_agents/policy/scripted_agent/pinky/types.py +138 -0
- cogames_agents/policy/scripted_agent/planky/CLAUDE.md +124 -0
- cogames_agents/policy/scripted_agent/planky/IMPROVEMENTS.md +160 -0
- cogames_agents/policy/scripted_agent/planky/NOTES.md +153 -0
- cogames_agents/policy/scripted_agent/planky/PLAN.md +254 -0
- cogames_agents/policy/scripted_agent/planky/README.md +214 -0
- cogames_agents/policy/scripted_agent/planky/STRATEGY.md +100 -0
- cogames_agents/policy/scripted_agent/planky/__init__.py +5 -0
- cogames_agents/policy/scripted_agent/planky/context.py +68 -0
- cogames_agents/policy/scripted_agent/planky/entity_map.py +152 -0
- cogames_agents/policy/scripted_agent/planky/goal.py +107 -0
- cogames_agents/policy/scripted_agent/planky/goals/__init__.py +27 -0
- cogames_agents/policy/scripted_agent/planky/goals/aligner.py +168 -0
- cogames_agents/policy/scripted_agent/planky/goals/gear.py +179 -0
- cogames_agents/policy/scripted_agent/planky/goals/miner.py +416 -0
- cogames_agents/policy/scripted_agent/planky/goals/scout.py +40 -0
- cogames_agents/policy/scripted_agent/planky/goals/scrambler.py +174 -0
- cogames_agents/policy/scripted_agent/planky/goals/shared.py +160 -0
- cogames_agents/policy/scripted_agent/planky/goals/stem.py +49 -0
- cogames_agents/policy/scripted_agent/planky/goals/survive.py +96 -0
- cogames_agents/policy/scripted_agent/planky/navigator.py +388 -0
- cogames_agents/policy/scripted_agent/planky/obs_parser.py +238 -0
- cogames_agents/policy/scripted_agent/planky/policy.py +485 -0
- cogames_agents/policy/scripted_agent/planky/tests/__init__.py +0 -0
- cogames_agents/policy/scripted_agent/planky/tests/conftest.py +66 -0
- cogames_agents/policy/scripted_agent/planky/tests/helpers.py +152 -0
- cogames_agents/policy/scripted_agent/planky/tests/test_aligner.py +24 -0
- cogames_agents/policy/scripted_agent/planky/tests/test_miner.py +30 -0
- cogames_agents/policy/scripted_agent/planky/tests/test_scout.py +15 -0
- cogames_agents/policy/scripted_agent/planky/tests/test_scrambler.py +29 -0
- cogames_agents/policy/scripted_agent/planky/tests/test_stem.py +36 -0
- cogames_agents/policy/scripted_agent/planky/trace.py +69 -0
- cogames_agents/policy/scripted_agent/types.py +239 -0
- cogames_agents/policy/scripted_agent/unclipping_agent.py +461 -0
- cogames_agents/policy/scripted_agent/utils.py +381 -0
- cogames_agents/policy/scripted_registry.py +80 -0
- cogames_agents/py.typed +0 -0
- cogames_agents-0.0.0.7.dist-info/METADATA +98 -0
- cogames_agents-0.0.0.7.dist-info/RECORD +128 -0
- cogames_agents-0.0.0.7.dist-info/WHEEL +6 -0
- cogames_agents-0.0.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""GetGearGoal — navigate to a station to acquire gear."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Optional
|
|
6
|
+
|
|
7
|
+
from cogames_agents.policy.scripted_agent.cogas.goal import Goal
|
|
8
|
+
from cogames_agents.policy.scripted_agent.cogas.navigator import _manhattan
|
|
9
|
+
from mettagrid.simulator import Action
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from cogames_agents.policy.scripted_agent.cogas.context import CogasContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GetGearGoal(Goal):
|
|
16
|
+
"""Navigate to a station to acquire gear for a role.
|
|
17
|
+
|
|
18
|
+
If the team lacks resources to produce gear, the station won't give any.
|
|
19
|
+
Checks collective resources before attempting, to avoid wasting time bumping
|
|
20
|
+
a station that can't dispense gear.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# How many bump attempts at dist=1 before exploring for another route
|
|
24
|
+
MAX_BUMPS_AT_STATION = 5
|
|
25
|
+
# How many total steps trying to get gear before giving up temporarily
|
|
26
|
+
MAX_TOTAL_ATTEMPTS = 80
|
|
27
|
+
# How many steps to wait before trying again
|
|
28
|
+
RETRY_INTERVAL = 150
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
gear_attr: str,
|
|
33
|
+
station_type: str,
|
|
34
|
+
goal_name: str,
|
|
35
|
+
gear_cost: dict[str, int] | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
self.name = goal_name
|
|
38
|
+
self._gear_attr = gear_attr # e.g. "miner_gear"
|
|
39
|
+
self._station_type = station_type # e.g. "miner_station"
|
|
40
|
+
self._gear_cost = gear_cost or {}
|
|
41
|
+
self._bb_attempts_key = f"{goal_name}_total_attempts"
|
|
42
|
+
self._bb_giveup_step_key = f"{goal_name}_giveup_step"
|
|
43
|
+
self._bb_bump_count_key = f"{goal_name}_bump_count"
|
|
44
|
+
self._bb_last_dist_key = f"{goal_name}_last_dist"
|
|
45
|
+
|
|
46
|
+
# Minimum collective resource reserve — don't consume below this level
|
|
47
|
+
# Reduced from 3 to 1 for faster gear acquisition
|
|
48
|
+
RESOURCE_RESERVE = 1
|
|
49
|
+
|
|
50
|
+
def _collective_can_afford(self, ctx: CogasContext) -> bool:
|
|
51
|
+
"""Check if the collective can afford gear while maintaining reserves."""
|
|
52
|
+
if not self._gear_cost:
|
|
53
|
+
return True
|
|
54
|
+
s = ctx.state
|
|
55
|
+
collective = {
|
|
56
|
+
"carbon": s.collective_carbon,
|
|
57
|
+
"oxygen": s.collective_oxygen,
|
|
58
|
+
"germanium": s.collective_germanium,
|
|
59
|
+
"silicon": s.collective_silicon,
|
|
60
|
+
}
|
|
61
|
+
# Must have cost + reserve for each resource
|
|
62
|
+
return all(collective.get(res, 0) >= amt + self.RESOURCE_RESERVE for res, amt in self._gear_cost.items())
|
|
63
|
+
|
|
64
|
+
def _get_hub_center(self, ctx: CogasContext) -> tuple[int, int]:
|
|
65
|
+
"""Find hub center from observations, falling back to current position."""
|
|
66
|
+
# Check if we already cached the hub position
|
|
67
|
+
cached_hub = ctx.blackboard.get("_hub_center")
|
|
68
|
+
if cached_hub is not None:
|
|
69
|
+
return cached_hub
|
|
70
|
+
|
|
71
|
+
# Try to find hub from entity map
|
|
72
|
+
pf = {"collective_id": ctx.my_collective_id} if ctx.my_collective_id is not None else None
|
|
73
|
+
hub = ctx.map.find_nearest(ctx.state.position, type_contains="hub", property_filter=pf)
|
|
74
|
+
if hub is not None:
|
|
75
|
+
hub_pos, _ = hub
|
|
76
|
+
ctx.blackboard["_hub_center"] = hub_pos
|
|
77
|
+
return hub_pos
|
|
78
|
+
|
|
79
|
+
# Fall back to current position if hub not visible yet
|
|
80
|
+
return ctx.state.position
|
|
81
|
+
|
|
82
|
+
def is_satisfied(self, ctx: CogasContext) -> bool:
|
|
83
|
+
# Satisfied if we have the gear
|
|
84
|
+
if getattr(ctx.state, self._gear_attr, False):
|
|
85
|
+
# Got gear - reset attempts for next time
|
|
86
|
+
ctx.blackboard[self._bb_attempts_key] = 0
|
|
87
|
+
ctx.blackboard[self._bb_bump_count_key] = 0
|
|
88
|
+
return True
|
|
89
|
+
# Also "satisfied" (skip) if we gave up recently
|
|
90
|
+
giveup_step = ctx.blackboard.get(self._bb_giveup_step_key, -9999)
|
|
91
|
+
if ctx.step - giveup_step < self.RETRY_INTERVAL:
|
|
92
|
+
return True
|
|
93
|
+
# Skip if collective can't afford this gear
|
|
94
|
+
if not self._collective_can_afford(ctx):
|
|
95
|
+
if ctx.trace:
|
|
96
|
+
ctx.trace.skip(self.name, "collective lacks resources")
|
|
97
|
+
return True
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
def execute(self, ctx: CogasContext) -> Optional[Action]:
|
|
101
|
+
# Track total attempts regardless of distance
|
|
102
|
+
attempts = ctx.blackboard.get(self._bb_attempts_key, 0) + 1
|
|
103
|
+
ctx.blackboard[self._bb_attempts_key] = attempts
|
|
104
|
+
|
|
105
|
+
if attempts > self.MAX_TOTAL_ATTEMPTS:
|
|
106
|
+
# Give up - team probably lacks resources or station unreachable
|
|
107
|
+
ctx.blackboard[self._bb_giveup_step_key] = ctx.step
|
|
108
|
+
ctx.blackboard[self._bb_attempts_key] = 0
|
|
109
|
+
ctx.blackboard[self._bb_bump_count_key] = 0
|
|
110
|
+
if ctx.trace:
|
|
111
|
+
ctx.trace.activate(self.name, "giving up after max attempts")
|
|
112
|
+
return None # Skip to next goal
|
|
113
|
+
|
|
114
|
+
# Find station by type (filter to own team if known)
|
|
115
|
+
pf = {"collective_id": ctx.my_collective_id} if ctx.my_collective_id is not None else None
|
|
116
|
+
result = ctx.map.find_nearest(ctx.state.position, type_contains=self._station_type, property_filter=pf)
|
|
117
|
+
if result is None:
|
|
118
|
+
# Station not discovered yet — navigate toward hub where stations are
|
|
119
|
+
hub_pos = self._get_hub_center(ctx)
|
|
120
|
+
hub_dist = _manhattan(ctx.state.position, hub_pos)
|
|
121
|
+
if ctx.trace:
|
|
122
|
+
ctx.trace.activate(self.name, f"exploring for {self._station_type} (hub dist={hub_dist})")
|
|
123
|
+
if hub_dist > 3:
|
|
124
|
+
# Navigate toward hub
|
|
125
|
+
return ctx.navigator.get_action(ctx.state.position, hub_pos, ctx.map, reach_adjacent=True)
|
|
126
|
+
# At hub — explore nearby to find the station
|
|
127
|
+
return ctx.navigator.explore(ctx.state.position, ctx.map)
|
|
128
|
+
|
|
129
|
+
station_pos, _ = result
|
|
130
|
+
dist = _manhattan(ctx.state.position, station_pos)
|
|
131
|
+
|
|
132
|
+
if ctx.trace:
|
|
133
|
+
ctx.trace.nav_target = station_pos
|
|
134
|
+
|
|
135
|
+
# Track if we're making progress toward the station
|
|
136
|
+
last_dist = ctx.blackboard.get(self._bb_last_dist_key, 999)
|
|
137
|
+
ctx.blackboard[self._bb_last_dist_key] = dist
|
|
138
|
+
|
|
139
|
+
if dist <= 1:
|
|
140
|
+
# Adjacent to station — try to bump into it
|
|
141
|
+
bump_count = ctx.blackboard.get(self._bb_bump_count_key, 0) + 1
|
|
142
|
+
ctx.blackboard[self._bb_bump_count_key] = bump_count
|
|
143
|
+
|
|
144
|
+
if bump_count > self.MAX_BUMPS_AT_STATION:
|
|
145
|
+
# Stuck at dist=1 - explore to find another path
|
|
146
|
+
ctx.blackboard[self._bb_bump_count_key] = 0
|
|
147
|
+
if ctx.trace:
|
|
148
|
+
ctx.trace.activate(self.name, "stuck at dist=1, exploring")
|
|
149
|
+
# Clear navigator cache and explore a random direction
|
|
150
|
+
ctx.navigator._cached_path = None
|
|
151
|
+
ctx.navigator._cached_target = None
|
|
152
|
+
return ctx.navigator.explore(ctx.state.position, ctx.map)
|
|
153
|
+
|
|
154
|
+
if ctx.trace:
|
|
155
|
+
ctx.trace.activate(self.name, f"bump {bump_count}/{self.MAX_BUMPS_AT_STATION}")
|
|
156
|
+
return _move_toward(ctx.state.position, station_pos)
|
|
157
|
+
|
|
158
|
+
# Not adjacent yet - navigate toward station
|
|
159
|
+
ctx.blackboard[self._bb_bump_count_key] = 0
|
|
160
|
+
|
|
161
|
+
# If we're not making progress (dist not decreasing), clear cache and try fresh path
|
|
162
|
+
if dist >= last_dist and attempts > 10:
|
|
163
|
+
ctx.navigator._cached_path = None
|
|
164
|
+
ctx.navigator._cached_target = None
|
|
165
|
+
|
|
166
|
+
return ctx.navigator.get_action(ctx.state.position, station_pos, ctx.map, reach_adjacent=True)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _move_toward(current: tuple[int, int], target: tuple[int, int]) -> Action:
|
|
170
|
+
"""Move one step toward target, trying the most direct direction."""
|
|
171
|
+
dr = target[0] - current[0]
|
|
172
|
+
dc = target[1] - current[1]
|
|
173
|
+
|
|
174
|
+
# When exactly adjacent (dist=1), we want to bump INTO the target
|
|
175
|
+
# Return the direction that would move us onto the target
|
|
176
|
+
if dr == 1 and dc == 0:
|
|
177
|
+
return Action(name="move_south")
|
|
178
|
+
if dr == -1 and dc == 0:
|
|
179
|
+
return Action(name="move_north")
|
|
180
|
+
if dr == 0 and dc == 1:
|
|
181
|
+
return Action(name="move_east")
|
|
182
|
+
if dr == 0 and dc == -1:
|
|
183
|
+
return Action(name="move_west")
|
|
184
|
+
|
|
185
|
+
# For larger distances, prefer the longer axis
|
|
186
|
+
if abs(dr) >= abs(dc):
|
|
187
|
+
if dr > 0:
|
|
188
|
+
return Action(name="move_south")
|
|
189
|
+
elif dr < 0:
|
|
190
|
+
return Action(name="move_north")
|
|
191
|
+
if dc > 0:
|
|
192
|
+
return Action(name="move_east")
|
|
193
|
+
elif dc < 0:
|
|
194
|
+
return Action(name="move_west")
|
|
195
|
+
|
|
196
|
+
# On target — shouldn't happen, but bump north as fallback
|
|
197
|
+
return Action(name="move_north")
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
"""Miner goals — pick resource, mine, deposit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Optional
|
|
6
|
+
|
|
7
|
+
from cogames_agents.policy.scripted_agent.cogas.goal import Goal
|
|
8
|
+
from cogames_agents.policy.scripted_agent.cogas.navigator import _manhattan
|
|
9
|
+
from mettagrid.simulator import Action
|
|
10
|
+
|
|
11
|
+
from .gear import GetGearGoal
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from cogames_agents.policy.scripted_agent.cogas.context import CogasContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GetMinerGearGoal(GetGearGoal):
|
|
18
|
+
"""Get miner gear (costs C1 O1 G3 S1 from collective).
|
|
19
|
+
|
|
20
|
+
Miners always get gear regardless of reserves — they produce resources.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
super().__init__(
|
|
25
|
+
gear_attr="miner_gear",
|
|
26
|
+
station_type="miner_station",
|
|
27
|
+
goal_name="GetMinerGear",
|
|
28
|
+
gear_cost={"carbon": 1, "oxygen": 1, "germanium": 3, "silicon": 1},
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def _collective_can_afford(self, ctx: "CogasContext") -> bool:
|
|
32
|
+
"""Miners always get gear — they're the resource producers.
|
|
33
|
+
|
|
34
|
+
But skip if collective is already well-stocked (no need to mine).
|
|
35
|
+
"""
|
|
36
|
+
if _collective_resources_sufficient(ctx):
|
|
37
|
+
return False
|
|
38
|
+
if not self._gear_cost:
|
|
39
|
+
return True
|
|
40
|
+
s = ctx.state
|
|
41
|
+
collective = {
|
|
42
|
+
"carbon": s.collective_carbon,
|
|
43
|
+
"oxygen": s.collective_oxygen,
|
|
44
|
+
"germanium": s.collective_germanium,
|
|
45
|
+
"silicon": s.collective_silicon,
|
|
46
|
+
}
|
|
47
|
+
# No reserve requirement for miners — just need the cost
|
|
48
|
+
return all(collective.get(res, 0) >= amt for res, amt in self._gear_cost.items())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Resource types that can be mined
|
|
52
|
+
RESOURCE_TYPES = ["carbon", "oxygen", "germanium", "silicon"]
|
|
53
|
+
|
|
54
|
+
# When the collective has more than this amount of every resource, stop mining.
|
|
55
|
+
COLLECTIVE_SUFFICIENT_THRESHOLD = 100
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _collective_resources_sufficient(ctx: "CogasContext") -> bool:
|
|
59
|
+
"""Return True when the collective has >COLLECTIVE_SUFFICIENT_THRESHOLD of every resource."""
|
|
60
|
+
s = ctx.state
|
|
61
|
+
return (
|
|
62
|
+
s.collective_carbon > COLLECTIVE_SUFFICIENT_THRESHOLD
|
|
63
|
+
and s.collective_oxygen > COLLECTIVE_SUFFICIENT_THRESHOLD
|
|
64
|
+
and s.collective_germanium > COLLECTIVE_SUFFICIENT_THRESHOLD
|
|
65
|
+
and s.collective_silicon > COLLECTIVE_SUFFICIENT_THRESHOLD
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ExploreHubGoal(Goal):
|
|
70
|
+
"""Explore the hub to discover all 4 extractors before mining.
|
|
71
|
+
|
|
72
|
+
Finds the actual hub from observations (not hardcoded position) and
|
|
73
|
+
explores nearby corners to discover extractors.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
name = "ExploreHub"
|
|
77
|
+
# Hub corner offsets from hub center — extractors at these positions
|
|
78
|
+
HUB_OFFSETS = [(-5, -5), (-5, 5), (5, 5), (5, -5)]
|
|
79
|
+
|
|
80
|
+
def is_satisfied(self, ctx: CogasContext) -> bool:
|
|
81
|
+
found = sum(1 for r in RESOURCE_TYPES if ctx.map.find(type=f"{r}_extractor"))
|
|
82
|
+
if found >= 4:
|
|
83
|
+
return True
|
|
84
|
+
# Time limit: don't explore forever, but give more time (30 steps)
|
|
85
|
+
if ctx.step > 30:
|
|
86
|
+
return True
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
def execute(self, ctx: CogasContext) -> Optional[Action]:
|
|
90
|
+
# Find actual hub position from observations
|
|
91
|
+
hub_center = self._get_hub_center(ctx)
|
|
92
|
+
|
|
93
|
+
corner_idx = ctx.blackboard.get("_hub_corner_idx", ctx.agent_id % 4)
|
|
94
|
+
offsets = self.HUB_OFFSETS
|
|
95
|
+
target = (hub_center[0] + offsets[corner_idx][0], hub_center[1] + offsets[corner_idx][1])
|
|
96
|
+
|
|
97
|
+
dist = _manhattan(ctx.state.position, target)
|
|
98
|
+
if dist <= 2:
|
|
99
|
+
corner_idx = (corner_idx + 1) % 4
|
|
100
|
+
ctx.blackboard["_hub_corner_idx"] = corner_idx
|
|
101
|
+
target = (hub_center[0] + offsets[corner_idx][0], hub_center[1] + offsets[corner_idx][1])
|
|
102
|
+
|
|
103
|
+
if ctx.trace:
|
|
104
|
+
ctx.trace.nav_target = target
|
|
105
|
+
found = sum(1 for r in RESOURCE_TYPES if ctx.map.find(type=f"{r}_extractor"))
|
|
106
|
+
ctx.trace.activate(self.name, f"corner={corner_idx} found={found}/4 hub={hub_center}")
|
|
107
|
+
|
|
108
|
+
# Check dist to avoid navigator returning noop
|
|
109
|
+
new_dist = _manhattan(ctx.state.position, target)
|
|
110
|
+
if new_dist <= 1:
|
|
111
|
+
return _move_toward(ctx.state.position, target)
|
|
112
|
+
return ctx.navigator.get_action(ctx.state.position, target, ctx.map, reach_adjacent=True)
|
|
113
|
+
|
|
114
|
+
def _get_hub_center(self, ctx: CogasContext) -> tuple[int, int]:
|
|
115
|
+
"""Find hub center from observations, falling back to current position."""
|
|
116
|
+
# Check if we already cached the hub position
|
|
117
|
+
cached_hub = ctx.blackboard.get("_hub_center")
|
|
118
|
+
if cached_hub is not None:
|
|
119
|
+
return cached_hub
|
|
120
|
+
|
|
121
|
+
# Try to find hub from entity map
|
|
122
|
+
pf = {"collective_id": ctx.my_collective_id} if ctx.my_collective_id is not None else None
|
|
123
|
+
hub = ctx.map.find_nearest(ctx.state.position, type_contains="hub", property_filter=pf)
|
|
124
|
+
if hub is not None:
|
|
125
|
+
hub_pos, _ = hub
|
|
126
|
+
ctx.blackboard["_hub_center"] = hub_pos
|
|
127
|
+
return hub_pos
|
|
128
|
+
|
|
129
|
+
# Fall back to current position if hub not visible yet
|
|
130
|
+
# (agent will explore from where it spawned)
|
|
131
|
+
return ctx.state.position
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class PickResourceGoal(Goal):
|
|
135
|
+
"""Select a target resource based on collective needs.
|
|
136
|
+
|
|
137
|
+
Prioritizes the resource that the collective has the least of,
|
|
138
|
+
ensuring balanced gathering for heart production.
|
|
139
|
+
Re-evaluates every 50 steps to adapt to changing needs.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
name = "PickResource"
|
|
143
|
+
REEVALUATE_INTERVAL = 50
|
|
144
|
+
|
|
145
|
+
def is_satisfied(self, ctx: CogasContext) -> bool:
|
|
146
|
+
# Don't bother picking a resource if collective is well-stocked
|
|
147
|
+
if _collective_resources_sufficient(ctx):
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
if "target_resource" not in ctx.blackboard:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
# Re-evaluate periodically to ensure we're mining what's needed
|
|
154
|
+
last_pick = ctx.blackboard.get("_target_resource_step", 0)
|
|
155
|
+
if ctx.step - last_pick >= self.REEVALUATE_INTERVAL:
|
|
156
|
+
# Clear to force re-evaluation
|
|
157
|
+
ctx.blackboard.pop("target_resource", None)
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
def execute(self, ctx: CogasContext) -> Optional[Action]:
|
|
163
|
+
# Get collective resource levels
|
|
164
|
+
collective = {
|
|
165
|
+
"carbon": ctx.state.collective_carbon,
|
|
166
|
+
"oxygen": ctx.state.collective_oxygen,
|
|
167
|
+
"germanium": ctx.state.collective_germanium,
|
|
168
|
+
"silicon": ctx.state.collective_silicon,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# Find resources with available extractors
|
|
172
|
+
available_resources: list[tuple[int, str]] = []
|
|
173
|
+
for resource in RESOURCE_TYPES:
|
|
174
|
+
extractors = ctx.map.find(type=f"{resource}_extractor")
|
|
175
|
+
usable = [
|
|
176
|
+
(pos, e)
|
|
177
|
+
for pos, e in extractors
|
|
178
|
+
if e.properties.get("remaining_uses", 999) > 0
|
|
179
|
+
and e.properties.get("inventory_amount", -1) != 0
|
|
180
|
+
and not _extractor_recently_failed(ctx, pos)
|
|
181
|
+
]
|
|
182
|
+
if usable:
|
|
183
|
+
# Score by collective amount (lower = higher priority)
|
|
184
|
+
available_resources.append((collective.get(resource, 0), resource))
|
|
185
|
+
|
|
186
|
+
if not available_resources:
|
|
187
|
+
# No extractors known — pick carbon as default, MineResource will explore
|
|
188
|
+
ctx.blackboard["target_resource"] = "carbon"
|
|
189
|
+
ctx.blackboard["_target_resource_step"] = ctx.step
|
|
190
|
+
if ctx.trace:
|
|
191
|
+
ctx.trace.activate(self.name, "no extractors known, defaulting to carbon")
|
|
192
|
+
# Return None to immediately continue to MineResource goal
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
# Pick the resource the collective has least of (that we can mine)
|
|
196
|
+
available_resources.sort()
|
|
197
|
+
best_resource = available_resources[0][1]
|
|
198
|
+
|
|
199
|
+
if ctx.trace:
|
|
200
|
+
ctx.trace.activate(self.name, f"need={best_resource} coll={collective}")
|
|
201
|
+
|
|
202
|
+
ctx.blackboard["target_resource"] = best_resource
|
|
203
|
+
ctx.blackboard["_target_resource_step"] = ctx.step
|
|
204
|
+
# Return None to immediately continue to next goal (mining)
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _extractor_recently_failed(ctx: CogasContext, pos: tuple[int, int]) -> bool:
|
|
209
|
+
"""Check if we recently failed to mine from this extractor."""
|
|
210
|
+
failed_step = ctx.blackboard.get(f"mine_failed_{pos}", -9999)
|
|
211
|
+
return ctx.step - failed_step < 100 # 100 step cooldown - extractors may refill
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class DepositCargoGoal(Goal):
|
|
215
|
+
"""Deposit resources at nearest cogs-aligned building when cargo is reasonably full.
|
|
216
|
+
|
|
217
|
+
Triggers when cargo is >= 50% full (or >= 10 resources for small capacity).
|
|
218
|
+
Once triggered, keeps depositing until cargo is EMPTY.
|
|
219
|
+
Tracks attempts and marks depots as failed if cargo doesn't decrease.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
name = "DepositCargo"
|
|
223
|
+
MAX_ATTEMPTS_PER_DEPOT = 5
|
|
224
|
+
|
|
225
|
+
def is_satisfied(self, ctx: CogasContext) -> bool:
|
|
226
|
+
cargo = ctx.state.cargo_total
|
|
227
|
+
|
|
228
|
+
# If we're currently depositing (flag set), keep going until empty
|
|
229
|
+
if ctx.blackboard.get("_depositing", False):
|
|
230
|
+
if cargo == 0:
|
|
231
|
+
ctx.blackboard["_depositing"] = False
|
|
232
|
+
return True
|
|
233
|
+
return False # Keep depositing until empty
|
|
234
|
+
|
|
235
|
+
# Not currently depositing - check if we should start
|
|
236
|
+
# Deposit when at least 50% full (but always deposit if cargo == capacity)
|
|
237
|
+
capacity = ctx.state.cargo_capacity
|
|
238
|
+
threshold = max(2, capacity // 2)
|
|
239
|
+
|
|
240
|
+
if cargo >= threshold:
|
|
241
|
+
ctx.blackboard["_depositing"] = True
|
|
242
|
+
return False # Start depositing
|
|
243
|
+
|
|
244
|
+
return True # Don't need to deposit yet
|
|
245
|
+
|
|
246
|
+
def execute(self, ctx: CogasContext) -> Optional[Action]:
|
|
247
|
+
# Track cargo to detect successful deposit
|
|
248
|
+
prev_cargo = ctx.blackboard.get("prev_deposit_cargo", ctx.state.cargo_total)
|
|
249
|
+
current_cargo = ctx.state.cargo_total
|
|
250
|
+
ctx.blackboard["prev_deposit_cargo"] = current_cargo
|
|
251
|
+
|
|
252
|
+
# Find nearest cogs depot
|
|
253
|
+
depot_pos = _find_cogs_depot(ctx)
|
|
254
|
+
if depot_pos is None:
|
|
255
|
+
return ctx.navigator.explore(ctx.state.position, ctx.map)
|
|
256
|
+
|
|
257
|
+
if ctx.trace:
|
|
258
|
+
ctx.trace.nav_target = depot_pos
|
|
259
|
+
|
|
260
|
+
dist = _manhattan(ctx.state.position, depot_pos)
|
|
261
|
+
if dist <= 1:
|
|
262
|
+
if ctx.trace:
|
|
263
|
+
hub_dbg_filter = {"collective_id": ctx.my_collective_id} if ctx.my_collective_id is not None else None
|
|
264
|
+
hubs = ctx.map.find(type_contains="hub", property_filter=hub_dbg_filter)
|
|
265
|
+
depot_entity = ctx.map.entities.get(depot_pos)
|
|
266
|
+
print(
|
|
267
|
+
f"[deposit-debug] agent={ctx.agent_id} t={ctx.step} pos={ctx.state.position}"
|
|
268
|
+
f" depot={depot_pos} depot_type={depot_entity.type if depot_entity else 'NONE'}"
|
|
269
|
+
f" depot_align={depot_entity.properties.get('alignment') if depot_entity else 'N/A'}"
|
|
270
|
+
f" cargo={current_cargo} prev={prev_cargo}"
|
|
271
|
+
f" hubs={[(p, e.properties.get('alignment')) for p, e in hubs]}"
|
|
272
|
+
)
|
|
273
|
+
# Adjacent to depot - track attempts
|
|
274
|
+
attempts_key = f"deposit_attempts_{depot_pos}"
|
|
275
|
+
attempts = ctx.blackboard.get(attempts_key, 0) + 1
|
|
276
|
+
|
|
277
|
+
# Reset if cargo decreased (deposit succeeded)
|
|
278
|
+
if current_cargo < prev_cargo:
|
|
279
|
+
ctx.blackboard[attempts_key] = 0
|
|
280
|
+
else:
|
|
281
|
+
ctx.blackboard[attempts_key] = attempts
|
|
282
|
+
|
|
283
|
+
if attempts > self.MAX_ATTEMPTS_PER_DEPOT:
|
|
284
|
+
# Mark as failed temporarily
|
|
285
|
+
ctx.blackboard[f"deposit_failed_{depot_pos}"] = ctx.step
|
|
286
|
+
ctx.blackboard[attempts_key] = 0
|
|
287
|
+
if ctx.trace:
|
|
288
|
+
ctx.trace.activate(self.name, f"giving up on {depot_pos}")
|
|
289
|
+
return ctx.navigator.explore(ctx.state.position, ctx.map)
|
|
290
|
+
|
|
291
|
+
return _move_toward(ctx.state.position, depot_pos)
|
|
292
|
+
|
|
293
|
+
# Not adjacent - reset attempts
|
|
294
|
+
ctx.blackboard[f"deposit_attempts_{depot_pos}"] = 0
|
|
295
|
+
return ctx.navigator.get_action(ctx.state.position, depot_pos, ctx.map, reach_adjacent=True)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class MineResourceGoal(Goal):
|
|
299
|
+
"""Navigate to extractor for target_resource and bump it.
|
|
300
|
+
|
|
301
|
+
Tracks attempts at each extractor and marks them as failed if
|
|
302
|
+
cargo doesn't increase after several bumps (extractor empty/broken).
|
|
303
|
+
|
|
304
|
+
NEVER satisfied - miners should always mine or explore, never noop.
|
|
305
|
+
"""
|
|
306
|
+
|
|
307
|
+
name = "MineResource"
|
|
308
|
+
MAX_ATTEMPTS_PER_EXTRACTOR = 3 # Reduced from 5 - fail faster
|
|
309
|
+
|
|
310
|
+
def is_satisfied(self, ctx: CogasContext) -> bool:
|
|
311
|
+
# Never satisfied - always mine or explore to avoid noops
|
|
312
|
+
# Even when collective is well-stocked, keep contributing
|
|
313
|
+
return False
|
|
314
|
+
|
|
315
|
+
def execute(self, ctx: CogasContext) -> Optional[Action]:
|
|
316
|
+
target_resource = ctx.blackboard.get("target_resource", "carbon")
|
|
317
|
+
|
|
318
|
+
# Track cargo to detect successful mining
|
|
319
|
+
prev_cargo = ctx.blackboard.get("prev_cargo", 0)
|
|
320
|
+
current_cargo = ctx.state.cargo_total
|
|
321
|
+
ctx.blackboard["prev_cargo"] = current_cargo
|
|
322
|
+
|
|
323
|
+
# Find nearest usable extractor for this resource
|
|
324
|
+
target_pos = self._find_extractor(ctx, target_resource)
|
|
325
|
+
|
|
326
|
+
if target_pos is None:
|
|
327
|
+
# Try any resource type
|
|
328
|
+
for resource in RESOURCE_TYPES:
|
|
329
|
+
if resource == target_resource:
|
|
330
|
+
continue
|
|
331
|
+
target_pos = self._find_extractor(ctx, resource)
|
|
332
|
+
if target_pos:
|
|
333
|
+
ctx.blackboard["target_resource"] = resource
|
|
334
|
+
ctx.blackboard["_target_resource_step"] = ctx.step
|
|
335
|
+
break
|
|
336
|
+
|
|
337
|
+
if target_pos is None:
|
|
338
|
+
# No extractors found — explore in agent-specific direction to discover them
|
|
339
|
+
ctx.blackboard.pop("target_resource", None)
|
|
340
|
+
directions = ["north", "east", "south", "west"]
|
|
341
|
+
return ctx.navigator.explore(
|
|
342
|
+
ctx.state.position,
|
|
343
|
+
ctx.map,
|
|
344
|
+
direction_bias=directions[ctx.agent_id % 4],
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
if ctx.trace:
|
|
348
|
+
ctx.trace.nav_target = target_pos
|
|
349
|
+
|
|
350
|
+
dist = _manhattan(ctx.state.position, target_pos)
|
|
351
|
+
if dist <= 1:
|
|
352
|
+
# Adjacent to extractor — track attempts
|
|
353
|
+
attempts_key = f"mine_attempts_{target_pos}"
|
|
354
|
+
attempts = ctx.blackboard.get(attempts_key, 0) + 1
|
|
355
|
+
|
|
356
|
+
# Reset attempts if cargo increased (mining succeeded)
|
|
357
|
+
if current_cargo > prev_cargo:
|
|
358
|
+
ctx.blackboard[attempts_key] = 0
|
|
359
|
+
else:
|
|
360
|
+
ctx.blackboard[attempts_key] = attempts
|
|
361
|
+
|
|
362
|
+
if attempts > self.MAX_ATTEMPTS_PER_EXTRACTOR:
|
|
363
|
+
# Mark as failed permanently for this episode
|
|
364
|
+
ctx.blackboard[f"mine_failed_{target_pos}"] = ctx.step
|
|
365
|
+
ctx.blackboard[attempts_key] = 0
|
|
366
|
+
# Also clear target resource to force re-evaluation
|
|
367
|
+
ctx.blackboard.pop("target_resource", None)
|
|
368
|
+
if ctx.trace:
|
|
369
|
+
ctx.trace.activate(self.name, f"giving up on {target_pos}")
|
|
370
|
+
return ctx.navigator.explore(
|
|
371
|
+
ctx.state.position,
|
|
372
|
+
ctx.map,
|
|
373
|
+
direction_bias=["north", "east", "south", "west"][ctx.agent_id % 4],
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
return _move_toward(ctx.state.position, target_pos)
|
|
377
|
+
|
|
378
|
+
# Don't reset attempts when moving away - only reset on successful mine
|
|
379
|
+
return ctx.navigator.get_action(ctx.state.position, target_pos, ctx.map, reach_adjacent=True)
|
|
380
|
+
|
|
381
|
+
def _find_extractor(self, ctx: CogasContext, resource: str) -> Optional[tuple[int, int]]:
|
|
382
|
+
"""Find nearest usable extractor."""
|
|
383
|
+
extractors = ctx.map.find(type=f"{resource}_extractor")
|
|
384
|
+
usable = [
|
|
385
|
+
(pos, e)
|
|
386
|
+
for pos, e in extractors
|
|
387
|
+
if e.properties.get("remaining_uses", 999) > 0
|
|
388
|
+
and e.properties.get("inventory_amount", -1) != 0
|
|
389
|
+
and not _extractor_recently_failed(ctx, pos)
|
|
390
|
+
]
|
|
391
|
+
|
|
392
|
+
if not usable:
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
# Sort by distance to agent
|
|
396
|
+
usable.sort(key=lambda x: _manhattan(ctx.state.position, x[0]))
|
|
397
|
+
return usable[0][0]
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _find_cogs_depot(ctx: CogasContext) -> tuple[int, int] | None:
|
|
401
|
+
"""Find nearest cogs-aligned depot, prioritizing hub."""
|
|
402
|
+
from cogames_agents.policy.scripted_agent.cogas.policy import SPAWN_POS
|
|
403
|
+
|
|
404
|
+
pos = ctx.state.position
|
|
405
|
+
|
|
406
|
+
def recently_failed(p: tuple[int, int]) -> bool:
|
|
407
|
+
failed_step = ctx.blackboard.get(f"deposit_failed_{p}", -9999)
|
|
408
|
+
return ctx.step - failed_step < 100
|
|
409
|
+
|
|
410
|
+
# Prioritize own team's hub
|
|
411
|
+
hub_filter = {"collective_id": ctx.my_collective_id} if ctx.my_collective_id is not None else None
|
|
412
|
+
for apos, _ in ctx.map.find(type_contains="hub", property_filter=hub_filter):
|
|
413
|
+
if not recently_failed(apos):
|
|
414
|
+
return apos
|
|
415
|
+
|
|
416
|
+
# Fallback: nearest cogs junction near hub
|
|
417
|
+
candidates: list[tuple[int, tuple[int, int]]] = []
|
|
418
|
+
for jpos, _ in ctx.map.find(type_contains="junction", property_filter={"alignment": "cogs"}):
|
|
419
|
+
if not recently_failed(jpos) and _manhattan(jpos, SPAWN_POS) <= 15:
|
|
420
|
+
candidates.append((_manhattan(pos, jpos), jpos))
|
|
421
|
+
|
|
422
|
+
if not candidates:
|
|
423
|
+
# Last resort: navigate to hub area
|
|
424
|
+
return SPAWN_POS
|
|
425
|
+
candidates.sort()
|
|
426
|
+
return candidates[0][1]
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _move_toward(current: tuple[int, int], target: tuple[int, int]) -> Action:
|
|
430
|
+
dr = target[0] - current[0]
|
|
431
|
+
dc = target[1] - current[1]
|
|
432
|
+
if abs(dr) >= abs(dc):
|
|
433
|
+
if dr > 0:
|
|
434
|
+
return Action(name="move_south")
|
|
435
|
+
elif dr < 0:
|
|
436
|
+
return Action(name="move_north")
|
|
437
|
+
if dc > 0:
|
|
438
|
+
return Action(name="move_east")
|
|
439
|
+
elif dc < 0:
|
|
440
|
+
return Action(name="move_west")
|
|
441
|
+
return Action(name="move_north")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Scout goals — explore the map."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from cogames_agents.policy.scripted_agent.cogas.goal import Goal
|
|
8
|
+
from mettagrid.simulator import Action
|
|
9
|
+
|
|
10
|
+
from .gear import GetGearGoal
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from cogames_agents.policy.scripted_agent.cogas.context import CogasContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GetScoutGearGoal(GetGearGoal):
|
|
17
|
+
"""Get scout gear (costs C1 O1 G1 S3 from collective)."""
|
|
18
|
+
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
super().__init__(
|
|
21
|
+
gear_attr="scout_gear",
|
|
22
|
+
station_type="scout_station",
|
|
23
|
+
goal_name="GetScoutGear",
|
|
24
|
+
gear_cost={"carbon": 1, "oxygen": 1, "germanium": 1, "silicon": 3},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ExploreGoal(Goal):
|
|
29
|
+
"""Explore the map by navigating to frontier cells."""
|
|
30
|
+
|
|
31
|
+
name = "Explore"
|
|
32
|
+
|
|
33
|
+
def is_satisfied(self, ctx: CogasContext) -> bool:
|
|
34
|
+
# Never satisfied — always explore
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
def execute(self, ctx: CogasContext) -> Action:
|
|
38
|
+
directions = ["north", "east", "south", "west"]
|
|
39
|
+
direction_bias = directions[ctx.agent_id % 4]
|
|
40
|
+
return ctx.navigator.explore(ctx.state.position, ctx.map, direction_bias=direction_bias)
|