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,984 +0,0 @@
|
|
|
1
|
-
import
|
|
2
|
-
std/[algorithm, deques, options, random, sequtils, sets, strutils, tables],
|
|
3
|
-
common
|
|
4
|
-
|
|
5
|
-
type
|
|
6
|
-
Phase = enum gatherPhase, assemblePhase, deliverPhase, rechargePhase
|
|
7
|
-
ObservedObject = object
|
|
8
|
-
name: string
|
|
9
|
-
clipped: bool
|
|
10
|
-
cooldownRemaining, remainingUses: int
|
|
11
|
-
protocolInputs, protocolOutputs: Table[string, int]
|
|
12
|
-
agentGroup, agentFrozen: int
|
|
13
|
-
ExtractorInfo = object
|
|
14
|
-
position: Location
|
|
15
|
-
resource: string
|
|
16
|
-
lastSeen, cooldownRemaining, remainingUses: int
|
|
17
|
-
clipped: bool
|
|
18
|
-
LadybugState = object
|
|
19
|
-
row, col, mapHeight, mapWidth, obsHalfWidth, obsHalfHeight: int
|
|
20
|
-
phase: Phase
|
|
21
|
-
stepCount, waitSteps, lastAction: int
|
|
22
|
-
explorationDirectionSetStep, explorationEscapeUntilStep, stuckEscapeStep: int
|
|
23
|
-
positionHistory: seq[Location]
|
|
24
|
-
occupancy: seq[seq[int]]
|
|
25
|
-
agentOccupancy: HashSet[Location]
|
|
26
|
-
energy, carbon, oxygen, germanium, silicon, hearts: int
|
|
27
|
-
decoder, modulator, resonator, scrambler: int
|
|
28
|
-
heartRecipe: Table[string, int]
|
|
29
|
-
heartRecipeKnown: bool
|
|
30
|
-
stations: Table[string, Option[Location]]
|
|
31
|
-
extractors: Table[string, seq[ExtractorInfo]]
|
|
32
|
-
currentGlyph, targetResource, pendingUseResource, explorationDirection: string
|
|
33
|
-
pendingUseAmount: int
|
|
34
|
-
waitingAtExtractor: Option[Location]
|
|
35
|
-
usingObjectThisStep, stuckLoopDetected: bool
|
|
36
|
-
cachedPath: seq[(int, int)]
|
|
37
|
-
cachedPathTarget: Option[Location]
|
|
38
|
-
cachedPathReachAdjacent: bool
|
|
39
|
-
LadybugAgent* = ref object
|
|
40
|
-
agentId*: int
|
|
41
|
-
cfg*: Config
|
|
42
|
-
random*: Rand
|
|
43
|
-
state*: LadybugState
|
|
44
|
-
LadybugPolicy* = ref object
|
|
45
|
-
agents*: seq[LadybugAgent]
|
|
46
|
-
|
|
47
|
-
proc initHeartRecipeFromConfig(agent: LadybugAgent) {.raises: [].}
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
defaultMapSize = 200
|
|
51
|
-
rechargeThresholdLow = 35
|
|
52
|
-
rechargeThresholdHigh = 85
|
|
53
|
-
positionHistorySize = 40
|
|
54
|
-
explorationAreaCheckWindow = 35
|
|
55
|
-
explorationAreaSizeThreshold = 9
|
|
56
|
-
explorationEscapeDuration = 8
|
|
57
|
-
explorationDirectionPersistence = 18
|
|
58
|
-
explorationAssemblerDistanceThreshold = 12
|
|
59
|
-
cellFree = 1
|
|
60
|
-
cellObstacle = 2
|
|
61
|
-
parentSentinel = (-9999, -9999)
|
|
62
|
-
ResourceTypes = ["carbon", "oxygen", "germanium", "silicon"]
|
|
63
|
-
StationKeys = ["assembler", "chest", "charger"]
|
|
64
|
-
DirectionNames = ["north", "south", "east", "west"]
|
|
65
|
-
CardinalNeighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
|
66
|
-
DefaultHeartRecipe = [
|
|
67
|
-
("carbon", 2),
|
|
68
|
-
("oxygen", 2),
|
|
69
|
-
("germanium", 1),
|
|
70
|
-
("silicon", 3)
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
proc initState(agent: LadybugAgent) =
|
|
74
|
-
agent.state = LadybugState()
|
|
75
|
-
agent.state.mapHeight = defaultMapSize
|
|
76
|
-
agent.state.mapWidth = defaultMapSize
|
|
77
|
-
agent.state.obsHalfWidth = agent.cfg.config.obsWidth div 2
|
|
78
|
-
agent.state.obsHalfHeight = agent.cfg.config.obsHeight div 2
|
|
79
|
-
agent.state.row = defaultMapSize div 2
|
|
80
|
-
agent.state.col = defaultMapSize div 2
|
|
81
|
-
agent.state.phase = gatherPhase
|
|
82
|
-
agent.state.currentGlyph = "default"
|
|
83
|
-
agent.state.targetResource = ""
|
|
84
|
-
agent.state.pendingUseResource = ""
|
|
85
|
-
agent.state.pendingUseAmount = 0
|
|
86
|
-
agent.state.heartRecipe = initTable[string, int]()
|
|
87
|
-
agent.state.heartRecipeKnown = false
|
|
88
|
-
agent.initHeartRecipeFromConfig()
|
|
89
|
-
if not agent.state.heartRecipeKnown:
|
|
90
|
-
for (resource, amount) in DefaultHeartRecipe:
|
|
91
|
-
agent.state.heartRecipe[resource] = amount
|
|
92
|
-
agent.state.heartRecipeKnown = true
|
|
93
|
-
agent.state.stations = initTable[string, Option[Location]]()
|
|
94
|
-
for key in StationKeys:
|
|
95
|
-
agent.state.stations[key] = none(Location)
|
|
96
|
-
agent.state.extractors = initTable[string, seq[ExtractorInfo]]()
|
|
97
|
-
for resource in ResourceTypes:
|
|
98
|
-
agent.state.extractors[resource] = @[]
|
|
99
|
-
agent.state.agentOccupancy = initHashSet[Location]()
|
|
100
|
-
agent.state.occupancy = newSeqWith(
|
|
101
|
-
agent.state.mapHeight,
|
|
102
|
-
newSeqWith(agent.state.mapWidth, cellFree.int)
|
|
103
|
-
)
|
|
104
|
-
agent.state.lastAction = agent.cfg.actions.noop
|
|
105
|
-
agent.state.explorationDirection = ""
|
|
106
|
-
agent.state.explorationDirectionSetStep = 0
|
|
107
|
-
agent.state.explorationEscapeUntilStep = 0
|
|
108
|
-
agent.state.positionHistory = @[]
|
|
109
|
-
agent.state.waitSteps = 0
|
|
110
|
-
agent.state.waitingAtExtractor = none(Location)
|
|
111
|
-
agent.state.usingObjectThisStep = false
|
|
112
|
-
agent.state.stuckLoopDetected = false
|
|
113
|
-
agent.state.stuckEscapeStep = 0
|
|
114
|
-
agent.state.cachedPath = @[]
|
|
115
|
-
agent.state.cachedPathTarget = none(Location)
|
|
116
|
-
agent.state.cachedPathReachAdjacent = false
|
|
117
|
-
|
|
118
|
-
proc detectLoops(agent: LadybugAgent)
|
|
119
|
-
proc tryRandomDirection(agent: LadybugAgent): int
|
|
120
|
-
|
|
121
|
-
proc resourceVibe(resource: string): string =
|
|
122
|
-
## Map canonical resource names to the actual vibe glyphs used by the engine.
|
|
123
|
-
case resource.toLowerAscii()
|
|
124
|
-
of "carbon":
|
|
125
|
-
"carbon_a"
|
|
126
|
-
of "oxygen":
|
|
127
|
-
"oxygen_a"
|
|
128
|
-
of "germanium":
|
|
129
|
-
"germanium_a"
|
|
130
|
-
of "silicon":
|
|
131
|
-
"silicon_a"
|
|
132
|
-
else:
|
|
133
|
-
"carbon_a"
|
|
134
|
-
|
|
135
|
-
proc tagName(cfg: Config, tagId: int): string =
|
|
136
|
-
if tagId >= 0 and tagId < cfg.config.tags.len:
|
|
137
|
-
return cfg.config.tags[tagId]
|
|
138
|
-
""
|
|
139
|
-
|
|
140
|
-
proc maybeAssign(
|
|
141
|
-
table: var Table[string, int],
|
|
142
|
-
featureField: int,
|
|
143
|
-
key: string,
|
|
144
|
-
featureId: int,
|
|
145
|
-
value: int
|
|
146
|
-
) =
|
|
147
|
-
if featureField != 0 and featureId == featureField:
|
|
148
|
-
table[key] = value
|
|
149
|
-
|
|
150
|
-
proc clearCachedPath(agent: LadybugAgent) =
|
|
151
|
-
agent.state.cachedPath = @[]
|
|
152
|
-
agent.state.cachedPathTarget = none(Location)
|
|
153
|
-
agent.state.cachedPathReachAdjacent = false
|
|
154
|
-
|
|
155
|
-
proc stationFromName(lowerName: string): string =
|
|
156
|
-
if lowerName.contains("assembler"):
|
|
157
|
-
"assembler"
|
|
158
|
-
elif lowerName.contains("chest"):
|
|
159
|
-
"chest"
|
|
160
|
-
elif lowerName.contains("charger"):
|
|
161
|
-
"charger"
|
|
162
|
-
else:
|
|
163
|
-
""
|
|
164
|
-
|
|
165
|
-
proc initHeartRecipeFromConfig(agent: LadybugAgent) =
|
|
166
|
-
for protocol in agent.cfg.assemblerProtocols:
|
|
167
|
-
if protocol.outputResources.getOrDefault("heart", 0) > 0:
|
|
168
|
-
agent.state.heartRecipe = initTable[string, int]()
|
|
169
|
-
for resource, amount in protocol.inputResources.pairs:
|
|
170
|
-
if resource != "energy":
|
|
171
|
-
agent.state.heartRecipe[resource] = amount
|
|
172
|
-
agent.state.heartRecipeKnown = true
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
proc newLadybugAgent*(agentId: int, environmentConfig: string): LadybugAgent {.raises: [].} =
|
|
176
|
-
var config = parseConfig(environmentConfig)
|
|
177
|
-
result = LadybugAgent(agentId: agentId, cfg: config)
|
|
178
|
-
result.random = initRand(agentId)
|
|
179
|
-
initState(result)
|
|
180
|
-
|
|
181
|
-
proc reset*(agent: LadybugAgent) =
|
|
182
|
-
initState(agent)
|
|
183
|
-
|
|
184
|
-
proc decodeObservation(
|
|
185
|
-
agent: LadybugAgent,
|
|
186
|
-
numTokens: int,
|
|
187
|
-
sizeToken: int,
|
|
188
|
-
rawObservation: pointer
|
|
189
|
-
): Table[Location, seq[FeatureValue]] =
|
|
190
|
-
let observations = cast[ptr UncheckedArray[uint8]](rawObservation)
|
|
191
|
-
for token in 0 ..< numTokens:
|
|
192
|
-
let baseIdx = token * sizeToken
|
|
193
|
-
let locationPacked = observations[baseIdx]
|
|
194
|
-
let featureId = observations[baseIdx + 1]
|
|
195
|
-
let value = observations[baseIdx + 2]
|
|
196
|
-
if locationPacked == 255 and featureId == 255 and value == 255:
|
|
197
|
-
break
|
|
198
|
-
var location: Location
|
|
199
|
-
if locationPacked != 0xFF:
|
|
200
|
-
location.y = (locationPacked shr 4).int - agent.state.obsHalfHeight
|
|
201
|
-
location.x = (locationPacked and 0x0F).int - agent.state.obsHalfWidth
|
|
202
|
-
if location notin result:
|
|
203
|
-
result[location] = @[]
|
|
204
|
-
result[location].add(FeatureValue(featureId: featureId.int, value: value.int))
|
|
205
|
-
|
|
206
|
-
proc worldLocation(agent: LadybugAgent, local: Location): Option[Location] =
|
|
207
|
-
let worldRow = agent.state.row + local.y
|
|
208
|
-
let worldCol = agent.state.col + local.x
|
|
209
|
-
if worldRow < 0 or worldRow >= agent.state.mapHeight:
|
|
210
|
-
return none(Location)
|
|
211
|
-
if worldCol < 0 or worldCol >= agent.state.mapWidth:
|
|
212
|
-
return none(Location)
|
|
213
|
-
some(Location(x: worldCol, y: worldRow))
|
|
214
|
-
|
|
215
|
-
proc updateInventory(agent: LadybugAgent, visible: Table[Location, seq[FeatureValue]]) =
|
|
216
|
-
agent.state.energy = agent.cfg.getInventory(visible, agent.cfg.features.invEnergy)
|
|
217
|
-
agent.state.carbon = agent.cfg.getInventory(visible, agent.cfg.features.invCarbon)
|
|
218
|
-
agent.state.oxygen = agent.cfg.getInventory(visible, agent.cfg.features.invOxygen)
|
|
219
|
-
agent.state.germanium = agent.cfg.getInventory(visible, agent.cfg.features.invGermanium)
|
|
220
|
-
agent.state.silicon = agent.cfg.getInventory(visible, agent.cfg.features.invSilicon)
|
|
221
|
-
agent.state.hearts = agent.cfg.getInventory(visible, agent.cfg.features.invHeart)
|
|
222
|
-
agent.state.decoder = agent.cfg.getInventory(visible, agent.cfg.features.invDecoder)
|
|
223
|
-
agent.state.modulator = agent.cfg.getInventory(visible, agent.cfg.features.invModulator)
|
|
224
|
-
agent.state.resonator = agent.cfg.getInventory(visible, agent.cfg.features.invResonator)
|
|
225
|
-
agent.state.scrambler = agent.cfg.getInventory(visible, agent.cfg.features.invScrambler)
|
|
226
|
-
|
|
227
|
-
proc buildObservedObject(agent: LadybugAgent, features: seq[FeatureValue]): ObservedObject =
|
|
228
|
-
var tagIds: seq[int] = @[]
|
|
229
|
-
result.protocolInputs = initTable[string, int]()
|
|
230
|
-
result.protocolOutputs = initTable[string, int]()
|
|
231
|
-
for fv in features:
|
|
232
|
-
if fv.featureId == agent.cfg.features.tag:
|
|
233
|
-
tagIds.add(fv.value)
|
|
234
|
-
continue
|
|
235
|
-
elif fv.featureId == agent.cfg.features.cooldownRemaining:
|
|
236
|
-
result.cooldownRemaining = fv.value
|
|
237
|
-
elif fv.featureId == agent.cfg.features.clipped:
|
|
238
|
-
result.clipped = fv.value > 0
|
|
239
|
-
elif fv.featureId == agent.cfg.features.remainingUses:
|
|
240
|
-
result.remainingUses = fv.value
|
|
241
|
-
elif fv.featureId == agent.cfg.features.group:
|
|
242
|
-
result.agentGroup = fv.value
|
|
243
|
-
elif fv.featureId == agent.cfg.features.frozen:
|
|
244
|
-
result.agentFrozen = fv.value
|
|
245
|
-
else:
|
|
246
|
-
let fid = fv.featureId
|
|
247
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputEnergy, "energy", fid, fv.value)
|
|
248
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputCarbon, "carbon", fid, fv.value)
|
|
249
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputOxygen, "oxygen", fid, fv.value)
|
|
250
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputGermanium, "germanium", fid, fv.value)
|
|
251
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputSilicon, "silicon", fid, fv.value)
|
|
252
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputHeart, "heart", fid, fv.value)
|
|
253
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputDecoder, "decoder", fid, fv.value)
|
|
254
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputModulator, "modulator", fid, fv.value)
|
|
255
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputResonator, "resonator", fid, fv.value)
|
|
256
|
-
maybeAssign(result.protocolInputs, agent.cfg.features.protocolInputScrambler, "scrambler", fid, fv.value)
|
|
257
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputEnergy, "energy", fid, fv.value)
|
|
258
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputCarbon, "carbon", fid, fv.value)
|
|
259
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputOxygen, "oxygen", fid, fv.value)
|
|
260
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputGermanium, "germanium", fid, fv.value)
|
|
261
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputSilicon, "silicon", fid, fv.value)
|
|
262
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputHeart, "heart", fid, fv.value)
|
|
263
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputDecoder, "decoder", fid, fv.value)
|
|
264
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputModulator, "modulator", fid, fv.value)
|
|
265
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputResonator, "resonator", fid, fv.value)
|
|
266
|
-
maybeAssign(result.protocolOutputs, agent.cfg.features.protocolOutputScrambler, "scrambler", fid, fv.value)
|
|
267
|
-
|
|
268
|
-
if tagIds.len > 0:
|
|
269
|
-
result.name = tagName(agent.cfg, tagIds[0])
|
|
270
|
-
elif result.agentGroup != 0 or result.agentFrozen != 0:
|
|
271
|
-
result.name = "agent"
|
|
272
|
-
else:
|
|
273
|
-
result.name = ""
|
|
274
|
-
|
|
275
|
-
proc discoverStation(agent: LadybugAgent, key: string, loc: Location) =
|
|
276
|
-
if key notin agent.state.stations:
|
|
277
|
-
agent.state.stations[key] = some(loc)
|
|
278
|
-
return
|
|
279
|
-
if agent.state.stations[key].isNone:
|
|
280
|
-
agent.state.stations[key] = some(loc)
|
|
281
|
-
|
|
282
|
-
proc discoverExtractor(
|
|
283
|
-
agent: LadybugAgent,
|
|
284
|
-
resource: string,
|
|
285
|
-
loc: Location,
|
|
286
|
-
obj: ObservedObject
|
|
287
|
-
) =
|
|
288
|
-
var items = agent.state.extractors.getOrDefault(resource, @[])
|
|
289
|
-
var found = false
|
|
290
|
-
for i in 0 ..< items.len:
|
|
291
|
-
if items[i].position == loc:
|
|
292
|
-
items[i].lastSeen = agent.state.stepCount
|
|
293
|
-
items[i].cooldownRemaining = obj.cooldownRemaining
|
|
294
|
-
items[i].clipped = obj.clipped
|
|
295
|
-
if obj.remainingUses != 0:
|
|
296
|
-
items[i].remainingUses = obj.remainingUses
|
|
297
|
-
found = true
|
|
298
|
-
break
|
|
299
|
-
if not found:
|
|
300
|
-
var info = ExtractorInfo(
|
|
301
|
-
position: loc,
|
|
302
|
-
resource: resource,
|
|
303
|
-
lastSeen: agent.state.stepCount,
|
|
304
|
-
cooldownRemaining: obj.cooldownRemaining,
|
|
305
|
-
clipped: obj.clipped,
|
|
306
|
-
remainingUses: (if obj.remainingUses == 0: 999 else: obj.remainingUses)
|
|
307
|
-
)
|
|
308
|
-
items.add(info)
|
|
309
|
-
agent.state.extractors[resource] = items
|
|
310
|
-
|
|
311
|
-
proc discoverObjects(agent: LadybugAgent, visible: Table[Location, seq[FeatureValue]]) =
|
|
312
|
-
agent.state.agentOccupancy.clear()
|
|
313
|
-
for dy in -agent.state.obsHalfHeight .. agent.state.obsHalfHeight:
|
|
314
|
-
for dx in -agent.state.obsHalfWidth .. agent.state.obsHalfWidth:
|
|
315
|
-
let local = Location(x: dx, y: dy)
|
|
316
|
-
let world = agent.worldLocation(local)
|
|
317
|
-
if world.isSome:
|
|
318
|
-
agent.state.occupancy[world.get().y][world.get().x] = cellFree
|
|
319
|
-
|
|
320
|
-
for local, features in visible.pairs:
|
|
321
|
-
if local.x == 0 and local.y == 0:
|
|
322
|
-
continue
|
|
323
|
-
let worldOpt = agent.worldLocation(local)
|
|
324
|
-
if worldOpt.isNone:
|
|
325
|
-
continue
|
|
326
|
-
let world = worldOpt.get()
|
|
327
|
-
var obj = agent.buildObservedObject(features)
|
|
328
|
-
if obj.name.len == 0:
|
|
329
|
-
continue
|
|
330
|
-
let lowerName = obj.name.toLowerAscii()
|
|
331
|
-
|
|
332
|
-
if lowerName == "agent":
|
|
333
|
-
agent.state.agentOccupancy.incl(world)
|
|
334
|
-
continue
|
|
335
|
-
|
|
336
|
-
if lowerName.contains("wall"):
|
|
337
|
-
agent.state.occupancy[world.y][world.x] = cellObstacle
|
|
338
|
-
continue
|
|
339
|
-
|
|
340
|
-
let stationKey = stationFromName(lowerName)
|
|
341
|
-
if stationKey.len > 0:
|
|
342
|
-
agent.state.occupancy[world.y][world.x] = cellObstacle
|
|
343
|
-
discoverStation(agent, stationKey, world)
|
|
344
|
-
if stationKey == "assembler" and (not agent.state.heartRecipeKnown) and
|
|
345
|
-
obj.protocolOutputs.getOrDefault("heart", 0) > 0:
|
|
346
|
-
agent.state.heartRecipe = initTable[string, int]()
|
|
347
|
-
for resource, amount in obj.protocolInputs.pairs:
|
|
348
|
-
if resource != "energy":
|
|
349
|
-
agent.state.heartRecipe[resource] = amount
|
|
350
|
-
agent.state.heartRecipeKnown = true
|
|
351
|
-
continue
|
|
352
|
-
|
|
353
|
-
if lowerName.contains("extractor"):
|
|
354
|
-
agent.state.occupancy[world.y][world.x] = cellObstacle
|
|
355
|
-
var resource = lowerName
|
|
356
|
-
if resource.startsWith("clipped_"):
|
|
357
|
-
resource = resource[8 .. ^1]
|
|
358
|
-
if resource.endsWith("_extractor"):
|
|
359
|
-
resource = resource[0 ..< resource.len - "_extractor".len]
|
|
360
|
-
discoverExtractor(agent, resource, world, obj)
|
|
361
|
-
|
|
362
|
-
proc updatePosition(agent: LadybugAgent) =
|
|
363
|
-
if agent.state.usingObjectThisStep:
|
|
364
|
-
agent.state.usingObjectThisStep = false
|
|
365
|
-
return
|
|
366
|
-
if agent.state.lastAction == agent.cfg.actions.moveNorth:
|
|
367
|
-
dec agent.state.row
|
|
368
|
-
elif agent.state.lastAction == agent.cfg.actions.moveSouth:
|
|
369
|
-
inc agent.state.row
|
|
370
|
-
elif agent.state.lastAction == agent.cfg.actions.moveEast:
|
|
371
|
-
inc agent.state.col
|
|
372
|
-
elif agent.state.lastAction == agent.cfg.actions.moveWest:
|
|
373
|
-
dec agent.state.col
|
|
374
|
-
agent.state.positionHistory.add(Location(x: agent.state.col, y: agent.state.row))
|
|
375
|
-
if agent.state.positionHistory.len > positionHistorySize:
|
|
376
|
-
agent.state.positionHistory.delete(0)
|
|
377
|
-
detectLoops(agent)
|
|
378
|
-
|
|
379
|
-
proc detectLoops(agent: LadybugAgent) =
|
|
380
|
-
let hist = agent.state.positionHistory
|
|
381
|
-
let lenHist = hist.len
|
|
382
|
-
if lenHist >= 6:
|
|
383
|
-
let a1 = hist[lenHist - 1]
|
|
384
|
-
let a2 = hist[lenHist - 2]
|
|
385
|
-
let a3 = hist[lenHist - 3]
|
|
386
|
-
let a4 = hist[lenHist - 4]
|
|
387
|
-
let a5 = hist[lenHist - 5]
|
|
388
|
-
let a6 = hist[lenHist - 6]
|
|
389
|
-
if a1 == a3 and a3 == a5 and a2 == a4 and a4 == a6 and a1 != a2:
|
|
390
|
-
agent.state.stuckLoopDetected = true
|
|
391
|
-
agent.state.stuckEscapeStep = agent.state.stepCount
|
|
392
|
-
return
|
|
393
|
-
if lenHist >= 9:
|
|
394
|
-
let b1 = hist[lenHist - 1]
|
|
395
|
-
let b2 = hist[lenHist - 2]
|
|
396
|
-
let b3 = hist[lenHist - 3]
|
|
397
|
-
let b4 = hist[lenHist - 4]
|
|
398
|
-
let b5 = hist[lenHist - 5]
|
|
399
|
-
let b6 = hist[lenHist - 6]
|
|
400
|
-
let b7 = hist[lenHist - 7]
|
|
401
|
-
let b8 = hist[lenHist - 8]
|
|
402
|
-
let b9 = hist[lenHist - 9]
|
|
403
|
-
if b1 == b4 and b4 == b7 and b2 == b5 and b5 == b8 and b3 == b6 and b6 == b9:
|
|
404
|
-
agent.state.stuckLoopDetected = true
|
|
405
|
-
agent.state.stuckEscapeStep = agent.state.stepCount
|
|
406
|
-
|
|
407
|
-
proc clearWaiting(agent: LadybugAgent) =
|
|
408
|
-
agent.state.waitingAtExtractor = none(Location)
|
|
409
|
-
agent.state.pendingUseResource = ""
|
|
410
|
-
agent.state.pendingUseAmount = 0
|
|
411
|
-
agent.state.waitSteps = 0
|
|
412
|
-
|
|
413
|
-
proc getInventoryValue(agent: LadybugAgent, resource: string): int =
|
|
414
|
-
case resource
|
|
415
|
-
of "carbon": result = agent.state.carbon
|
|
416
|
-
of "oxygen": result = agent.state.oxygen
|
|
417
|
-
of "germanium": result = agent.state.germanium
|
|
418
|
-
of "silicon": result = agent.state.silicon
|
|
419
|
-
of "heart": result = agent.state.hearts
|
|
420
|
-
else: result = 0
|
|
421
|
-
|
|
422
|
-
proc findExtractor(
|
|
423
|
-
agent: LadybugAgent,
|
|
424
|
-
resource: string,
|
|
425
|
-
loc: Location
|
|
426
|
-
): Option[ExtractorInfo] =
|
|
427
|
-
if resource notin agent.state.extractors:
|
|
428
|
-
return none(ExtractorInfo)
|
|
429
|
-
for info in agent.state.extractors[resource]:
|
|
430
|
-
if info.position == loc:
|
|
431
|
-
return some(info)
|
|
432
|
-
none(ExtractorInfo)
|
|
433
|
-
|
|
434
|
-
proc handleWaiting(agent: LadybugAgent): Option[int] =
|
|
435
|
-
if agent.state.pendingUseResource.len == 0 or agent.state.waitingAtExtractor.isNone:
|
|
436
|
-
return none(int)
|
|
437
|
-
|
|
438
|
-
let resource = agent.state.pendingUseResource
|
|
439
|
-
let waitingLoc = agent.state.waitingAtExtractor.get()
|
|
440
|
-
if agent.getInventoryValue(resource) > agent.state.pendingUseAmount:
|
|
441
|
-
clearWaiting(agent)
|
|
442
|
-
return none(int)
|
|
443
|
-
|
|
444
|
-
var maxWait = 20
|
|
445
|
-
let infoOpt = agent.findExtractor(resource, waitingLoc)
|
|
446
|
-
if infoOpt.isSome():
|
|
447
|
-
maxWait = infoOpt.get().cooldownRemaining + 5
|
|
448
|
-
|
|
449
|
-
inc agent.state.waitSteps
|
|
450
|
-
if agent.state.waitSteps > maxWait:
|
|
451
|
-
clearWaiting(agent)
|
|
452
|
-
return none(int)
|
|
453
|
-
|
|
454
|
-
some(agent.cfg.actions.noop)
|
|
455
|
-
|
|
456
|
-
proc calculateDeficits(agent: LadybugAgent): Table[string, int] =
|
|
457
|
-
if not agent.state.heartRecipeKnown:
|
|
458
|
-
raise newException(ValueError,
|
|
459
|
-
"Heart recipe not discovered! Agent must observe assembler with correct vibe to learn recipe. Ensure protocol_details_obs=True in game config.")
|
|
460
|
-
result = initTable[string, int]()
|
|
461
|
-
for resource in ResourceTypes:
|
|
462
|
-
let required = agent.state.heartRecipe.getOrDefault(resource, 0)
|
|
463
|
-
let deficit = required - agent.getInventoryValue(resource)
|
|
464
|
-
result[resource] = (if deficit > 0: deficit else: 0)
|
|
465
|
-
|
|
466
|
-
proc findNearestExtractor(agent: LadybugAgent, resource: string): Option[ExtractorInfo] =
|
|
467
|
-
if resource notin agent.state.extractors:
|
|
468
|
-
return none(ExtractorInfo)
|
|
469
|
-
var bestDist = high(int)
|
|
470
|
-
var best: ExtractorInfo
|
|
471
|
-
for info in agent.state.extractors[resource]:
|
|
472
|
-
if info.clipped or info.remainingUses == 0:
|
|
473
|
-
continue
|
|
474
|
-
let dist = abs(info.position.x - agent.state.col) + abs(info.position.y - agent.state.row)
|
|
475
|
-
if dist < bestDist:
|
|
476
|
-
bestDist = dist
|
|
477
|
-
best = info
|
|
478
|
-
if bestDist == high(int):
|
|
479
|
-
return none(ExtractorInfo)
|
|
480
|
-
some(best)
|
|
481
|
-
|
|
482
|
-
proc findAnyNeededExtractor(agent: LadybugAgent): Option[(ExtractorInfo, string)] =
|
|
483
|
-
let deficits = agent.calculateDeficits()
|
|
484
|
-
var needed = toSeq(deficits.pairs)
|
|
485
|
-
needed.sort(proc(a, b: (string, int)): int = cmp(b[1], a[1]))
|
|
486
|
-
for (resource, deficit) in needed:
|
|
487
|
-
if deficit > 0:
|
|
488
|
-
let extractorOpt = agent.findNearestExtractor(resource)
|
|
489
|
-
if extractorOpt.isSome():
|
|
490
|
-
return some((extractorOpt.get(), resource))
|
|
491
|
-
none((ExtractorInfo, string))
|
|
492
|
-
|
|
493
|
-
proc isWithinBounds(agent: LadybugAgent, row: int, col: int): bool =
|
|
494
|
-
row >= 0 and row < agent.state.mapHeight and col >= 0 and col < agent.state.mapWidth
|
|
495
|
-
|
|
496
|
-
proc isAgentBlocking(agent: LadybugAgent, row: int, col: int): bool =
|
|
497
|
-
let loc = Location(x: col, y: row)
|
|
498
|
-
loc in agent.state.agentOccupancy
|
|
499
|
-
|
|
500
|
-
proc isTraversable(agent: LadybugAgent, row: int, col: int): bool =
|
|
501
|
-
if not agent.isWithinBounds(row, col):
|
|
502
|
-
return false
|
|
503
|
-
if agent.isAgentBlocking(row, col):
|
|
504
|
-
return false
|
|
505
|
-
agent.state.occupancy[row][col] == cellFree
|
|
506
|
-
|
|
507
|
-
proc isAdjacent(row1: int, col1: int, row2: int, col2: int): bool =
|
|
508
|
-
let dr = abs(row1 - row2)
|
|
509
|
-
let dc = abs(col1 - col2)
|
|
510
|
-
(dr == 1 and dc == 0) or (dr == 0 and dc == 1)
|
|
511
|
-
|
|
512
|
-
proc computeGoalCells(
|
|
513
|
-
agent: LadybugAgent,
|
|
514
|
-
targetRow: int,
|
|
515
|
-
targetCol: int,
|
|
516
|
-
reachAdjacent: bool
|
|
517
|
-
): seq[(int, int)] =
|
|
518
|
-
if not reachAdjacent:
|
|
519
|
-
return @[(targetRow, targetCol)]
|
|
520
|
-
for delta in CardinalNeighbors:
|
|
521
|
-
let nr = targetRow + delta[0]
|
|
522
|
-
let nc = targetCol + delta[1]
|
|
523
|
-
if agent.isTraversable(nr, nc):
|
|
524
|
-
result.add((nr, nc))
|
|
525
|
-
if result.len == 0:
|
|
526
|
-
for delta in CardinalNeighbors:
|
|
527
|
-
let nr = targetRow + delta[0]
|
|
528
|
-
let nc = targetCol + delta[1]
|
|
529
|
-
if agent.isWithinBounds(nr, nc):
|
|
530
|
-
result.add((nr, nc))
|
|
531
|
-
|
|
532
|
-
proc reconstructPath(
|
|
533
|
-
cameFrom: Table[(int, int), (int, int)],
|
|
534
|
-
current: (int, int)
|
|
535
|
-
): seq[(int, int)] =
|
|
536
|
-
var cur = current
|
|
537
|
-
while cameFrom.hasKey(cur):
|
|
538
|
-
let prev = cameFrom[cur]
|
|
539
|
-
if prev == parentSentinel:
|
|
540
|
-
break
|
|
541
|
-
result.add(cur)
|
|
542
|
-
cur = prev
|
|
543
|
-
result.reverse()
|
|
544
|
-
|
|
545
|
-
proc moveTowards(
|
|
546
|
-
agent: LadybugAgent,
|
|
547
|
-
targetRow: int,
|
|
548
|
-
targetCol: int,
|
|
549
|
-
reachAdjacent: bool = false,
|
|
550
|
-
allowGoalBlock: bool = false
|
|
551
|
-
): int =
|
|
552
|
-
if not reachAdjacent and agent.state.row == targetRow and agent.state.col == targetCol:
|
|
553
|
-
return agent.cfg.actions.noop
|
|
554
|
-
let goals = agent.computeGoalCells(targetRow, targetCol, reachAdjacent)
|
|
555
|
-
if goals.len == 0:
|
|
556
|
-
return agent.cfg.actions.noop
|
|
557
|
-
var goalSet = initHashSet[(int, int)]()
|
|
558
|
-
for g in goals:
|
|
559
|
-
goalSet.incl(g)
|
|
560
|
-
let targetLoc = Location(x: targetCol, y: targetRow)
|
|
561
|
-
var pathValid = false
|
|
562
|
-
if agent.state.cachedPath.len > 0 and agent.state.cachedPathTarget.isSome and
|
|
563
|
-
agent.state.cachedPathTarget.get().x == targetCol and
|
|
564
|
-
agent.state.cachedPathTarget.get().y == targetRow and
|
|
565
|
-
agent.state.cachedPathReachAdjacent == reachAdjacent:
|
|
566
|
-
let nextStep = agent.state.cachedPath[0]
|
|
567
|
-
if agent.isTraversable(nextStep[0], nextStep[1]) or (allowGoalBlock and nextStep in goalSet):
|
|
568
|
-
pathValid = true
|
|
569
|
-
else:
|
|
570
|
-
clearCachedPath(agent)
|
|
571
|
-
if not pathValid:
|
|
572
|
-
var queue = initDeque[(int, int)]()
|
|
573
|
-
var cameFrom = initTable[(int, int), (int, int)]()
|
|
574
|
-
let start = (agent.state.row, agent.state.col)
|
|
575
|
-
queue.addLast(start)
|
|
576
|
-
cameFrom[start] = parentSentinel
|
|
577
|
-
var found = false
|
|
578
|
-
var goalReached: (int, int)
|
|
579
|
-
while queue.len > 0 and not found:
|
|
580
|
-
let current = queue.popFirst()
|
|
581
|
-
if current in goalSet:
|
|
582
|
-
found = true
|
|
583
|
-
goalReached = current
|
|
584
|
-
break
|
|
585
|
-
for delta in CardinalNeighbors:
|
|
586
|
-
let nr = current[0] + delta[0]
|
|
587
|
-
let nc = current[1] + delta[1]
|
|
588
|
-
if cameFrom.hasKey((nr, nc)):
|
|
589
|
-
continue
|
|
590
|
-
var canTraverse = agent.isTraversable(nr, nc)
|
|
591
|
-
if not canTraverse and allowGoalBlock and (nr, nc) in goalSet:
|
|
592
|
-
canTraverse = true
|
|
593
|
-
if not canTraverse:
|
|
594
|
-
continue
|
|
595
|
-
cameFrom[(nr, nc)] = current
|
|
596
|
-
queue.addLast((nr, nc))
|
|
597
|
-
if not found:
|
|
598
|
-
clearCachedPath(agent)
|
|
599
|
-
let randomAction = agent.tryRandomDirection()
|
|
600
|
-
return randomAction
|
|
601
|
-
let newPath = reconstructPath(cameFrom, goalReached)
|
|
602
|
-
if newPath.len == 0:
|
|
603
|
-
return agent.cfg.actions.noop
|
|
604
|
-
agent.state.cachedPath = newPath
|
|
605
|
-
agent.state.cachedPathTarget = some(targetLoc)
|
|
606
|
-
agent.state.cachedPathReachAdjacent = reachAdjacent
|
|
607
|
-
let nextStep = agent.state.cachedPath[0]
|
|
608
|
-
agent.state.cachedPath.delete(0)
|
|
609
|
-
if agent.state.cachedPath.len == 0:
|
|
610
|
-
clearCachedPath(agent)
|
|
611
|
-
if allowGoalBlock and agent.isAgentBlocking(nextStep[0], nextStep[1]):
|
|
612
|
-
clearCachedPath(agent)
|
|
613
|
-
let reroute = agent.tryRandomDirection()
|
|
614
|
-
return reroute
|
|
615
|
-
let dr = nextStep[0] - agent.state.row
|
|
616
|
-
let dc = nextStep[1] - agent.state.col
|
|
617
|
-
if dr == -1 and dc == 0:
|
|
618
|
-
return agent.cfg.actions.moveNorth
|
|
619
|
-
if dr == 1 and dc == 0:
|
|
620
|
-
return agent.cfg.actions.moveSouth
|
|
621
|
-
if dr == 0 and dc == 1:
|
|
622
|
-
return agent.cfg.actions.moveEast
|
|
623
|
-
if dr == 0 and dc == -1:
|
|
624
|
-
return agent.cfg.actions.moveWest
|
|
625
|
-
agent.cfg.actions.noop
|
|
626
|
-
|
|
627
|
-
proc directionDelta(direction: string): (int, int) =
|
|
628
|
-
case direction
|
|
629
|
-
of "north": (-1, 0)
|
|
630
|
-
of "south": (1, 0)
|
|
631
|
-
of "east": (0, 1)
|
|
632
|
-
of "west": (0, -1)
|
|
633
|
-
else: (0, 0)
|
|
634
|
-
|
|
635
|
-
proc stepInDirection(agent: LadybugAgent, direction: string): Option[int] =
|
|
636
|
-
let delta = directionDelta(direction)
|
|
637
|
-
if delta == (0, 0):
|
|
638
|
-
return none(int)
|
|
639
|
-
let nr = agent.state.row + delta[0]
|
|
640
|
-
let nc = agent.state.col + delta[1]
|
|
641
|
-
if not agent.isTraversable(nr, nc):
|
|
642
|
-
return none(int)
|
|
643
|
-
let action = agent.moveTowards(nr, nc)
|
|
644
|
-
if action == agent.cfg.actions.noop:
|
|
645
|
-
return none(int)
|
|
646
|
-
some(action)
|
|
647
|
-
|
|
648
|
-
proc tryRandomDirection(agent: LadybugAgent): int =
|
|
649
|
-
var dirs = @DirectionNames
|
|
650
|
-
agent.random.shuffle(dirs)
|
|
651
|
-
for dir in dirs:
|
|
652
|
-
let action = agent.stepInDirection(dir)
|
|
653
|
-
if action.isSome():
|
|
654
|
-
clearCachedPath(agent)
|
|
655
|
-
return action.get()
|
|
656
|
-
return agent.cfg.actions.noop
|
|
657
|
-
|
|
658
|
-
proc explore(agent: LadybugAgent): int =
|
|
659
|
-
clearCachedPath(agent)
|
|
660
|
-
if agent.state.explorationDirection.len > 0:
|
|
661
|
-
let steps = agent.state.stepCount - agent.state.explorationDirectionSetStep
|
|
662
|
-
if steps >= explorationDirectionPersistence:
|
|
663
|
-
agent.state.explorationDirection = ""
|
|
664
|
-
|
|
665
|
-
# Escape mode: navigate toward assembler if stuck recently
|
|
666
|
-
if agent.state.explorationEscapeUntilStep > agent.state.stepCount:
|
|
667
|
-
agent.state.explorationDirection = ""
|
|
668
|
-
if agent.state.stations["assembler"].isSome():
|
|
669
|
-
let assemblerLoc = agent.state.stations["assembler"].get()
|
|
670
|
-
if isAdjacent(agent.state.row, agent.state.col, assemblerLoc.y, assemblerLoc.x):
|
|
671
|
-
agent.state.explorationEscapeUntilStep = 0
|
|
672
|
-
else:
|
|
673
|
-
return agent.moveTowards(assemblerLoc.y, assemblerLoc.x, reachAdjacent = true)
|
|
674
|
-
else:
|
|
675
|
-
agent.state.explorationEscapeUntilStep = 0
|
|
676
|
-
else:
|
|
677
|
-
# Check if we've stayed within a small area recently; if so, trigger escape
|
|
678
|
-
let historyLen = agent.state.positionHistory.len
|
|
679
|
-
if historyLen >= explorationAreaCheckWindow and agent.state.stations["assembler"].isSome():
|
|
680
|
-
var minRow = high(int)
|
|
681
|
-
var maxRow = low(int)
|
|
682
|
-
var minCol = high(int)
|
|
683
|
-
var maxCol = low(int)
|
|
684
|
-
for i in (historyLen - explorationAreaCheckWindow) ..< historyLen:
|
|
685
|
-
let pos = agent.state.positionHistory[i]
|
|
686
|
-
if pos.y < minRow:
|
|
687
|
-
minRow = pos.y
|
|
688
|
-
if pos.y > maxRow:
|
|
689
|
-
maxRow = pos.y
|
|
690
|
-
if pos.x < minCol:
|
|
691
|
-
minCol = pos.x
|
|
692
|
-
if pos.x > maxCol:
|
|
693
|
-
maxCol = pos.x
|
|
694
|
-
let areaHeight = maxRow - minRow + 1
|
|
695
|
-
let areaWidth = maxCol - minCol + 1
|
|
696
|
-
if areaHeight <= explorationAreaSizeThreshold and areaWidth <= explorationAreaSizeThreshold:
|
|
697
|
-
let assemblerLoc = agent.state.stations["assembler"].get()
|
|
698
|
-
let dist = abs(agent.state.row - assemblerLoc.y) + abs(agent.state.col - assemblerLoc.x)
|
|
699
|
-
if dist > explorationAssemblerDistanceThreshold:
|
|
700
|
-
agent.state.explorationEscapeUntilStep = agent.state.stepCount + explorationEscapeDuration
|
|
701
|
-
agent.state.explorationDirection = ""
|
|
702
|
-
return agent.moveTowards(assemblerLoc.y, assemblerLoc.x, reachAdjacent = true)
|
|
703
|
-
|
|
704
|
-
if agent.state.explorationDirection.len > 0:
|
|
705
|
-
let action = agent.stepInDirection(agent.state.explorationDirection)
|
|
706
|
-
if action.isSome():
|
|
707
|
-
return action.get()
|
|
708
|
-
agent.state.explorationDirection = ""
|
|
709
|
-
|
|
710
|
-
var dirs = @DirectionNames
|
|
711
|
-
agent.random.shuffle(dirs)
|
|
712
|
-
for dir in dirs:
|
|
713
|
-
let action = agent.stepInDirection(dir)
|
|
714
|
-
if action.isSome():
|
|
715
|
-
agent.state.explorationDirection = dir
|
|
716
|
-
agent.state.explorationDirectionSetStep = agent.state.stepCount
|
|
717
|
-
return action.get()
|
|
718
|
-
return agent.tryRandomDirection()
|
|
719
|
-
|
|
720
|
-
proc exploreUntil(agent: LadybugAgent, condition: proc (): bool, reason: string): Option[int] =
|
|
721
|
-
if condition():
|
|
722
|
-
return none(int)
|
|
723
|
-
agent.state.explorationEscapeUntilStep = 0
|
|
724
|
-
some(agent.explore())
|
|
725
|
-
|
|
726
|
-
proc navigateToAdjacent(
|
|
727
|
-
agent: LadybugAgent,
|
|
728
|
-
targetRow: int,
|
|
729
|
-
targetCol: int
|
|
730
|
-
): Option[int] =
|
|
731
|
-
if isAdjacent(agent.state.row, agent.state.col, targetRow, targetCol):
|
|
732
|
-
return none(int)
|
|
733
|
-
let action = agent.moveTowards(targetRow, targetCol, reachAdjacent = true)
|
|
734
|
-
if action == agent.cfg.actions.noop:
|
|
735
|
-
return some(agent.explore())
|
|
736
|
-
some(action)
|
|
737
|
-
|
|
738
|
-
proc moveIntoCell(agent: LadybugAgent, targetRow: int, targetCol: int): int =
|
|
739
|
-
if agent.state.row == targetRow and agent.state.col == targetCol:
|
|
740
|
-
return agent.cfg.actions.noop
|
|
741
|
-
if agent.isAgentBlocking(targetRow, targetCol):
|
|
742
|
-
clearCachedPath(agent)
|
|
743
|
-
return agent.tryRandomDirection()
|
|
744
|
-
agent.state.usingObjectThisStep = true
|
|
745
|
-
return agent.moveTowards(targetRow, targetCol, allowGoalBlock = true)
|
|
746
|
-
|
|
747
|
-
proc useExtractor(agent: LadybugAgent, extractor: ExtractorInfo): int =
|
|
748
|
-
if extractor.cooldownRemaining > 0:
|
|
749
|
-
agent.state.waitingAtExtractor = some(extractor.position)
|
|
750
|
-
inc agent.state.waitSteps
|
|
751
|
-
return agent.cfg.actions.noop
|
|
752
|
-
if extractor.remainingUses == 0 or extractor.clipped:
|
|
753
|
-
clearWaiting(agent)
|
|
754
|
-
return agent.cfg.actions.noop
|
|
755
|
-
agent.state.pendingUseResource = extractor.resource
|
|
756
|
-
agent.state.pendingUseAmount = agent.getInventoryValue(extractor.resource)
|
|
757
|
-
agent.state.waitingAtExtractor = some(extractor.position)
|
|
758
|
-
agent.state.waitSteps = 0
|
|
759
|
-
return agent.moveIntoCell(extractor.position.y, extractor.position.x)
|
|
760
|
-
|
|
761
|
-
proc handleStuck(agent: LadybugAgent): Option[int] =
|
|
762
|
-
if not agent.state.stuckLoopDetected:
|
|
763
|
-
return none(int)
|
|
764
|
-
agent.state.stuckLoopDetected = false
|
|
765
|
-
some(agent.tryRandomDirection())
|
|
766
|
-
|
|
767
|
-
proc updatePhase(agent: LadybugAgent) =
|
|
768
|
-
let previousPhase = agent.state.phase
|
|
769
|
-
if agent.state.energy < rechargeThresholdLow:
|
|
770
|
-
agent.state.phase = rechargePhase
|
|
771
|
-
clearWaiting(agent)
|
|
772
|
-
if previousPhase != agent.state.phase:
|
|
773
|
-
clearCachedPath(agent)
|
|
774
|
-
return
|
|
775
|
-
if agent.state.phase == rechargePhase and agent.state.energy < rechargeThresholdHigh:
|
|
776
|
-
return
|
|
777
|
-
if agent.state.hearts > 0:
|
|
778
|
-
agent.state.phase = deliverPhase
|
|
779
|
-
clearWaiting(agent)
|
|
780
|
-
if previousPhase != agent.state.phase:
|
|
781
|
-
clearCachedPath(agent)
|
|
782
|
-
return
|
|
783
|
-
if agent.state.heartRecipeKnown:
|
|
784
|
-
var ready = true
|
|
785
|
-
for resource, amount in agent.state.heartRecipe.pairs:
|
|
786
|
-
if agent.getInventoryValue(resource) < amount:
|
|
787
|
-
ready = false
|
|
788
|
-
break
|
|
789
|
-
if ready:
|
|
790
|
-
agent.state.phase = assemblePhase
|
|
791
|
-
clearWaiting(agent)
|
|
792
|
-
if previousPhase != agent.state.phase:
|
|
793
|
-
clearCachedPath(agent)
|
|
794
|
-
return
|
|
795
|
-
agent.state.phase = gatherPhase
|
|
796
|
-
if previousPhase != agent.state.phase:
|
|
797
|
-
clearCachedPath(agent)
|
|
798
|
-
|
|
799
|
-
proc desiredVibe(agent: LadybugAgent): string =
|
|
800
|
-
case agent.state.phase
|
|
801
|
-
of gatherPhase:
|
|
802
|
-
if agent.state.targetResource.len > 0:
|
|
803
|
-
return resourceVibe(agent.state.targetResource)
|
|
804
|
-
return "carbon_a"
|
|
805
|
-
of assemblePhase:
|
|
806
|
-
return "heart_a"
|
|
807
|
-
of deliverPhase:
|
|
808
|
-
return "default"
|
|
809
|
-
of rechargePhase:
|
|
810
|
-
return "charger"
|
|
811
|
-
|
|
812
|
-
proc vibeAction(agent: LadybugAgent, vibe: string): int =
|
|
813
|
-
case vibe
|
|
814
|
-
of "carbon_a": agent.cfg.actions.vibeCarbonA
|
|
815
|
-
of "carbon_b": agent.cfg.actions.vibeCarbonB
|
|
816
|
-
of "oxygen_a": agent.cfg.actions.vibeOxygenA
|
|
817
|
-
of "oxygen_b": agent.cfg.actions.vibeOxygenB
|
|
818
|
-
of "germanium_a": agent.cfg.actions.vibeGermaniumA
|
|
819
|
-
of "germanium_b": agent.cfg.actions.vibeGermaniumB
|
|
820
|
-
of "silicon_a": agent.cfg.actions.vibeSiliconA
|
|
821
|
-
of "silicon_b": agent.cfg.actions.vibeSiliconB
|
|
822
|
-
of "heart_a": agent.cfg.actions.vibeHeartA
|
|
823
|
-
of "heart_b": agent.cfg.actions.vibeHeartB
|
|
824
|
-
of "charger": agent.cfg.actions.vibeCharger
|
|
825
|
-
of "gear": agent.cfg.actions.vibeGear
|
|
826
|
-
of "assembler": agent.cfg.actions.vibeAssembler
|
|
827
|
-
of "chest": agent.cfg.actions.vibeChest
|
|
828
|
-
of "wall": agent.cfg.actions.vibeWall
|
|
829
|
-
of "default": agent.cfg.actions.vibeDefault
|
|
830
|
-
else: agent.cfg.actions.vibeDefault
|
|
831
|
-
|
|
832
|
-
proc doGather(agent: LadybugAgent): int =
|
|
833
|
-
let waitAction = agent.handleWaiting()
|
|
834
|
-
if waitAction.isSome():
|
|
835
|
-
return waitAction.get()
|
|
836
|
-
if not agent.state.heartRecipeKnown:
|
|
837
|
-
agent.state.targetResource = ""
|
|
838
|
-
clearWaiting(agent)
|
|
839
|
-
return agent.explore()
|
|
840
|
-
let deficits = agent.calculateDeficits()
|
|
841
|
-
var needsResources = false
|
|
842
|
-
for deficit in deficits.values:
|
|
843
|
-
if deficit > 0:
|
|
844
|
-
needsResources = true
|
|
845
|
-
break
|
|
846
|
-
if not needsResources:
|
|
847
|
-
clearWaiting(agent)
|
|
848
|
-
return agent.cfg.actions.noop
|
|
849
|
-
let exploreAction = agent.exploreUntil(
|
|
850
|
-
proc (): bool = agent.findAnyNeededExtractor().isSome(),
|
|
851
|
-
"need extractor"
|
|
852
|
-
)
|
|
853
|
-
if exploreAction.isSome():
|
|
854
|
-
return exploreAction.get()
|
|
855
|
-
let extractorChoice = agent.findAnyNeededExtractor()
|
|
856
|
-
if extractorChoice.isNone():
|
|
857
|
-
return agent.explore()
|
|
858
|
-
let (extractor, resource) = extractorChoice.get()
|
|
859
|
-
agent.state.targetResource = resource
|
|
860
|
-
let nav = agent.navigateToAdjacent(extractor.position.y, extractor.position.x)
|
|
861
|
-
if nav.isSome():
|
|
862
|
-
clearWaiting(agent)
|
|
863
|
-
return nav.get()
|
|
864
|
-
return agent.useExtractor(extractor)
|
|
865
|
-
|
|
866
|
-
proc doAssemble(agent: LadybugAgent): int =
|
|
867
|
-
agent.state.targetResource = ""
|
|
868
|
-
let exploreAction = agent.exploreUntil(
|
|
869
|
-
proc (): bool = agent.state.stations["assembler"].isSome(),
|
|
870
|
-
"need assembler"
|
|
871
|
-
)
|
|
872
|
-
if exploreAction.isSome():
|
|
873
|
-
return exploreAction.get()
|
|
874
|
-
let loc = agent.state.stations["assembler"].get()
|
|
875
|
-
let nav = agent.navigateToAdjacent(loc.y, loc.x)
|
|
876
|
-
if nav.isSome():
|
|
877
|
-
return nav.get()
|
|
878
|
-
return agent.moveIntoCell(loc.y, loc.x)
|
|
879
|
-
|
|
880
|
-
proc doDeliver(agent: LadybugAgent): int =
|
|
881
|
-
agent.state.targetResource = ""
|
|
882
|
-
let exploreAction = agent.exploreUntil(
|
|
883
|
-
proc (): bool = agent.state.stations["chest"].isSome(),
|
|
884
|
-
"need chest"
|
|
885
|
-
)
|
|
886
|
-
if exploreAction.isSome():
|
|
887
|
-
return exploreAction.get()
|
|
888
|
-
let loc = agent.state.stations["chest"].get()
|
|
889
|
-
let nav = agent.navigateToAdjacent(loc.y, loc.x)
|
|
890
|
-
if nav.isSome():
|
|
891
|
-
return nav.get()
|
|
892
|
-
return agent.moveIntoCell(loc.y, loc.x)
|
|
893
|
-
|
|
894
|
-
proc doRecharge(agent: LadybugAgent): int =
|
|
895
|
-
agent.state.targetResource = ""
|
|
896
|
-
let exploreAction = agent.exploreUntil(
|
|
897
|
-
proc (): bool = agent.state.stations["charger"].isSome(),
|
|
898
|
-
"need charger"
|
|
899
|
-
)
|
|
900
|
-
if exploreAction.isSome():
|
|
901
|
-
return exploreAction.get()
|
|
902
|
-
let loc = agent.state.stations["charger"].get()
|
|
903
|
-
let nav = agent.navigateToAdjacent(loc.y, loc.x)
|
|
904
|
-
if nav.isSome():
|
|
905
|
-
return nav.get()
|
|
906
|
-
return agent.moveIntoCell(loc.y, loc.x)
|
|
907
|
-
|
|
908
|
-
proc executePhase(agent: LadybugAgent): int =
|
|
909
|
-
case agent.state.phase
|
|
910
|
-
of gatherPhase:
|
|
911
|
-
doGather(agent)
|
|
912
|
-
of assemblePhase:
|
|
913
|
-
doAssemble(agent)
|
|
914
|
-
of deliverPhase:
|
|
915
|
-
doDeliver(agent)
|
|
916
|
-
of rechargePhase:
|
|
917
|
-
doRecharge(agent)
|
|
918
|
-
|
|
919
|
-
proc step*(
|
|
920
|
-
agent: LadybugAgent,
|
|
921
|
-
numAgents: int,
|
|
922
|
-
numTokens: int,
|
|
923
|
-
sizeToken: int,
|
|
924
|
-
rawObservations: pointer,
|
|
925
|
-
numActions: int,
|
|
926
|
-
rawActions: pointer
|
|
927
|
-
) {.raises: [].} =
|
|
928
|
-
discard numAgents
|
|
929
|
-
discard numActions
|
|
930
|
-
let observations = cast[ptr UncheckedArray[uint8]](rawObservations)
|
|
931
|
-
let actions = cast[ptr UncheckedArray[int32]](rawActions)
|
|
932
|
-
let offset = agent.agentId * numTokens * sizeToken
|
|
933
|
-
let agentObservation = cast[pointer](observations[offset].addr)
|
|
934
|
-
try:
|
|
935
|
-
inc agent.state.stepCount
|
|
936
|
-
updatePosition(agent)
|
|
937
|
-
let visible = agent.decodeObservation(numTokens, sizeToken, agentObservation)
|
|
938
|
-
agent.updateInventory(visible)
|
|
939
|
-
discoverObjects(agent, visible)
|
|
940
|
-
updatePhase(agent)
|
|
941
|
-
let stuckAction = handleStuck(agent)
|
|
942
|
-
if stuckAction.isSome():
|
|
943
|
-
let actionId = stuckAction.get()
|
|
944
|
-
actions[agent.agentId] = actionId.int32
|
|
945
|
-
agent.state.lastAction = actionId
|
|
946
|
-
return
|
|
947
|
-
let desired = agent.desiredVibe()
|
|
948
|
-
if agent.state.currentGlyph != desired:
|
|
949
|
-
agent.state.currentGlyph = desired
|
|
950
|
-
let actionId = agent.vibeAction(desired)
|
|
951
|
-
actions[agent.agentId] = actionId.int32
|
|
952
|
-
agent.state.lastAction = actionId
|
|
953
|
-
return
|
|
954
|
-
let actionId = agent.executePhase()
|
|
955
|
-
actions[agent.agentId] = actionId.int32
|
|
956
|
-
agent.state.lastAction = actionId
|
|
957
|
-
except:
|
|
958
|
-
echo getCurrentException().getStackTrace()
|
|
959
|
-
echo getCurrentExceptionMsg()
|
|
960
|
-
actions[agent.agentId] = agent.cfg.actions.noop.int32
|
|
961
|
-
agent.state.lastAction = agent.cfg.actions.noop
|
|
962
|
-
|
|
963
|
-
proc newLadybugPolicy*(environmentConfig: string): LadybugPolicy =
|
|
964
|
-
let cfg = parseConfig(environmentConfig)
|
|
965
|
-
var agents: seq[LadybugAgent] = @[]
|
|
966
|
-
for id in 0 ..< cfg.config.numAgents:
|
|
967
|
-
agents.add(newLadybugAgent(id, environmentConfig))
|
|
968
|
-
LadybugPolicy(agents: agents)
|
|
969
|
-
|
|
970
|
-
proc stepBatch*(
|
|
971
|
-
policy: LadybugPolicy,
|
|
972
|
-
agentIds: pointer,
|
|
973
|
-
numAgentIds: int,
|
|
974
|
-
numAgents: int,
|
|
975
|
-
numTokens: int,
|
|
976
|
-
sizeToken: int,
|
|
977
|
-
rawObservations: pointer,
|
|
978
|
-
numActions: int,
|
|
979
|
-
rawActions: pointer
|
|
980
|
-
) =
|
|
981
|
-
let ids = cast[ptr UncheckedArray[int32]](agentIds)
|
|
982
|
-
for i in 0 ..< numAgentIds:
|
|
983
|
-
let idx = int(ids[i])
|
|
984
|
-
step(policy.agents[idx], numAgents, numTokens, sizeToken, rawObservations, numActions, rawActions)
|