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.
Files changed (169) hide show
  1. cogames/cli/client.py +60 -6
  2. cogames/cli/docsync/__init__.py +0 -0
  3. cogames/cli/docsync/_nb_md_directive_processing.py +180 -0
  4. cogames/cli/docsync/_nb_md_sync.py +103 -0
  5. cogames/cli/docsync/_nb_py_sync.py +122 -0
  6. cogames/cli/docsync/_three_way_sync.py +115 -0
  7. cogames/cli/docsync/_utils.py +76 -0
  8. cogames/cli/docsync/docsync.py +156 -0
  9. cogames/cli/leaderboard.py +112 -28
  10. cogames/cli/mission.py +64 -53
  11. cogames/cli/policy.py +46 -10
  12. cogames/cli/submit.py +268 -67
  13. cogames/cogs_vs_clips/cog.py +79 -0
  14. cogames/cogs_vs_clips/cogs_vs_clips_mapgen.md +19 -16
  15. cogames/cogs_vs_clips/cogsguard_reward_variants.py +153 -0
  16. cogames/cogs_vs_clips/cogsguard_tutorial.py +56 -0
  17. cogames/cogs_vs_clips/evals/README.md +10 -16
  18. cogames/cogs_vs_clips/evals/cogsguard_evals.py +81 -0
  19. cogames/cogs_vs_clips/evals/diagnostic_evals.py +49 -444
  20. cogames/cogs_vs_clips/evals/difficulty_variants.py +13 -326
  21. cogames/cogs_vs_clips/evals/integrated_evals.py +5 -45
  22. cogames/cogs_vs_clips/evals/spanning_evals.py +9 -180
  23. cogames/cogs_vs_clips/mission.py +187 -146
  24. cogames/cogs_vs_clips/missions.py +46 -137
  25. cogames/cogs_vs_clips/procedural.py +8 -8
  26. cogames/cogs_vs_clips/sites.py +107 -3
  27. cogames/cogs_vs_clips/stations.py +198 -186
  28. cogames/cogs_vs_clips/tutorial_missions.py +1 -1
  29. cogames/cogs_vs_clips/variants.py +25 -476
  30. cogames/device.py +13 -1
  31. cogames/{policy/scripted_agent/README.md → docs/SCRIPTED_AGENT.md} +82 -58
  32. cogames/evaluate.py +18 -30
  33. cogames/main.py +1434 -243
  34. cogames/maps/canidate1_1000.map +1 -1
  35. cogames/maps/canidate1_1000_stations.map +2 -2
  36. cogames/maps/canidate1_500.map +1 -1
  37. cogames/maps/canidate1_500_stations.map +2 -2
  38. cogames/maps/canidate2_1000.map +1 -1
  39. cogames/maps/canidate2_1000_stations.map +2 -2
  40. cogames/maps/canidate2_500.map +1 -1
  41. cogames/maps/canidate2_500_stations.map +2 -2
  42. cogames/maps/canidate3_1000.map +1 -1
  43. cogames/maps/canidate3_1000_stations.map +2 -2
  44. cogames/maps/canidate3_500.map +1 -1
  45. cogames/maps/canidate3_500_stations.map +2 -2
  46. cogames/maps/canidate4_500.map +1 -1
  47. cogames/maps/canidate4_500_stations.map +2 -2
  48. cogames/maps/cave_base_50.map +2 -2
  49. cogames/maps/diagnostic_evals/diagnostic_agile.map +2 -2
  50. cogames/maps/diagnostic_evals/diagnostic_agile_hard.map +2 -2
  51. cogames/maps/diagnostic_evals/diagnostic_charge_up.map +2 -2
  52. cogames/maps/diagnostic_evals/diagnostic_charge_up_hard.map +2 -2
  53. cogames/maps/diagnostic_evals/diagnostic_chest_navigation1.map +2 -2
  54. cogames/maps/diagnostic_evals/diagnostic_chest_navigation1_hard.map +2 -2
  55. cogames/maps/diagnostic_evals/diagnostic_chest_navigation2.map +2 -2
  56. cogames/maps/diagnostic_evals/diagnostic_chest_navigation2_hard.map +2 -2
  57. cogames/maps/diagnostic_evals/diagnostic_chest_navigation3.map +2 -2
  58. cogames/maps/diagnostic_evals/diagnostic_chest_navigation3_hard.map +2 -2
  59. cogames/maps/diagnostic_evals/diagnostic_chest_near.map +2 -2
  60. cogames/maps/diagnostic_evals/diagnostic_chest_search.map +2 -2
  61. cogames/maps/diagnostic_evals/diagnostic_chest_search_hard.map +2 -2
  62. cogames/maps/diagnostic_evals/diagnostic_extract_lab.map +2 -2
  63. cogames/maps/diagnostic_evals/diagnostic_extract_lab_hard.map +2 -2
  64. cogames/maps/diagnostic_evals/diagnostic_memory.map +2 -2
  65. cogames/maps/diagnostic_evals/diagnostic_memory_hard.map +2 -2
  66. cogames/maps/diagnostic_evals/diagnostic_radial.map +2 -2
  67. cogames/maps/diagnostic_evals/diagnostic_radial_hard.map +2 -2
  68. cogames/maps/diagnostic_evals/diagnostic_resource_lab.map +2 -2
  69. cogames/maps/diagnostic_evals/diagnostic_unclip.map +2 -2
  70. cogames/maps/evals/eval_balanced_spread.map +9 -5
  71. cogames/maps/evals/eval_clip_oxygen.map +9 -5
  72. cogames/maps/evals/eval_collect_resources.map +9 -5
  73. cogames/maps/evals/eval_collect_resources_hard.map +9 -5
  74. cogames/maps/evals/eval_collect_resources_medium.map +9 -5
  75. cogames/maps/evals/eval_divide_and_conquer.map +9 -5
  76. cogames/maps/evals/eval_energy_starved.map +9 -5
  77. cogames/maps/evals/eval_multi_coordinated_collect_hard.map +9 -5
  78. cogames/maps/evals/eval_oxygen_bottleneck.map +9 -5
  79. cogames/maps/evals/eval_single_use_world.map +9 -5
  80. cogames/maps/evals/extractor_hub_100x100.map +9 -5
  81. cogames/maps/evals/extractor_hub_30x30.map +9 -5
  82. cogames/maps/evals/extractor_hub_50x50.map +9 -5
  83. cogames/maps/evals/extractor_hub_70x70.map +9 -5
  84. cogames/maps/evals/extractor_hub_80x80.map +9 -5
  85. cogames/maps/machina_100_stations.map +2 -2
  86. cogames/maps/machina_200_stations.map +2 -2
  87. cogames/maps/machina_200_stations_small.map +2 -2
  88. cogames/maps/machina_eval_exp01.map +2 -2
  89. cogames/maps/machina_eval_template_large.map +2 -2
  90. cogames/maps/machinatrainer4agents.map +2 -2
  91. cogames/maps/machinatrainer4agentsbase.map +2 -2
  92. cogames/maps/machinatrainerbig.map +2 -2
  93. cogames/maps/machinatrainersmall.map +2 -2
  94. cogames/maps/planky_evals/aligner_avoid_aoe.map +28 -0
  95. cogames/maps/planky_evals/aligner_full_cycle.map +28 -0
  96. cogames/maps/planky_evals/aligner_gear.map +24 -0
  97. cogames/maps/planky_evals/aligner_hearts.map +24 -0
  98. cogames/maps/planky_evals/aligner_junction.map +26 -0
  99. cogames/maps/planky_evals/exploration_distant.map +28 -0
  100. cogames/maps/planky_evals/maze.map +32 -0
  101. cogames/maps/planky_evals/miner_best_resource.map +26 -0
  102. cogames/maps/planky_evals/miner_deposit.map +24 -0
  103. cogames/maps/planky_evals/miner_extract.map +26 -0
  104. cogames/maps/planky_evals/miner_full_cycle.map +28 -0
  105. cogames/maps/planky_evals/miner_gear.map +24 -0
  106. cogames/maps/planky_evals/multi_role.map +28 -0
  107. cogames/maps/planky_evals/resource_chain.map +30 -0
  108. cogames/maps/planky_evals/scout_explore.map +32 -0
  109. cogames/maps/planky_evals/scout_gear.map +24 -0
  110. cogames/maps/planky_evals/scrambler_full_cycle.map +28 -0
  111. cogames/maps/planky_evals/scrambler_gear.map +24 -0
  112. cogames/maps/planky_evals/scrambler_target.map +26 -0
  113. cogames/maps/planky_evals/stuck_corridor.map +32 -0
  114. cogames/maps/planky_evals/survive_retreat.map +26 -0
  115. cogames/maps/training_facility_clipped.map +2 -2
  116. cogames/maps/training_facility_open_1.map +2 -2
  117. cogames/maps/training_facility_open_2.map +2 -2
  118. cogames/maps/training_facility_open_3.map +2 -2
  119. cogames/maps/training_facility_tight_4.map +2 -2
  120. cogames/maps/training_facility_tight_5.map +2 -2
  121. cogames/maps/vanilla_large.map +2 -2
  122. cogames/maps/vanilla_small.map +2 -2
  123. cogames/pickup.py +183 -0
  124. cogames/play.py +166 -33
  125. cogames/policy/chaos_monkey.py +54 -0
  126. cogames/policy/nim_agents/__init__.py +27 -10
  127. cogames/policy/nim_agents/agents.py +121 -60
  128. cogames/policy/nim_agents/thinky_eval.py +35 -222
  129. cogames/policy/pufferlib_policy.py +67 -32
  130. cogames/policy/starter_agent.py +184 -0
  131. cogames/policy/trainable_policy_template.py +4 -1
  132. cogames/train.py +51 -13
  133. cogames/verbose.py +2 -2
  134. cogames-0.3.64.dist-info/METADATA +1842 -0
  135. cogames-0.3.64.dist-info/RECORD +159 -0
  136. cogames-0.3.64.dist-info/licenses/LICENSE +21 -0
  137. cogames-0.3.64.dist-info/top_level.txt +2 -0
  138. metta_alo/__init__.py +0 -0
  139. metta_alo/job_specs.py +17 -0
  140. metta_alo/policy.py +16 -0
  141. metta_alo/pure_single_episode_runner.py +75 -0
  142. metta_alo/py.typed +0 -0
  143. metta_alo/rollout.py +322 -0
  144. metta_alo/scoring.py +168 -0
  145. cogames/maps/diagnostic_evals/diagnostic_assembler_near.map +0 -49
  146. cogames/maps/diagnostic_evals/diagnostic_assembler_search.map +0 -49
  147. cogames/maps/diagnostic_evals/diagnostic_assembler_search_hard.map +0 -89
  148. cogames/policy/nim_agents/common.nim +0 -887
  149. cogames/policy/nim_agents/install.sh +0 -1
  150. cogames/policy/nim_agents/ladybug_agent.nim +0 -984
  151. cogames/policy/nim_agents/nim_agents.nim +0 -55
  152. cogames/policy/nim_agents/nim_agents.nims +0 -14
  153. cogames/policy/nim_agents/nimby.lock +0 -3
  154. cogames/policy/nim_agents/racecar_agents.nim +0 -884
  155. cogames/policy/nim_agents/random_agents.nim +0 -68
  156. cogames/policy/nim_agents/test_agents.py +0 -53
  157. cogames/policy/nim_agents/thinky_agents.nim +0 -717
  158. cogames/policy/scripted_agent/baseline_agent.py +0 -1049
  159. cogames/policy/scripted_agent/demo_policy.py +0 -244
  160. cogames/policy/scripted_agent/pathfinding.py +0 -126
  161. cogames/policy/scripted_agent/starter_agent.py +0 -136
  162. cogames/policy/scripted_agent/types.py +0 -235
  163. cogames/policy/scripted_agent/unclipping_agent.py +0 -476
  164. cogames/policy/scripted_agent/utils.py +0 -385
  165. cogames-0.3.49.dist-info/METADATA +0 -406
  166. cogames-0.3.49.dist-info/RECORD +0 -136
  167. cogames-0.3.49.dist-info/top_level.txt +0 -1
  168. {cogames-0.3.49.dist-info → cogames-0.3.64.dist-info}/WHEEL +0 -0
  169. {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)