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,333 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aligner role for CoGsGuard.
|
|
3
|
+
|
|
4
|
+
Aligners find supply depots and align them to the cogs commons to take control.
|
|
5
|
+
With aligner gear, they get +20 influence capacity.
|
|
6
|
+
|
|
7
|
+
Strategy:
|
|
8
|
+
- Find ALL junctions on the map
|
|
9
|
+
- Prioritize aligning neutral and enemy (clips) junctions
|
|
10
|
+
- Systematically work through all junctions to take them over
|
|
11
|
+
- Check energy before moving to targets
|
|
12
|
+
- Retry failed align actions up to MAX_RETRIES times
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
from cogames_agents.policy.scripted_agent.utils import is_adjacent
|
|
20
|
+
from mettagrid.simulator import Action
|
|
21
|
+
|
|
22
|
+
from .policy import DEBUG, CogsguardAgentPolicyImpl
|
|
23
|
+
from .types import CogsguardAgentState, Role, StructureType
|
|
24
|
+
|
|
25
|
+
# Maximum number of times to retry a failed align action
|
|
26
|
+
MAX_RETRIES = 3
|
|
27
|
+
# HP buffer to start returning to the hub before gear is lost.
|
|
28
|
+
HP_RETURN_BUFFER = 12
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AlignerAgentPolicyImpl(CogsguardAgentPolicyImpl):
|
|
32
|
+
"""Aligner agent: align ALL supply depots to cogs."""
|
|
33
|
+
|
|
34
|
+
ROLE = Role.ALIGNER
|
|
35
|
+
|
|
36
|
+
def execute_role(self, s: CogsguardAgentState) -> Action:
|
|
37
|
+
"""Execute aligner behavior: find and align ALL supply depots.
|
|
38
|
+
|
|
39
|
+
Energy-aware behavior:
|
|
40
|
+
- Check if we have enough energy before attempting to move to targets
|
|
41
|
+
- If energy is low, go recharge at the nexus
|
|
42
|
+
- Retry failed align actions up to MAX_RETRIES times
|
|
43
|
+
- Require gear, heart, and influence before attempting to align
|
|
44
|
+
- If gear acquisition fails repeatedly, get hearts first
|
|
45
|
+
"""
|
|
46
|
+
if DEBUG and s.step_count % 50 == 0:
|
|
47
|
+
num_junctions = len(s.get_structures_by_type(StructureType.CHARGER))
|
|
48
|
+
num_worked = len(s.worked_junctions)
|
|
49
|
+
print(
|
|
50
|
+
f"[A{s.agent_id}] ALIGNER: step={s.step_count} influence={s.influence} "
|
|
51
|
+
f"heart={s.heart} energy={s.energy} gear={s.aligner} "
|
|
52
|
+
f"junctions_known={num_junctions} worked={num_worked}"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
hub_pos = s.get_structure_position(StructureType.HUB)
|
|
56
|
+
if hub_pos is not None:
|
|
57
|
+
dist_to_hub = abs(hub_pos[0] - s.row) + abs(hub_pos[1] - s.col)
|
|
58
|
+
if s.hp <= dist_to_hub + HP_RETURN_BUFFER:
|
|
59
|
+
if DEBUG and s.step_count % 10 == 0:
|
|
60
|
+
print(f"[A{s.agent_id}] ALIGNER: Low HP ({s.hp}), returning to hub")
|
|
61
|
+
return self._do_recharge(s)
|
|
62
|
+
|
|
63
|
+
# === Resource check: need gear, heart, and influence to align ===
|
|
64
|
+
has_gear = s.aligner >= 1
|
|
65
|
+
has_heart = s.heart >= 1
|
|
66
|
+
has_influence = s.influence >= 1
|
|
67
|
+
|
|
68
|
+
# If we don't have gear, try to get it
|
|
69
|
+
if not has_gear:
|
|
70
|
+
return self._handle_no_gear(s)
|
|
71
|
+
|
|
72
|
+
# If we have gear but are missing resources, go get them
|
|
73
|
+
if not has_heart or not has_influence:
|
|
74
|
+
if DEBUG and s.step_count % 10 == 0:
|
|
75
|
+
print(
|
|
76
|
+
f"[A{s.agent_id}] ALIGNER: Have gear but missing resources "
|
|
77
|
+
f"(heart={has_heart}, influence={has_influence}), getting them first"
|
|
78
|
+
)
|
|
79
|
+
return self._get_resources(s, need_influence=not has_influence, need_heart=not has_heart)
|
|
80
|
+
|
|
81
|
+
# Check if last action succeeded (for retry logic)
|
|
82
|
+
# Actions can fail due to insufficient energy - agents auto-regen so just retry
|
|
83
|
+
if s._pending_action_type == "align":
|
|
84
|
+
target = s._pending_action_target
|
|
85
|
+
if s.check_action_success():
|
|
86
|
+
if DEBUG:
|
|
87
|
+
print(f"[A{s.agent_id}] ALIGNER: Previous align succeeded!")
|
|
88
|
+
if target is not None and self._smart_role_coordinator is not None:
|
|
89
|
+
hub_pos = s.stations.get("hub")
|
|
90
|
+
self._smart_role_coordinator.register_junction_alignment(
|
|
91
|
+
target,
|
|
92
|
+
"cogs",
|
|
93
|
+
hub_pos,
|
|
94
|
+
s.step_count,
|
|
95
|
+
)
|
|
96
|
+
elif s.should_retry_action(MAX_RETRIES):
|
|
97
|
+
retry_count = s.increment_retry()
|
|
98
|
+
if DEBUG:
|
|
99
|
+
print(
|
|
100
|
+
f"[A{s.agent_id}] ALIGNER: Align failed, retrying ({retry_count}/{MAX_RETRIES}) "
|
|
101
|
+
f"at {s._pending_action_target}"
|
|
102
|
+
)
|
|
103
|
+
# Retry the same action - agent will have auto-regenerated some energy
|
|
104
|
+
if s._pending_action_target and is_adjacent((s.row, s.col), s._pending_action_target):
|
|
105
|
+
return self._use_object_at(s, s._pending_action_target)
|
|
106
|
+
else:
|
|
107
|
+
if DEBUG:
|
|
108
|
+
print(f"[A{s.agent_id}] ALIGNER: Align failed after {MAX_RETRIES} retries, moving on")
|
|
109
|
+
if target is not None and target == s._pending_alignment_target:
|
|
110
|
+
s._pending_alignment_target = None
|
|
111
|
+
s.clear_pending_action()
|
|
112
|
+
|
|
113
|
+
# Find the best depot to align (prioritize closest non-cogs junction)
|
|
114
|
+
target_depot = None
|
|
115
|
+
pending_target = s._pending_alignment_target
|
|
116
|
+
if pending_target is not None:
|
|
117
|
+
pending_struct = s.get_structure_at(pending_target)
|
|
118
|
+
if pending_struct is None or pending_struct.structure_type == StructureType.CHARGER:
|
|
119
|
+
if pending_struct is None or pending_struct.alignment in (None, "neutral"):
|
|
120
|
+
target_depot = pending_target
|
|
121
|
+
elif pending_struct.alignment == "cogs":
|
|
122
|
+
s._pending_alignment_target = None
|
|
123
|
+
|
|
124
|
+
if target_depot is None:
|
|
125
|
+
target_depot = self._find_best_target(s)
|
|
126
|
+
|
|
127
|
+
if target_depot is None:
|
|
128
|
+
if DEBUG and s.step_count % 50 == 0:
|
|
129
|
+
print(f"[A{s.agent_id}] ALIGNER: No targets, exploring for junctions")
|
|
130
|
+
return self._explore_for_junctions(s)
|
|
131
|
+
|
|
132
|
+
# Navigate to depot
|
|
133
|
+
# Note: moves require energy. If move fails due to low energy,
|
|
134
|
+
# action failure detection will catch it and we'll retry next step
|
|
135
|
+
# (agents auto-regen energy every step, and regen full near aligned buildings)
|
|
136
|
+
if not is_adjacent((s.row, s.col), target_depot):
|
|
137
|
+
if DEBUG and s.step_count % 20 == 0:
|
|
138
|
+
print(f"[A{s.agent_id}] ALIGNER: Moving to junction at {target_depot}")
|
|
139
|
+
return self._move_towards(s, target_depot, reach_adjacent=True)
|
|
140
|
+
|
|
141
|
+
# Align the depot by bumping it
|
|
142
|
+
# Mark this junction as worked for a while (align multiple times then move on)
|
|
143
|
+
last_worked = s.worked_junctions.get(target_depot, 0)
|
|
144
|
+
times_worked = s.step_count - last_worked if last_worked > 0 else 0
|
|
145
|
+
s.worked_junctions[target_depot] = s.step_count
|
|
146
|
+
|
|
147
|
+
# Start tracking this align attempt
|
|
148
|
+
s.start_action_attempt("align", target_depot)
|
|
149
|
+
|
|
150
|
+
if DEBUG and times_worked < 5:
|
|
151
|
+
print(f"[A{s.agent_id}] ALIGNER: ALIGNING junction at {target_depot} (energy={s.energy}, heart={s.heart})!")
|
|
152
|
+
return self._use_object_at(s, target_depot)
|
|
153
|
+
|
|
154
|
+
def _handle_no_gear(self, s: CogsguardAgentState) -> Action:
|
|
155
|
+
"""Handle behavior when aligner doesn't have gear.
|
|
156
|
+
|
|
157
|
+
Strategy: Go to gear station and wait there until gear is available.
|
|
158
|
+
Can't do much without gear, so just wait.
|
|
159
|
+
"""
|
|
160
|
+
station_pos = s.get_structure_position(StructureType.ALIGNER_STATION)
|
|
161
|
+
|
|
162
|
+
# If we don't know where the station is, explore to find it
|
|
163
|
+
if station_pos is None:
|
|
164
|
+
if DEBUG:
|
|
165
|
+
print(f"[A{s.agent_id}] ALIGNER_NO_GEAR: Station unknown, exploring")
|
|
166
|
+
return self._explore(s)
|
|
167
|
+
|
|
168
|
+
# Go to gear station
|
|
169
|
+
if not is_adjacent((s.row, s.col), station_pos):
|
|
170
|
+
if DEBUG and s.step_count % 10 == 0:
|
|
171
|
+
print(f"[A{s.agent_id}] ALIGNER_NO_GEAR: Moving to station at {station_pos}")
|
|
172
|
+
return self._move_towards(s, station_pos, reach_adjacent=True)
|
|
173
|
+
|
|
174
|
+
# At station - keep trying to get gear
|
|
175
|
+
if DEBUG and s.step_count % 10 == 0:
|
|
176
|
+
print(f"[A{s.agent_id}] ALIGNER_NO_GEAR: At station, waiting for gear")
|
|
177
|
+
return self._use_object_at(s, station_pos)
|
|
178
|
+
|
|
179
|
+
def _get_resources(self, s: CogsguardAgentState, need_influence: bool, need_heart: bool) -> Action:
|
|
180
|
+
"""Get hearts from the chest (primary source).
|
|
181
|
+
|
|
182
|
+
The chest can produce hearts from resources:
|
|
183
|
+
1. First tries to withdraw existing hearts from cogs commons (get_heart handler)
|
|
184
|
+
2. If no hearts available, converts 1 of each element into 1 heart (make_heart handler)
|
|
185
|
+
|
|
186
|
+
So as long as miners deposit resources, aligners can get hearts.
|
|
187
|
+
If we've been trying to get hearts for too long, go explore instead.
|
|
188
|
+
"""
|
|
189
|
+
if need_heart:
|
|
190
|
+
# If we've waited more than 40 steps for hearts, go explore instead
|
|
191
|
+
if s._heart_wait_start == 0:
|
|
192
|
+
s._heart_wait_start = s.step_count
|
|
193
|
+
if s.step_count - s._heart_wait_start > 40:
|
|
194
|
+
if DEBUG:
|
|
195
|
+
print(f"[A{s.agent_id}] ALIGNER: Waited 40+ steps for hearts, exploring instead")
|
|
196
|
+
s._heart_wait_start = 0
|
|
197
|
+
return self._explore_for_junctions(s)
|
|
198
|
+
|
|
199
|
+
# Try chest first - it's the primary heart source
|
|
200
|
+
chest_pos = s.get_structure_position(StructureType.CHEST)
|
|
201
|
+
if chest_pos is not None:
|
|
202
|
+
if DEBUG and s.step_count % 10 == 0:
|
|
203
|
+
adj = is_adjacent((s.row, s.col), chest_pos)
|
|
204
|
+
print(f"[A{s.agent_id}] ALIGNER: Getting hearts from chest at {chest_pos}, adjacent={adj}")
|
|
205
|
+
if not is_adjacent((s.row, s.col), chest_pos):
|
|
206
|
+
return self._move_towards(s, chest_pos, reach_adjacent=True)
|
|
207
|
+
return self._use_object_at(s, chest_pos)
|
|
208
|
+
|
|
209
|
+
# Try hub as fallback (may have heart AOE or deposit function)
|
|
210
|
+
hub_pos = s.get_structure_position(StructureType.HUB)
|
|
211
|
+
if hub_pos is not None:
|
|
212
|
+
if DEBUG:
|
|
213
|
+
print(f"[A{s.agent_id}] ALIGNER: No chest found, trying hub at {hub_pos}")
|
|
214
|
+
if not is_adjacent((s.row, s.col), hub_pos):
|
|
215
|
+
return self._move_towards(s, hub_pos, reach_adjacent=True)
|
|
216
|
+
return self._use_object_at(s, hub_pos)
|
|
217
|
+
|
|
218
|
+
# Neither found - explore to find them
|
|
219
|
+
if DEBUG:
|
|
220
|
+
print(f"[A{s.agent_id}] ALIGNER: No chest/hub found, exploring")
|
|
221
|
+
s._heart_wait_start = 0
|
|
222
|
+
return self._explore(s)
|
|
223
|
+
|
|
224
|
+
# Just need influence - wait for AOE regeneration near hub
|
|
225
|
+
hub_pos = s.get_structure_position(StructureType.HUB)
|
|
226
|
+
if hub_pos is None:
|
|
227
|
+
return self._explore(s)
|
|
228
|
+
if not is_adjacent((s.row, s.col), hub_pos):
|
|
229
|
+
return self._move_towards(s, hub_pos, reach_adjacent=True)
|
|
230
|
+
return self._noop()
|
|
231
|
+
|
|
232
|
+
def _find_best_target(self, s: CogsguardAgentState) -> Optional[tuple[int, int]]:
|
|
233
|
+
"""Find the closest un-aligned junction to align.
|
|
234
|
+
|
|
235
|
+
Prioritizes by distance - aligns the closest junction that isn't already cogs-aligned.
|
|
236
|
+
Skips junctions that were recently worked on to ensure we visit multiple junctions.
|
|
237
|
+
"""
|
|
238
|
+
# Get all known junctions from structures map
|
|
239
|
+
junctions = s.get_structures_by_type(StructureType.CHARGER)
|
|
240
|
+
|
|
241
|
+
# How long to ignore a junction after working on it (steps)
|
|
242
|
+
cooldown = 50
|
|
243
|
+
|
|
244
|
+
recent_candidates: list[tuple[int, tuple[int, int]]] = []
|
|
245
|
+
if self._smart_role_coordinator is not None:
|
|
246
|
+
hub_pos = s.stations.get("hub")
|
|
247
|
+
recent_targets = self._smart_role_coordinator.recent_scramble_targets(hub_pos, s.step_count)
|
|
248
|
+
for pos in recent_targets:
|
|
249
|
+
last_worked = s.worked_junctions.get(pos, 0)
|
|
250
|
+
if last_worked > 0 and s.step_count - last_worked < cooldown:
|
|
251
|
+
continue
|
|
252
|
+
junction = s.get_structure_at(pos)
|
|
253
|
+
if junction is not None and junction.alignment in ("cogs", "clips"):
|
|
254
|
+
continue
|
|
255
|
+
dist = abs(pos[0] - s.row) + abs(pos[1] - s.col)
|
|
256
|
+
recent_candidates.append((dist, pos))
|
|
257
|
+
|
|
258
|
+
if recent_candidates:
|
|
259
|
+
recent_candidates.sort()
|
|
260
|
+
target_idx = 0
|
|
261
|
+
if self._smart_role_coordinator is not None:
|
|
262
|
+
aligner_ids = sorted(
|
|
263
|
+
agent_id
|
|
264
|
+
for agent_id, snapshot in self._smart_role_coordinator.agent_snapshots.items()
|
|
265
|
+
if snapshot.role == Role.ALIGNER
|
|
266
|
+
)
|
|
267
|
+
if aligner_ids:
|
|
268
|
+
target_idx = aligner_ids.index(s.agent_id) if s.agent_id in aligner_ids else 0
|
|
269
|
+
return recent_candidates[target_idx % len(recent_candidates)][1]
|
|
270
|
+
|
|
271
|
+
# Collect all un-aligned junctions (not cogs) and sort by distance
|
|
272
|
+
unaligned_junctions: list[tuple[int, tuple[int, int]]] = []
|
|
273
|
+
|
|
274
|
+
for junction in junctions:
|
|
275
|
+
pos = junction.position
|
|
276
|
+
dist = abs(pos[0] - s.row) + abs(pos[1] - s.col)
|
|
277
|
+
|
|
278
|
+
# Skip recently worked junctions
|
|
279
|
+
last_worked = s.worked_junctions.get(pos, 0)
|
|
280
|
+
if last_worked > 0 and s.step_count - last_worked < cooldown:
|
|
281
|
+
continue
|
|
282
|
+
|
|
283
|
+
# Skip already cogs-aligned junctions
|
|
284
|
+
if junction.alignment is not None:
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
# Add neutral junctions only
|
|
288
|
+
unaligned_junctions.append((dist, pos))
|
|
289
|
+
|
|
290
|
+
# Sort by distance and return closest
|
|
291
|
+
if unaligned_junctions:
|
|
292
|
+
unaligned_junctions.sort()
|
|
293
|
+
if DEBUG and s.step_count % 20 == 0:
|
|
294
|
+
count = len(unaligned_junctions)
|
|
295
|
+
closest = unaligned_junctions[0][1]
|
|
296
|
+
print(f"[A{s.agent_id}] ALIGNER: Found {count} un-aligned junctions, closest at {closest}")
|
|
297
|
+
target_idx = 0
|
|
298
|
+
if self._smart_role_coordinator is not None:
|
|
299
|
+
aligner_ids = sorted(
|
|
300
|
+
agent_id
|
|
301
|
+
for agent_id, snapshot in self._smart_role_coordinator.agent_snapshots.items()
|
|
302
|
+
if snapshot.role == Role.ALIGNER
|
|
303
|
+
)
|
|
304
|
+
if aligner_ids:
|
|
305
|
+
target_idx = aligner_ids.index(s.agent_id) if s.agent_id in aligner_ids else 0
|
|
306
|
+
return unaligned_junctions[target_idx % len(unaligned_junctions)][1]
|
|
307
|
+
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
def _explore_for_junctions(self, s: CogsguardAgentState) -> Action:
|
|
311
|
+
"""Explore aggressively to find more junctions spread around the map."""
|
|
312
|
+
frontier_action = self._explore_frontier(s)
|
|
313
|
+
if frontier_action is not None:
|
|
314
|
+
return frontier_action
|
|
315
|
+
|
|
316
|
+
# Move in a direction based on agent ID and step count to spread out
|
|
317
|
+
directions = ["north", "south", "east", "west"]
|
|
318
|
+
# Cycle through directions, spending 20 steps in each direction
|
|
319
|
+
dir_idx = (s.agent_id + s.step_count // 20) % 4
|
|
320
|
+
direction = directions[dir_idx]
|
|
321
|
+
|
|
322
|
+
dr, dc = self._move_deltas[direction]
|
|
323
|
+
next_r, next_c = s.row + dr, s.col + dc
|
|
324
|
+
|
|
325
|
+
# Check if we can move in that direction
|
|
326
|
+
from cogames_agents.policy.scripted_agent.pathfinding import is_traversable
|
|
327
|
+
from cogames_agents.policy.scripted_agent.types import CellType
|
|
328
|
+
|
|
329
|
+
if is_traversable(s, next_r, next_c, CellType): # type: ignore[arg-type]
|
|
330
|
+
return self._move(direction)
|
|
331
|
+
|
|
332
|
+
# Fall back to regular exploration if blocked
|
|
333
|
+
return self._explore(s)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Callable
|
|
4
|
+
|
|
5
|
+
from mettagrid.simulator import Action
|
|
6
|
+
|
|
7
|
+
from .policy import CogsguardMultiRoleImpl
|
|
8
|
+
from .types import CogsguardAgentState, Role
|
|
9
|
+
|
|
10
|
+
BehaviorHook = Callable[[CogsguardAgentState], Action]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def build_cogsguard_behavior_hooks(policy: CogsguardMultiRoleImpl) -> dict[str, BehaviorHook]:
|
|
14
|
+
"""Bind evolutionary behavior names to existing role implementations.
|
|
15
|
+
|
|
16
|
+
These hooks are not wired into the live policy yet, but they allow the
|
|
17
|
+
evolutionary coordinator to execute real behavior logic when integrated.
|
|
18
|
+
"""
|
|
19
|
+
miner = policy._get_role_impl(Role.MINER)
|
|
20
|
+
scout = policy._get_role_impl(Role.SCOUT)
|
|
21
|
+
aligner = policy._get_role_impl(Role.ALIGNER)
|
|
22
|
+
scrambler = policy._get_role_impl(Role.SCRAMBLER)
|
|
23
|
+
|
|
24
|
+
def _discover_with_scout(s: CogsguardAgentState) -> Action:
|
|
25
|
+
return scout.execute_role(s)
|
|
26
|
+
|
|
27
|
+
def _get_influence(s: CogsguardAgentState) -> Action:
|
|
28
|
+
return aligner._get_resources(s, need_influence=True, need_heart=False)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
"explore": policy._explore,
|
|
32
|
+
"recharge": policy._do_recharge,
|
|
33
|
+
"mine_resource": miner._do_gather,
|
|
34
|
+
"deposit_resource": miner._do_deposit,
|
|
35
|
+
"find_extractor": miner._do_gather,
|
|
36
|
+
"discover_stations": _discover_with_scout,
|
|
37
|
+
"discover_extractors": _discover_with_scout,
|
|
38
|
+
"discover_junctions": _discover_with_scout,
|
|
39
|
+
"get_hearts": scrambler._get_hearts,
|
|
40
|
+
"get_influence": _get_influence,
|
|
41
|
+
"align_junction": aligner.execute_role,
|
|
42
|
+
"scramble_junction": scrambler.execute_role,
|
|
43
|
+
"find_enemy_junction": scrambler.execute_role,
|
|
44
|
+
}
|