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,887 +0,0 @@
1
-
2
- import
3
- std/[algorithm, strformat, strutils, tables, sets, options],
4
- fidget2/measure,
5
- jsony
6
-
7
- type
8
- ConfigFeature* = object
9
- id*: int
10
- name*: string
11
- normalization*: float
12
-
13
- AssemblerProtocol* = object
14
- inputResources*: Table[string, int]
15
- outputResources*: Table[string, int]
16
-
17
- PolicyConfig* = object
18
- numAgents*: int
19
- obsWidth*: int
20
- obsHeight*: int
21
- actions*: seq[string]
22
- tags*: seq[string]
23
- obsFeatures*: seq[ConfigFeature]
24
- assemblerProtocols*: seq[AssemblerProtocol]
25
-
26
- Config* = object
27
- config*: PolicyConfig
28
- actions*: Actions
29
- features*: Features
30
- tags*: Tags
31
- vibes*: Vibes
32
- assemblerProtocols*: seq[AssemblerProtocol]
33
- inventoryTokenBase*: int
34
- inventoryPowerFeatures*: Table[int, array[2, int]]
35
-
36
- FeatureValue* = object
37
- featureId*: int
38
- value*: int
39
-
40
- Location* = object
41
- x*: int
42
- y*: int
43
-
44
- MapBounds* = object
45
- minX*: int
46
- maxX*: int
47
- minY*: int
48
- maxY*: int
49
-
50
- Actions* = object
51
- noop*: int
52
- moveNorth*: int
53
- moveSouth*: int
54
- moveWest*: int
55
- moveEast*: int
56
- vibeDefault*: int
57
- vibeCharger*: int
58
- vibeCarbonA*: int
59
- vibeCarbonB*: int
60
- vibeOxygenA*: int
61
- vibeOxygenB*: int
62
- vibeGermaniumA*: int
63
- vibeGermaniumB*: int
64
- vibeSiliconA*: int
65
- vibeSiliconB*: int
66
- vibeHeartA*: int
67
- vibeHeartB*: int
68
- vibeGear*: int
69
- vibeAssembler*: int
70
- vibeChest*: int
71
- vibeWall*: int
72
-
73
- Tags* = object
74
- agent*: int
75
- assembler*: int
76
- carbonExtractor*: int
77
- charger*: int
78
- chest*: int
79
- germaniumExtractor*: int
80
- oxygenExtractor*: int
81
- siliconExtractor*: int
82
- wall*: int
83
-
84
- Vibes* = object
85
- # TODO: Pass with vibes from config.
86
- default*: int = 0
87
- charger*: int = 1
88
- carbonA*: int = 2
89
- carbonB*: int = 3
90
- oxygenA*: int = 4
91
- oxygenB*: int = 5
92
- germaniumA*: int = 6
93
- germaniumB*: int = 7
94
- siliconA*: int = 8
95
- siliconB*: int = 9
96
- heartA*: int = 10
97
- heartB*: int = 11
98
- gear*: int = 12
99
- assembler*: int = 13
100
- chest*: int = 14
101
- wall*: int = 15
102
- paperclip*: int = 16
103
-
104
- Features* = object
105
- group*: int
106
- frozen*: int
107
- episodeCompletionPct*: int
108
- goal*: int
109
- lastAction*: int
110
- lastReward*: int
111
- vibe*: int
112
- compass*: int
113
- tag*: int
114
- cooldownRemaining*: int
115
- clipped*: int
116
- remainingUses*: int
117
- invEnergy*: int
118
- invCarbon*: int
119
- invOxygen*: int
120
- invGermanium*: int
121
- invSilicon*: int
122
- invHeart*: int
123
- invDecoder*: int
124
- invModulator*: int
125
- invResonator*: int
126
- invScrambler*: int
127
-
128
- protocolInputEnergy*: int
129
- protocolInputCarbon*: int
130
- protocolInputOxygen*: int
131
- protocolInputGermanium*: int
132
- protocolInputSilicon*: int
133
- protocolInputHeart*: int
134
- protocolInputDecoder*: int
135
- protocolInputModulator*: int
136
- protocolInputResonator*: int
137
- protocolInputScrambler*: int
138
-
139
- protocolOutputEnergy*: int
140
- protocolOutputCarbon*: int
141
- protocolOutputOxygen*: int
142
- protocolOutputGermanium*: int
143
- protocolOutputSilicon*: int
144
- protocolOutputHeart*: int
145
- protocolOutputDecoder*: int
146
- protocolOutputModulator*: int
147
- protocolOutputResonator*: int
148
- protocolOutputScrambler*: int
149
-
150
- RecipeInfo* = object
151
- pattern*: seq[int] # In vibe indices
152
-
153
- energyCost*: int
154
- carbonCost*: int
155
- oxygenCost*: int
156
- germaniumCost*: int
157
- siliconCost*: int
158
- heartCost*: int
159
- decoderCost*: int
160
- modulatorCost*: int
161
- resonatorCost*: int
162
- scramblerCost*: int
163
-
164
- energyOutput*: int
165
- carbonOutput*: int
166
- oxygenOutput*: int
167
- germaniumOutput*: int
168
- siliconOutput*: int
169
- heartOutput*: int
170
- decoderOutput*: int
171
- modulatorOutput*: int
172
- resonatorOutput*: int
173
- scramblerOutput*: int
174
- cooldown*: int
175
-
176
- proc `+`*(location1: Location, location2: Location): Location =
177
- ## Add two locations.
178
- result.x = location1.x + location2.x
179
- result.y = location1.y + location2.y
180
-
181
- proc `-`*(location1: Location, location2: Location): Location =
182
- ## Subtract two locations.
183
- result.x = location1.x - location2.x
184
- result.y = location1.y - location2.y
185
-
186
- proc manhattan*(a, b: Location): int =
187
- ## Get the Manhattan distance between two locations.
188
- abs(a.x - b.x) + abs(a.y - b.y)
189
-
190
- proc generateSpiral*(count: int): seq[Location] =
191
- ## Generate a square spiral starting at (0,0) and spiraling outwards.
192
- result = @[]
193
- var
194
- x = 0
195
- y = 0
196
- dx = 1
197
- dy = 0
198
- stepSize = 1
199
- stepsTaken = 0
200
- directionChanges = 0
201
- for i in 0 ..< count:
202
- result.add(Location(x: x, y: y))
203
- x += dx
204
- y += dy
205
- inc stepsTaken
206
- if stepsTaken == stepSize:
207
- stepsTaken = 0
208
- inc(directionChanges)
209
- # Rotate direction: (dx, dy) -> (-dy, dx)
210
- let tmp = dx
211
- dx = -dy
212
- dy = tmp
213
- if directionChanges mod 2 == 0:
214
- inc stepSize
215
- return result
216
-
217
- const spiral* = generateSpiral(1000)
218
-
219
- proc `$`*(recipe: RecipeInfo): string =
220
- ## Stringify the recipe.
221
- result = "Recipe(pattern: ["
222
- for vibe in recipe.pattern:
223
- case vibe:
224
- of 0:
225
- result.add("Default")
226
- result.add(", ")
227
- of 1:
228
- result.add("Charger")
229
- result.add(", ")
230
- of 2:
231
- result.add("CarbonA")
232
- result.add(", ")
233
- of 3:
234
- result.add("CarbonB")
235
- result.add(", ")
236
- of 4:
237
- result.add("OxygenA")
238
- result.add(", ")
239
- of 5:
240
- result.add("OxygenB")
241
- result.add(", ")
242
- of 6:
243
- result.add("GermaniumA")
244
- result.add(", ")
245
- of 7:
246
- result.add("GermaniumB")
247
- result.add(", ")
248
- of 8:
249
- result.add("SiliconA")
250
- result.add(", ")
251
- of 9:
252
- result.add("SiliconB")
253
- result.add(", ")
254
- of 10:
255
- result.add("HeartA")
256
- result.add(", ")
257
- of 11:
258
- result.add("HeartB")
259
- result.add(", ")
260
- of 12:
261
- result.add("Gear")
262
- result.add(", ")
263
- of 13:
264
- result.add("Assembler")
265
- result.add(", ")
266
- of 14:
267
- result.add("Chest")
268
- result.add(", ")
269
- of 15:
270
- result.add("Wall")
271
- result.add(", ")
272
- of 16:
273
- result.add("Paperclip")
274
- result.add(", ")
275
- else:
276
- result.add("???")
277
- result.add(", ")
278
- result.removeSuffix(", ")
279
- result.add("]")
280
- if recipe.energyCost != 0:
281
- result.add(" E:")
282
- result.add($recipe.energyCost)
283
- if recipe.carbonCost != 0:
284
- result.add(" C:")
285
- result.add($recipe.carbonCost)
286
- if recipe.oxygenCost != 0:
287
- result.add(" O2:")
288
- result.add($recipe.oxygenCost)
289
- if recipe.germaniumCost != 0:
290
- result.add(" Ge:")
291
- result.add($recipe.germaniumCost)
292
- if recipe.siliconCost != 0:
293
- result.add(" Si:")
294
- result.add($recipe.siliconCost)
295
- if recipe.heartCost != 0:
296
- result.add(" Heart:")
297
- result.add($recipe.heartCost)
298
- if recipe.decoderCost != 0:
299
- result.add(" Decoder:")
300
- result.add($recipe.decoderCost)
301
- if recipe.modulatorCost != 0:
302
- result.add(" Modulator:")
303
- result.add($recipe.modulatorCost)
304
- if recipe.resonatorCost != 0:
305
- result.add(" Resonator:")
306
- result.add($recipe.resonatorCost)
307
- if recipe.scramblerCost != 0:
308
- result.add(" Scrambler:")
309
- result.add($recipe.scramblerCost)
310
- result.add(" -> ")
311
- if recipe.energyOutput != 0:
312
- result.add(" E:")
313
- result.add($recipe.energyOutput)
314
- if recipe.carbonOutput != 0:
315
- result.add(" C:")
316
- result.add($recipe.carbonOutput)
317
- if recipe.oxygenOutput != 0:
318
- result.add(" O2:")
319
- result.add($recipe.oxygenOutput)
320
- if recipe.germaniumOutput != 0:
321
- result.add(" Ge:")
322
- result.add($recipe.germaniumOutput)
323
- if recipe.siliconOutput != 0:
324
- result.add(" Si:")
325
- result.add($recipe.siliconOutput)
326
- if recipe.heartOutput != 0:
327
- result.add(" Heart:")
328
- result.add($recipe.heartOutput)
329
- if recipe.decoderOutput != 0:
330
- result.add(" Decoder:")
331
- result.add($recipe.decoderOutput)
332
- if recipe.modulatorOutput != 0:
333
- result.add(" Modulator:")
334
- result.add($recipe.modulatorOutput)
335
- if recipe.resonatorOutput != 0:
336
- result.add(" Resonator:")
337
- result.add($recipe.resonatorOutput)
338
- if recipe.scramblerOutput != 0:
339
- result.add(" Scrambler:")
340
- result.add($recipe.scramblerOutput)
341
- result.add(")")
342
-
343
- proc registerProtocolFeature(feature: ConfigFeature; prefix: string;
344
- dest: var Table[string, int]): bool =
345
- ## Store protocol input/output features keyed by their resource suffix.
346
- if not feature.name.startsWith(prefix):
347
- return false
348
- if feature.name.len <= prefix.len:
349
- echo "Protocol feature missing resource suffix: ", feature.name
350
- return true
351
-
352
- let resource = feature.name[prefix.len .. ^1]
353
- dest[resource] = feature.id
354
- return true
355
-
356
- proc ctrlCHandler*() {.noconv.} =
357
- ## Handle ctrl-c signal to exit cleanly.
358
- echo "\nNim DLL caught ctrl-c, exiting..."
359
- quit(0)
360
-
361
- proc initCHook*() =
362
- setControlCHook(ctrlCHandler)
363
- echo "NimAgents initialized"
364
-
365
- proc parseConfig*(environmentConfig: string): Config {.raises: [].} =
366
- try:
367
- var config = environmentConfig.fromJson(PolicyConfig)
368
- result = Config(config: config)
369
- result.assemblerProtocols = config.assemblerProtocols
370
- result.inventoryPowerFeatures = initTable[int, array[2, int]]()
371
- var inventoryBaseIds = initTable[string, int]()
372
- var inventoryPowerIds = initTable[string, array[2, int]]()
373
-
374
- for feature in config.obsFeatures:
375
- if feature.name.startsWith("inv:"):
376
- if result.inventoryTokenBase == 0:
377
- result.inventoryTokenBase = int(feature.normalization)
378
- let suffix = feature.name[4 .. ^1]
379
- let powerIndex = suffix.rfind(":p")
380
- if powerIndex != -1:
381
- let resource = suffix[0 ..< powerIndex]
382
- let powerStr = suffix[powerIndex + 2 .. ^1]
383
- if resource.len > 0 and powerStr.len > 0 and powerStr.allCharsInSet({'0' .. '9'}):
384
- let power = parseInt(powerStr)
385
- if power > 0:
386
- var powers = inventoryPowerIds.getOrDefault(resource, [-1, -1])
387
- if power <= 2:
388
- powers[power - 1] = feature.id
389
- inventoryPowerIds[resource] = powers
390
- continue
391
- else:
392
- inventoryBaseIds[suffix] = feature.id
393
- case feature.name:
394
- of "agent:group":
395
- result.features.group = feature.id
396
- of "agent:frozen":
397
- result.features.frozen = feature.id
398
- of "episode_completion_pct":
399
- result.features.episodeCompletionPct = feature.id
400
- of "goal":
401
- result.features.goal = feature.id
402
- of "last_action":
403
- result.features.lastAction = feature.id
404
- of "last_reward":
405
- result.features.lastReward = feature.id
406
- of "vibe":
407
- result.features.vibe = feature.id
408
- of "agent:compass":
409
- result.features.compass = feature.id
410
- of "tag":
411
- result.features.tag = feature.id
412
- of "cooldown_remaining":
413
- result.features.cooldownRemaining = feature.id
414
- of "clipped":
415
- result.features.clipped = feature.id
416
- of "remaining_uses":
417
- result.features.remainingUses = feature.id
418
- of "inv:energy":
419
- result.features.invEnergy = feature.id
420
- of "inv:carbon":
421
- result.features.invCarbon = feature.id
422
- of "inv:oxygen":
423
- result.features.invOxygen = feature.id
424
- of "inv:germanium":
425
- result.features.invGermanium = feature.id
426
- of "inv:silicon":
427
- result.features.invSilicon = feature.id
428
- of "inv:heart":
429
- result.features.invHeart = feature.id
430
- of "inv:decoder":
431
- result.features.invDecoder = feature.id
432
- of "inv:modulator":
433
- result.features.invModulator = feature.id
434
- of "inv:resonator":
435
- result.features.invResonator = feature.id
436
- of "inv:scrambler":
437
- result.features.invScrambler = feature.id
438
- of "protocol_input:energy":
439
- result.features.protocolInputEnergy = feature.id
440
- of "protocol_input:carbon":
441
- result.features.protocolInputCarbon = feature.id
442
- of "protocol_input:oxygen":
443
- result.features.protocolInputOxygen = feature.id
444
- of "protocol_input:germanium":
445
- result.features.protocolInputGermanium = feature.id
446
- of "protocol_input:silicon":
447
- result.features.protocolInputSilicon = feature.id
448
- of "protocol_input:heart":
449
- result.features.protocolInputHeart = feature.id
450
- of "protocol_input:decoder":
451
- result.features.protocolInputDecoder = feature.id
452
- of "protocol_input:modulator":
453
- result.features.protocolInputModulator = feature.id
454
- of "protocol_input:resonator":
455
- result.features.protocolInputResonator = feature.id
456
- of "protocol_input:scrambler":
457
- result.features.protocolInputScrambler = feature.id
458
- of "protocol_output:energy":
459
- result.features.protocolOutputEnergy = feature.id
460
- of "protocol_output:carbon":
461
- result.features.protocolOutputCarbon = feature.id
462
- of "protocol_output:oxygen":
463
- result.features.protocolOutputOxygen = feature.id
464
- of "protocol_output:germanium":
465
- result.features.protocolOutputGermanium = feature.id
466
- of "protocol_output:silicon":
467
- result.features.protocolOutputSilicon = feature.id
468
- of "protocol_output:heart":
469
- result.features.protocolOutputHeart = feature.id
470
- of "protocol_output:decoder":
471
- result.features.protocolOutputDecoder = feature.id
472
- of "protocol_output:modulator":
473
- result.features.protocolOutputModulator = feature.id
474
- of "protocol_output:resonator":
475
- result.features.protocolOutputResonator = feature.id
476
- of "protocol_output:scrambler":
477
- result.features.protocolOutputScrambler = feature.id
478
- else:
479
- echo "Unknown feature: ", feature.name
480
-
481
- for resource, powers in inventoryPowerIds:
482
- if resource in inventoryBaseIds:
483
- result.inventoryPowerFeatures[inventoryBaseIds[resource]] = powers
484
-
485
- for id, name in config.actions:
486
- case name:
487
- of "noop":
488
- result.actions.noop = id
489
- of "move_north":
490
- result.actions.moveNorth = id
491
- of "move_south":
492
- result.actions.moveSouth = id
493
- of "move_west":
494
- result.actions.moveWest = id
495
- of "move_east":
496
- result.actions.moveEast = id
497
- of "change_vibe_default":
498
- result.actions.vibeDefault = id
499
- of "change_vibe_charger":
500
- result.actions.vibeCharger = id
501
- of "change_vibe_carbon_a":
502
- result.actions.vibeCarbonA = id
503
- of "change_vibe_carbon_b":
504
- result.actions.vibeCarbonB = id
505
- of "change_vibe_oxygen_a":
506
- result.actions.vibeOxygenA = id
507
- of "change_vibe_oxygen_b":
508
- result.actions.vibeOxygenB = id
509
- of "change_vibe_germanium_a":
510
- result.actions.vibeGermaniumA = id
511
- of "change_vibe_germanium_b":
512
- result.actions.vibeGermaniumB = id
513
- of "change_vibe_silicon_a":
514
- result.actions.vibeSiliconA = id
515
- of "change_vibe_silicon_b":
516
- result.actions.vibeSiliconB = id
517
- of "change_vibe_heart_a":
518
- result.actions.vibeHeartA = id
519
- of "change_vibe_heart_b":
520
- result.actions.vibeHeartB = id
521
- of "change_vibe_carbon":
522
- result.actions.vibeCarbonA = id
523
- of "change_vibe_oxygen":
524
- result.actions.vibeOxygenA = id
525
- of "change_vibe_germanium":
526
- result.actions.vibeGermaniumA = id
527
- of "change_vibe_silicon":
528
- result.actions.vibeSiliconA = id
529
- of "change_vibe_heart":
530
- result.actions.vibeHeartA = id
531
- of "change_vibe_gear":
532
- result.actions.vibeGear = id
533
- of "change_vibe_assembler":
534
- result.actions.vibeAssembler = id
535
- of "change_vibe_chest":
536
- result.actions.vibeChest = id
537
- of "change_vibe_wall":
538
- result.actions.vibeWall = id
539
- else:
540
- discard
541
-
542
- for id, name in config.tags:
543
- case name:
544
- of "agent":
545
- result.tags.agent = id
546
- of "assembler":
547
- result.tags.assembler = id
548
- of "carbon_extractor":
549
- result.tags.carbonExtractor = id
550
- of "charger":
551
- result.tags.charger = id
552
- of "chest":
553
- result.tags.chest = id
554
- of "germanium_extractor":
555
- result.tags.germaniumExtractor = id
556
- of "oxygen_extractor":
557
- result.tags.oxygenExtractor = id
558
- of "silicon_extractor":
559
- result.tags.siliconExtractor = id
560
- of "wall":
561
- result.tags.wall = id
562
- else:
563
- discard
564
- except JsonError, ValueError:
565
- echo "Error parsing environment config: ", getCurrentExceptionMsg()
566
-
567
-
568
- proc computeMapBounds*(map: Table[Location, seq[FeatureValue]]): MapBounds =
569
- ## Compute the bounds of the map.
570
- result.minX = -5
571
- result.maxX = 5
572
- result.minY = -5
573
- result.maxY = 5
574
- for location, featureValues in map:
575
- if location.x < result.minX:
576
- result.minX = location.x
577
- if location.x > result.maxX:
578
- result.maxX = location.x
579
- if location.y < result.minY:
580
- result.minY = location.y
581
- if location.y > result.maxY:
582
- result.maxY = location.y
583
-
584
- proc drawMap*(cfg: Config, map: Table[Location, seq[FeatureValue]], seen: HashSet[Location]) =
585
- ## Draw the map to the console.
586
- let bounds = computeMapBounds(map)
587
- var line = "+"
588
- for x in bounds.minX .. bounds.maxX:
589
- line.add "--"
590
- line.add "+"
591
- echo line
592
- for y in bounds.minY .. bounds.maxY:
593
- line = "|"
594
- for x in bounds.minX .. bounds.maxX:
595
- var cell = " "
596
- let location = Location(x: x, y: y)
597
- if location notin seen:
598
- cell = "~~"
599
- if location in map:
600
- for featureValue in map[location]:
601
- if featureValue.featureId == cfg.features.group:
602
- if featureValue.value == 0:
603
- cell = "@" & ($featureValue.value)[0]
604
- if featureValue.featureId == cfg.features.tag:
605
- if featureValue.value == cfg.tags.agent:
606
- cell = "@@"
607
- elif featureValue.value == cfg.tags.assembler:
608
- cell = "As"
609
- elif featureValue.value == cfg.tags.carbonExtractor:
610
- cell = "Ca"
611
- elif featureValue.value == cfg.tags.charger:
612
- cell = "En"
613
- elif featureValue.value == cfg.tags.chest:
614
- cell = "Ch"
615
- elif featureValue.value == cfg.tags.germaniumExtractor:
616
- cell = "Ge"
617
- elif featureValue.value == cfg.tags.oxygenExtractor:
618
- cell = "O2"
619
- elif featureValue.value == cfg.tags.siliconExtractor:
620
- cell = "Si"
621
- elif featureValue.value == cfg.tags.wall:
622
- cell = "##"
623
- else:
624
- cell = &"{featureValue.value:2d}"
625
- line.add cell
626
- line.add "|"
627
- echo line
628
- line = "+"
629
- for x in bounds.minX .. bounds.maxX:
630
- line.add "--"
631
- line.add "+"
632
- echo line
633
-
634
- proc getTag*(cfg: Config, map: Table[Location, seq[FeatureValue]], location: Location): int =
635
- ## Get the type id of the location in the map.
636
- if location in map:
637
- for featureValue in map[location]:
638
- if featureValue.featureId == cfg.features.tag:
639
- return featureValue.value
640
- return -1
641
-
642
- proc getFeature*(
643
- cfg: Config,
644
- visible: Table[Location,
645
- seq[FeatureValue]], featureId: int,
646
- location: Location = Location(x: 0, y: 0)
647
- ): int =
648
- ## Get the feature of the visible map.
649
- if location in visible:
650
- for featureValue in visible[location]:
651
- if featureValue.featureId == featureId:
652
- return featureValue.value
653
- return -1
654
-
655
- proc getLastAction*(cfg: Config, visible: Table[Location, seq[FeatureValue]]): int =
656
- ## Get the last action of the visible map.
657
- cfg.getFeature(visible, cfg.features.lastAction)
658
-
659
- proc getInventory*(
660
- cfg: Config,
661
- visible: Table[Location, seq[FeatureValue]],
662
- inventoryId: int,
663
- location: Location = Location(x: 0, y: 0)
664
- ): int =
665
- ## Get the inventory of the visible map.
666
- result = cfg.getFeature(visible, inventoryId, location)
667
- # Missing inventory is 0.
668
- if result == -1:
669
- result = 0
670
- if cfg.inventoryTokenBase > 1 and inventoryId in cfg.inventoryPowerFeatures:
671
- let powers = cfg.inventoryPowerFeatures[inventoryId]
672
- if powers[0] > -1:
673
- let powerValue = cfg.getFeature(visible, powers[0], location)
674
- if powerValue > -1:
675
- result += powerValue * cfg.inventoryTokenBase
676
- if powers[1] > -1:
677
- let powerValue = cfg.getFeature(visible, powers[1], location)
678
- if powerValue > -1:
679
- result += powerValue * cfg.inventoryTokenBase * cfg.inventoryTokenBase
680
-
681
- proc getOtherInventory*(
682
- cfg: Config,
683
- map: Table[Location, seq[FeatureValue]],
684
- location: Location,
685
- inventoryId: int
686
- ): int =
687
- ## Get the other inventory of the visible map.
688
- cfg.getInventory(map, inventoryId, location)
689
-
690
- proc getVibe*(cfg: Config, visible: Table[Location, seq[FeatureValue]], location: Location): int =
691
- ## Get the vibe of the visible map.
692
- result = cfg.getFeature(visible, cfg.features.vibe, location)
693
-
694
- proc getNearby*(
695
- cfg: Config,
696
- currentLocation: Location,
697
- map: Table[Location, seq[FeatureValue]],
698
- tagId: int
699
- ): Option[Location] =
700
- ## Get if there is a nearby location with the given tag.
701
- var
702
- found = false
703
- closestLocation = Location(x: 0, y: 0)
704
- closestDistance = 9999
705
- for location, featureValues in map:
706
- for featureValue in featureValues:
707
- if featureValue.featureId == cfg.features.tag and featureValue.value == tagId:
708
- let distance = manhattan(location, currentLocation)
709
- if distance < closestDistance:
710
- closestDistance = distance
711
- closestLocation = location
712
- found = true
713
- if found:
714
- return some(closestLocation)
715
- return none(Location)
716
-
717
- proc getNearbyUnseen*(
718
- cfg: Config,
719
- currentLocation: Location,
720
- map: Table[Location, seq[FeatureValue]],
721
- seen: HashSet[Location],
722
- unreachables: HashSet[Location]
723
- ): Option[Location] =
724
- ## Get if there is a nearby location that is unseen.
725
- var
726
- found = false
727
- closestLocation = Location(x: 0, y: 0)
728
- closestDistance = 9999
729
- for spiralLocation in spiral:
730
- let location = spiralLocation + currentLocation
731
- if location notin seen and location notin unreachables:
732
- let distance = manhattan(location, currentLocation)
733
- if distance < closestDistance:
734
- closestDistance = distance
735
- closestLocation = location
736
- found = true
737
- if found:
738
- return some(closestLocation)
739
- else:
740
- return none(Location)
741
-
742
- proc simpleGoTo*(cfg: Config, currentLocation: Location, targetLocation: Location): int =
743
- ## Navigate to the given location.
744
- echo "currentLocation: ", currentLocation.x, ", ", currentLocation.y
745
- echo "targetLocation: ", targetLocation.x, ", ", targetLocation.y
746
- if currentLocation.x < targetLocation.x:
747
- echo "moving east"
748
- return cfg.actions.moveEast
749
- elif currentLocation.x > targetLocation.x:
750
- echo "moving west"
751
- return cfg.actions.moveWest
752
- elif currentLocation.y < targetLocation.y:
753
- echo "moving south"
754
- return cfg.actions.moveSouth
755
- elif currentLocation.y > targetLocation.y:
756
- echo "moving north"
757
- return cfg.actions.moveNorth
758
- else:
759
- echo "no action"
760
- return cfg.actions.noop
761
-
762
- proc isWalkable*(cfg: Config, map: Table[Location, seq[FeatureValue]], loc: Location): bool =
763
- # Default: tiles not present are walkable; present tiles are walkable unless you decide otherwise.
764
- if loc in map:
765
- for featureValue in map[loc]:
766
- if featureValue.featureId == cfg.features.tag:
767
- # Its something that blocks movement.
768
- return false
769
- if featureValue.featureId == cfg.features.group:
770
- # If the group there, then its an agent.
771
- return false
772
- return true
773
-
774
- proc neighbors(loc: Location): array[4, Location] =
775
- [
776
- Location(x: loc.x + 1, y: loc.y), # East
777
- Location(x: loc.x - 1, y: loc.y), # West
778
- Location(x: loc.x, y: loc.y - 1), # North (assuming y-1 is north)
779
- Location(x: loc.x, y: loc.y + 1) # South
780
- ]
781
-
782
- proc reconstructPath(cameFrom: Table[Location, Location], current: Location): seq[Location] =
783
- var cur = current
784
- result = @[cur]
785
- var cf = cameFrom
786
- while cf.hasKey(cur):
787
- cur = cf[cur]
788
- result.add(cur)
789
- result.reverse()
790
-
791
- proc stepToAction(cfg: Config, fromLoc, toLoc: Location): int =
792
- # Translate the first step along the path into an action id.
793
- if toLoc.x == fromLoc.x + 1 and toLoc.y == fromLoc.y:
794
- return cfg.actions.moveEast
795
- elif toLoc.x == fromLoc.x - 1 and toLoc.y == fromLoc.y:
796
- return cfg.actions.moveWest
797
- elif toLoc.y == fromLoc.y - 1 and toLoc.x == fromLoc.x:
798
- return cfg.actions.moveNorth
799
- elif toLoc.y == fromLoc.y + 1 and toLoc.x == fromLoc.x:
800
- return cfg.actions.moveSouth
801
- else:
802
- # Not an adjacent cardinal move; noop as a safeguard.
803
- return cfg.actions.noop
804
-
805
- proc aStar*(
806
- cfg: Config,
807
- currentLocation: Location,
808
- targetLocation: Location,
809
- map: Table[Location, seq[FeatureValue]]
810
- ): Option[int] {.measure.} =
811
- ## Navigate to the given location using A*. Returns the next action to take.
812
- if currentLocation == targetLocation:
813
- return none(int)
814
-
815
- # Open set: nodes to evaluate
816
- var openSet = initHashSet[Location]()
817
- openSet.incl(currentLocation)
818
-
819
- # For path reconstruction
820
- var cameFrom = initTable[Location, Location]()
821
-
822
- # gScore: cost from start
823
- var gScore = initTable[Location, int]()
824
- gScore[currentLocation] = 0
825
-
826
- # fScore: g + heuristic
827
- var fScore = initTable[Location, int]()
828
- fScore[currentLocation] = manhattan(currentLocation, targetLocation)
829
-
830
- # Utility to get fScore with default "infinite"
831
- proc getF(loc: Location): int =
832
- if fScore.hasKey(loc): fScore[loc] else: high(int)
833
-
834
- while openSet.len > 0:
835
-
836
- if openSet.len > 100:
837
- # Too far... bail out.
838
- return none(int)
839
-
840
- # Pick node in openSet with lowest fScore
841
- var currentIter = false
842
- var current: Location
843
- var bestF = high(int)
844
- for n in openSet:
845
- let f = getF(n)
846
- if not currentIter or f < bestF:
847
- bestF = f
848
- current = n
849
- currentIter = true
850
-
851
- # (Optional sanity guard)
852
- if not currentIter:
853
- # openSet was somehow empty; break out safely
854
- return none(int)
855
- if current == targetLocation:
856
- let path = reconstructPath(cameFrom, current)
857
- # path[0] is currentLocation; path[1] is our next step (if exists)
858
- if path.len >= 2:
859
- return some(stepToAction(cfg, path[0], path[1]))
860
- else:
861
- return none(int)
862
-
863
- openSet.excl(current)
864
-
865
- # Explore neighbors
866
- for nb in neighbors(current).items:
867
- # Allow stepping onto the goal even if it's "blocked" (e.g., extractor tile).
868
- if nb != targetLocation and not cfg.isWalkable(map, nb):
869
- continue
870
-
871
- let tentativeG = (if gScore.hasKey(current): gScore[current] else: high(int)) + 1
872
- # If nb has no gScore or this path is better, record it
873
- let nbG = (if gScore.hasKey(nb): gScore[nb] else: high(int))
874
- if tentativeG < nbG:
875
- cameFrom[nb] = current
876
- gScore[nb] = tentativeG
877
- fScore[nb] = tentativeG + manhattan(nb, targetLocation)
878
- if nb notin openSet:
879
- openSet.incl(nb)
880
-
881
- # No path found — fall back to greedy single-step
882
- return none(int)
883
-
884
- proc remove*[T](seq: var seq[T], item: T) =
885
- let index = seq.find(item)
886
- if index != -1:
887
- seq.delete(index)