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,884 +0,0 @@
1
- import
2
- std/[strformat, tables, random, sets, options, json, deques],
3
- fidget2/measure,
4
- common
5
-
6
- const
7
- MaxEnergy = 100
8
- MaxResourceInventory = 100
9
- MaxToolInventory = 100
10
- MaxStepsDefault = 10000
11
-
12
- PutCarbonAmount = 10
13
- PutOxygenAmount = 10
14
- PutGermaniumAmount = 1
15
- PutSiliconAmount = 25
16
-
17
- type
18
- RaceCarAgent* = ref object
19
- agentId*: int
20
-
21
- maxSteps: int
22
- steps: int
23
- map: Table[Location, seq[FeatureValue]]
24
- seen: HashSet[Location]
25
- unreachable: HashSet[Location]
26
- depleted: HashSet[Location] # extractors/chargers with remainingUses == 0
27
- cfg: Config
28
- random: Rand
29
- location: Location
30
- lastActions: seq[int]
31
- recipes: seq[RecipeInfo]
32
-
33
- carbonTarget: int
34
- oxygenTarget: int
35
- germaniumTarget: int
36
- siliconTarget: int
37
-
38
- assignedVibe: Option[int] # desired vibe for vibe-check recipes
39
- needsPatternReapply: bool # if we changed vibe away from pattern for a side-task
40
-
41
- bump: bool
42
- offsets4: seq[Location] # 4 cardinal but random for each agent
43
- seenAssembler: bool
44
- seenChest: bool
45
- assemblerHome: Option[Location]
46
- chestHome: Option[Location]
47
- exploreLocations: seq[Location]
48
-
49
- RaceCarPolicy* = ref object
50
- agents*: seq[RaceCarAgent]
51
-
52
- proc log(message: string) =
53
- when defined(debug):
54
- echo message
55
-
56
- const Offsets4 = [
57
- Location(x: -1, y: +0),
58
- Location(x: +0, y: +1),
59
- Location(x: +0, y: -1),
60
- Location(x: +1, y: +0),
61
- ]
62
-
63
- const Offsets8 = [
64
- Location(x: +0, y: +0),
65
- Location(x: +0, y: +1),
66
- Location(x: +0, y: -1),
67
- Location(x: +1, y: +0),
68
- Location(x: +1, y: +1),
69
- Location(x: +1, y: -1),
70
- Location(x: -1, y: +0),
71
- Location(x: -1, y: +1),
72
- Location(x: -1, y: -1),
73
- ]
74
-
75
- proc getActiveRecipe(agent: RaceCarAgent): RecipeInfo {.measure.} =
76
- ## Get the recipes form the assembler protocol inputs.
77
- let assemblerLocation = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.assembler)
78
- if assemblerLocation.isSome():
79
- let location = assemblerLocation.get()
80
- # Get the vibe key.
81
- result.pattern = @[]
82
- for offsets in Offsets8:
83
- let vibeKey = agent.cfg.getVibe(agent.map, location + offsets)
84
- if vibeKey != -1:
85
- result.pattern.add(vibeKey)
86
-
87
- # Get the required resources.
88
- let assemblerFeatures = agent.map[location]
89
- for feature in assemblerFeatures:
90
- if feature.featureId == agent.cfg.features.protocolInputEnergy:
91
- result.energyCost = feature.value
92
- elif feature.featureId == agent.cfg.features.protocolInputCarbon:
93
- result.carbonCost = feature.value
94
- elif feature.featureId == agent.cfg.features.protocolInputOxygen:
95
- result.oxygenCost = feature.value
96
- elif feature.featureId == agent.cfg.features.protocolInputGermanium:
97
- result.germaniumCost = feature.value
98
- elif feature.featureId == agent.cfg.features.protocolInputSilicon:
99
- result.siliconCost = feature.value
100
- elif feature.featureId == agent.cfg.features.protocolInputHeart:
101
- result.heartCost = feature.value
102
- elif feature.featureId == agent.cfg.features.protocolInputDecoder:
103
- result.decoderCost = feature.value
104
- elif feature.featureId == agent.cfg.features.protocolInputModulator:
105
- result.modulatorCost = feature.value
106
- elif feature.featureId == agent.cfg.features.protocolInputResonator:
107
- result.resonatorCost = feature.value
108
- elif feature.featureId == agent.cfg.features.protocolInputScrambler:
109
- result.scramblerCost = feature.value
110
-
111
- elif feature.featureId == agent.cfg.features.protocolOutputEnergy:
112
- result.energyOutput = feature.value
113
- elif feature.featureId == agent.cfg.features.protocolOutputCarbon:
114
- result.carbonOutput = feature.value
115
- elif feature.featureId == agent.cfg.features.protocolOutputOxygen:
116
- result.oxygenOutput = feature.value
117
- elif feature.featureId == agent.cfg.features.protocolOutputGermanium:
118
- result.germaniumOutput = feature.value
119
- elif feature.featureId == agent.cfg.features.protocolOutputSilicon:
120
- result.siliconOutput = feature.value
121
- elif feature.featureId == agent.cfg.features.protocolOutputHeart:
122
- result.heartOutput = feature.value
123
- elif feature.featureId == agent.cfg.features.protocolOutputDecoder:
124
- result.decoderOutput = feature.value
125
- elif feature.featureId == agent.cfg.features.protocolOutputModulator:
126
- result.modulatorOutput = feature.value
127
- elif feature.featureId == agent.cfg.features.protocolOutputResonator:
128
- result.resonatorOutput = feature.value
129
- elif feature.featureId == agent.cfg.features.protocolOutputScrambler:
130
- result.scramblerOutput = feature.value
131
-
132
- proc newRaceCarAgent*(agentId: int, environmentConfig: string): RaceCarAgent =
133
- ## Create a new racecar agent, the fastest and the smartest agent.
134
-
135
- var config = parseConfig(environmentConfig)
136
- result = RaceCarAgent(agentId: agentId, cfg: config)
137
- result.maxSteps = MaxStepsDefault
138
- # Try to read max_steps from the environment config; fall back to default.
139
- try:
140
- let env = environmentConfig.parseJson()
141
- if env.hasKey("game") and env["game"].hasKey("max_steps"):
142
- result.maxSteps = env["game"]["max_steps"].getInt()
143
- elif env.hasKey("max_steps"):
144
- result.maxSteps = env["max_steps"].getInt()
145
- except CatchableError:
146
- discard
147
- result.random = initRand(agentId)
148
- result.map = initTable[Location, seq[FeatureValue]]()
149
- result.seen = initHashSet[Location]()
150
- result.unreachable = initHashSet[Location]()
151
- result.depleted = initHashSet[Location]()
152
- result.location = Location(x: 0, y: 0)
153
- result.assignedVibe = none(int)
154
- result.needsPatternReapply = false
155
- result.steps = 0
156
- result.assemblerHome = none(Location)
157
- result.chestHome = none(Location)
158
- result.lastActions = @[]
159
- # Randomize the offsets4 for each agent, so they take different directions.
160
- var offsets4 = Offsets4
161
- result.random.shuffle(offsets4)
162
- result.offsets4 = @[]
163
- for o in offsets4:
164
- result.offsets4.add(o)
165
-
166
- result.exploreLocations = @[
167
- Location(x: -7, y: 0),
168
- Location(x: 0, y: +7),
169
- Location(x: +7, y: 0),
170
- Location(x: 0, y: -7),
171
- ]
172
- result.random.shuffle(result.exploreLocations)
173
-
174
- proc updateMap(agent: RaceCarAgent, visible: Table[Location, seq[FeatureValue]]) {.measure.} =
175
- ## Update the big map with the small visible map.
176
-
177
- if agent.map.len == 0:
178
- # First time we're called, just copy the visible map to the big map.
179
- agent.map = visible
180
- agent.location = Location(x: 0, y: 0)
181
- for x in -5 .. 5:
182
- for y in -5 .. 5:
183
- let visibleLocation = Location(x: x, y: y)
184
- agent.seen.incl(visibleLocation)
185
- return
186
-
187
- var newLocation = agent.location
188
- let lastAction = agent.cfg.getLastAction(visible)
189
- if lastAction == agent.cfg.actions.moveNorth:
190
- newLocation.y -= 1
191
- elif lastAction == agent.cfg.actions.moveSouth:
192
- newLocation.y += 1
193
- elif lastAction == agent.cfg.actions.moveWest:
194
- newLocation.x -= 1
195
- elif lastAction == agent.cfg.actions.moveEast:
196
- newLocation.x += 1
197
-
198
- # Does the new location have force any tags to no be there?
199
- # If so we did a bump instead of a move.
200
- block bumpCheck:
201
- agent.bump = false
202
- for x in -5 .. 5:
203
- for y in -5 .. 5:
204
- let visibleLocation = Location(x: x, y: y)
205
- let mapLocation = Location(x: x + newLocation.x, y: y + newLocation.y)
206
- if mapLocation notin agent.seen:
207
- continue
208
- var visibleTag = agent.cfg.getTag(visible, visibleLocation)
209
- if visibleTag == agent.cfg.tags.agent:
210
- # Ignore agents.
211
- visibleTag = -1
212
- var mapTag = agent.cfg.getTag(agent.map, mapLocation)
213
- if mapTag == agent.cfg.tags.agent:
214
- # Ignore agents.
215
- mapTag = -1
216
- if visibleTag != mapTag:
217
- newLocation = agent.location
218
- agent.bump = true
219
- break bumpCheck
220
-
221
- # Update the seen set.
222
- agent.location = newLocation
223
- for x in -5 .. 5:
224
- for y in -5 .. 5:
225
- let visibleLocation = Location(x: x, y: y)
226
- let mapLocation = Location(x: x + agent.location.x, y: y + agent.location.y)
227
- if visibleLocation in visible:
228
- agent.map[mapLocation] = visible[visibleLocation]
229
- # Mark depleted sites to avoid revisits.
230
- for f in visible[visibleLocation]:
231
- if f.featureId == agent.cfg.features.remainingUses and f.value == 0:
232
- agent.depleted.incl(mapLocation)
233
- else:
234
- agent.map[mapLocation] = @[]
235
- agent.seen.incl(mapLocation)
236
-
237
- # agent.cfg.drawMap(agent.map, agent.seen)
238
-
239
- proc getNumAgentsNearby*(
240
- cfg: Config,
241
- location: Location,
242
- map: Table[Location, seq[FeatureValue]]
243
- ): int {.measure.} =
244
- ## Get the number of agents nearby.
245
- for offset in Offsets8:
246
- let at = location + offset
247
- if at in map:
248
- for featureValue in map[at]:
249
- if featureValue.featureId == cfg.features.group:
250
- result += 1
251
- break
252
-
253
- proc getNearbyExtractor*(
254
- cfg: Config,
255
- currentLocation: Location,
256
- map: Table[Location, seq[FeatureValue]],
257
- tagId: int
258
- ): Option[Location] =
259
- ## Get if there is a nearby location with the given tag.
260
- var
261
- found = false
262
- closestLocation = Location(x: 0, y: 0)
263
- closestDistance = 9999
264
- for location, featureValues in map:
265
- for featureValue in featureValues:
266
- if featureValue.featureId == cfg.features.tag and featureValue.value == tagId:
267
- let agentsNearby = cfg.getNumAgentsNearby(location, map)
268
- if agentsNearby > 1:
269
- continue
270
- var skip = false
271
- for f in map[location]:
272
- if f.featureId == cfg.features.remainingUses and f.value == 0:
273
- skip = true
274
- break
275
- # if f.featureId == cfg.features.cooldownRemaining and f.value > 50:
276
- # skip = true
277
- # break
278
- if skip:
279
- continue
280
- let distance = manhattan(location, currentLocation)
281
- if distance < closestDistance:
282
- closestDistance = distance
283
- closestLocation = location
284
- found = true
285
- if found:
286
- return some(closestLocation)
287
- return none(Location)
288
-
289
- proc step*(
290
- agent: RaceCarAgent,
291
- numAgents: int,
292
- numTokens: int,
293
- sizeToken: int,
294
- rawObservation: pointer,
295
- numActions: int,
296
- agentAction: ptr int32
297
- ) {.measure.} =
298
- try:
299
-
300
- let observations = cast[ptr UncheckedArray[uint8]](rawObservation)
301
-
302
- # Parse the tokens into a vision map.
303
- var visible: Table[Location, seq[FeatureValue]]
304
- for token in 0 ..< numTokens:
305
- let locationPacked = observations[token * sizeToken]
306
- let featureId = observations[token * sizeToken + 1]
307
- let value = observations[token * sizeToken + 2]
308
- if locationPacked == 255 and featureId == 255 and value == 255:
309
- break
310
- var location: Location
311
- if locationPacked != 0xFF:
312
- location.y = (locationPacked shr 4).int - 5
313
- location.x = (locationPacked and 0x0F).int - 5
314
- if location notin visible:
315
- visible[location] = @[]
316
- visible[location].add(FeatureValue(featureId: featureId.int, value: value.int))
317
-
318
- proc doAction(action: int) {.measure.} =
319
-
320
- # Get last action from observations, in case something else moved us.
321
- agent.lastActions.add(agent.cfg.getLastAction(visible))
322
-
323
- # Helper to set action without modifying history again.
324
- proc setAction(actionToSet: int32) =
325
- agentAction[] = actionToSet
326
-
327
- # Stuck prevention: if last 2 actions are oscillating, sometimes noop.
328
- if agent.lastActions.len >= 2:
329
- if action == agent.cfg.actions.moveWest and
330
- agent.lastActions[^1] == agent.cfg.actions.moveEast and
331
- agent.lastActions[^2] == agent.cfg.actions.moveWest:
332
- if agent.random.rand(1 .. 2) == 1:
333
- log "Stuck prevention: left, right, left"
334
- setAction(agent.cfg.actions.noop.int32)
335
- return
336
- elif action == agent.cfg.actions.moveEast and
337
- agent.lastActions[^1] == agent.cfg.actions.moveWest and
338
- agent.lastActions[^2] == agent.cfg.actions.moveEast:
339
- if agent.random.rand(1 .. 2) == 1:
340
- log "Stuck prevention: right, left, right"
341
- setAction(agent.cfg.actions.noop.int32)
342
- return
343
- elif action == agent.cfg.actions.moveNorth and
344
- agent.lastActions[^1] == agent.cfg.actions.moveSouth and
345
- agent.lastActions[^2] == agent.cfg.actions.moveNorth:
346
- if agent.random.rand(1 .. 2) == 1:
347
- log "Stuck prevention: north, south, north"
348
- setAction(agent.cfg.actions.noop.int32)
349
- return
350
- elif action == agent.cfg.actions.moveSouth and
351
- agent.lastActions[^1] == agent.cfg.actions.moveNorth and
352
- agent.lastActions[^2] == agent.cfg.actions.moveSouth:
353
- if agent.random.rand(1 .. 2) == 1:
354
- log "Stuck prevention: south, north, south"
355
- setAction(agent.cfg.actions.noop.int32)
356
- return
357
-
358
- setAction(action.int32)
359
-
360
- updateMap(agent, visible)
361
- agent.steps += 1
362
-
363
- # Track base infrastructure as soon as we see it and seed exploration around it
364
- let assemblerNearbyNow = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.assembler)
365
- if assemblerNearbyNow.isSome():
366
- let asmLoc = assemblerNearbyNow.get()
367
- agent.assemblerHome = some(asmLoc)
368
- if not agent.seenAssembler:
369
- agent.seenAssembler = true
370
- let keyLocations = [
371
- Location(x: -10, y: -10),
372
- Location(x: -10, y: +10),
373
- Location(x: +10, y: -10),
374
- Location(x: +10, y: +10),
375
- Location(x: -20, y: 0),
376
- Location(x: +20, y: 0),
377
- Location(x: 0, y: -20),
378
- Location(x: 0, y: +20),
379
- ]
380
- for keyLocation in keyLocations:
381
- let location = asmLoc + keyLocation
382
- if location notin agent.exploreLocations:
383
- agent.exploreLocations.add(location)
384
-
385
- let chestNearbyNow = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
386
- if chestNearbyNow.isSome():
387
- let chestLoc = chestNearbyNow.get()
388
- agent.chestHome = some(chestLoc)
389
- if not agent.seenChest:
390
- agent.seenChest = true
391
- let keyLocations = [
392
- Location(x: -3, y: 0),
393
- Location(x: 0, y: +3),
394
- Location(x: +3, y: 0),
395
- Location(x: 0, y: -3),
396
- Location(x: -6, y: 0),
397
- Location(x: 0, y: +6),
398
- Location(x: +6, y: 0),
399
- Location(x: 0, y: -6),
400
- ]
401
- for keyLocation in keyLocations:
402
- let location = chestLoc + keyLocation
403
- if location notin agent.exploreLocations:
404
- agent.exploreLocations.add(location)
405
-
406
- let
407
- vibe = agent.cfg.getVibe(visible, Location(x: 0, y: 0))
408
- invEnergy = agent.cfg.getInventory(visible, agent.cfg.features.invEnergy)
409
- invCarbon = agent.cfg.getInventory(visible, agent.cfg.features.invCarbon)
410
- invOxygen = agent.cfg.getInventory(visible, agent.cfg.features.invOxygen)
411
- invGermanium = agent.cfg.getInventory(visible, agent.cfg.features.invGermanium)
412
- invSilicon = agent.cfg.getInventory(visible, agent.cfg.features.invSilicon)
413
- invHeart = agent.cfg.getInventory(visible, agent.cfg.features.invHeart)
414
- invDecoder = agent.cfg.getInventory(visible, agent.cfg.features.invDecoder)
415
- invModulator = agent.cfg.getInventory(visible, agent.cfg.features.invModulator)
416
- invResonator = agent.cfg.getInventory(visible, agent.cfg.features.invResonator)
417
- invScrambler = agent.cfg.getInventory(visible, agent.cfg.features.invScrambler)
418
- completionPct = agent.cfg.getInventory(visible, agent.cfg.features.episodeCompletionPct)
419
-
420
- var stepsRemaining = max(0, agent.maxSteps - agent.steps)
421
- if completionPct > 0 and completionPct < 100:
422
- # Estimate total steps from observed completion percentage.
423
- let estTotal = int((agent.steps * 100) div completionPct)
424
- stepsRemaining = max(0, estTotal - agent.steps)
425
-
426
- log &"vibe:{vibe} H:{invHeart} E:{invEnergy} C:{invCarbon} O2:{invOxygen} Ge:{invGermanium} Si:{invSilicon} D:{invDecoder} M:{invModulator} R:{invResonator} S:{invScrambler}"
427
-
428
- let activeRecipe = agent.getActiveRecipe()
429
- log "active recipe: " & $activeRecipe
430
-
431
- # If only one resource is needed, make it the sole target to avoid wasted trips.
432
- var numNeeded = 0
433
- for cost in [activeRecipe.carbonCost, activeRecipe.oxygenCost, activeRecipe.germaniumCost, activeRecipe.siliconCost]:
434
- if cost > 0:
435
- inc numNeeded
436
-
437
- if activeRecipe.pattern.len > 0:
438
- # Split the cost evenly across the agents.
439
- proc divUp(a, b: int): int =
440
- ## Like div, but rounds up instead of down.
441
- let extra = if a mod b > 0: 1 else: 0
442
- return a div b + extra
443
- agent.carbonTarget = max(agent.carbonTarget, activeRecipe.carbonCost.divUp(activeRecipe.pattern.len))
444
- agent.oxygenTarget = max(agent.oxygenTarget, activeRecipe.oxygenCost.divUp(activeRecipe.pattern.len))
445
- agent.germaniumTarget = max(agent.germaniumTarget, activeRecipe.germaniumCost.divUp(activeRecipe.pattern.len))
446
- agent.siliconTarget = max(agent.siliconTarget, activeRecipe.siliconCost.divUp(activeRecipe.pattern.len))
447
- else:
448
- agent.carbonTarget = max(agent.carbonTarget, activeRecipe.carbonCost)
449
- agent.oxygenTarget = max(agent.oxygenTarget, activeRecipe.oxygenCost)
450
- agent.germaniumTarget = max(agent.germaniumTarget, activeRecipe.germaniumCost)
451
- agent.siliconTarget = max(agent.siliconTarget, activeRecipe.siliconCost)
452
-
453
- if numNeeded == 1:
454
- # Only keep the required resource; zero out others so we don't waste time or dump it.
455
- if activeRecipe.carbonCost > 0:
456
- agent.oxygenTarget = 0
457
- agent.germaniumTarget = 0
458
- agent.siliconTarget = 0
459
- elif activeRecipe.oxygenCost > 0:
460
- agent.carbonTarget = 0
461
- agent.germaniumTarget = 0
462
- agent.siliconTarget = 0
463
- elif activeRecipe.germaniumCost > 0:
464
- agent.carbonTarget = 0
465
- agent.oxygenTarget = 0
466
- agent.siliconTarget = 0
467
- elif activeRecipe.siliconCost > 0:
468
- agent.carbonTarget = 0
469
- agent.oxygenTarget = 0
470
- agent.germaniumTarget = 0
471
-
472
- # Vibe-check support: assign and enforce a required vibe from the assembler pattern.
473
- proc actionForVibe(cfg: Config, v: int): Option[int] =
474
- if v == cfg.vibes.default: return some(cfg.actions.vibeDefault)
475
- if v == cfg.vibes.charger: return some(cfg.actions.vibeCharger)
476
- if v == cfg.vibes.carbonA: return some(cfg.actions.vibeCarbonA)
477
- if v == cfg.vibes.carbonB: return some(cfg.actions.vibeCarbonB)
478
- if v == cfg.vibes.oxygenA: return some(cfg.actions.vibeOxygenA)
479
- if v == cfg.vibes.oxygenB: return some(cfg.actions.vibeOxygenB)
480
- if v == cfg.vibes.germaniumA: return some(cfg.actions.vibeGermaniumA)
481
- if v == cfg.vibes.germaniumB: return some(cfg.actions.vibeGermaniumB)
482
- if v == cfg.vibes.siliconA: return some(cfg.actions.vibeSiliconA)
483
- if v == cfg.vibes.siliconB: return some(cfg.actions.vibeSiliconB)
484
- if v == cfg.vibes.heartA: return some(cfg.actions.vibeHeartA)
485
- if v == cfg.vibes.heartB: return some(cfg.actions.vibeHeartB)
486
- if v == cfg.vibes.gear: return some(cfg.actions.vibeGear)
487
- if v == cfg.vibes.assembler: return some(cfg.actions.vibeAssembler)
488
- if v == cfg.vibes.chest: return some(cfg.actions.vibeChest)
489
- if v == cfg.vibes.wall: return some(cfg.actions.vibeWall)
490
- return none(int)
491
-
492
- template markPatternReapply(v: int) =
493
- if activeRecipe.pattern.len > 0 and agent.assignedVibe.isSome() and v != agent.assignedVibe.get():
494
- agent.needsPatternReapply = true
495
-
496
- if activeRecipe.pattern.len > 0:
497
- # Assign required vibe: if more agents than vibes, duplicate the first vibe to keep full coverage.
498
- var haveAssigned = false
499
- if agent.assignedVibe.isSome():
500
- for v in activeRecipe.pattern:
501
- if v == agent.assignedVibe.get():
502
- haveAssigned = true
503
- break
504
- if not haveAssigned:
505
- let idx = if agent.agentId < activeRecipe.pattern.len: agent.agentId else: 0
506
- agent.assignedVibe = some(activeRecipe.pattern[idx])
507
-
508
- proc patternSatisfied(agent: RaceCarAgent): bool =
509
- # Check if all required vibes are present around the assembler location we know.
510
- if agent.assemblerHome.isNone():
511
- return false
512
- let asmLoc = agent.assemblerHome.get()
513
- var seenVibes: HashSet[int]
514
- for offset in Offsets8:
515
- let loc = asmLoc + offset
516
- let v = agent.cfg.getVibe(agent.map, loc)
517
- if v != -1:
518
- seenVibes.incl(v)
519
- for v in activeRecipe.pattern:
520
- if v notin seenVibes:
521
- return false
522
- return true
523
-
524
- let desiredVibe = agent.assignedVibe.get()
525
- if not patternSatisfied(agent) and (vibe != desiredVibe or agent.needsPatternReapply):
526
- let vibeAction = actionForVibe(agent.cfg, desiredVibe)
527
- if vibeAction.isSome():
528
- doAction(vibeAction.get().int32)
529
- log "setting vibe to match assembler pattern: " & $desiredVibe
530
- agent.needsPatternReapply = false
531
- return
532
-
533
- # Are we running low on energy?
534
- if invEnergy < MaxEnergy div 4:
535
- let chargerNearby = agent.cfg.getNearbyExtractor(agent.location, agent.map, agent.cfg.tags.charger)
536
- if chargerNearby.isSome():
537
- measurePush("charger nearby")
538
- let action = agent.cfg.aStar(agent.location, chargerNearby.get(), agent.map)
539
- measurePop()
540
- if action.isSome():
541
- doAction(action.get().int32)
542
- log "going to charger"
543
- return
544
-
545
- # Charge opportunistically, with a slightly higher margin for hard energy maps.
546
- let opportunisticMargin = if stepsRemaining < 200: MaxEnergy - 30 else: MaxEnergy - 20
547
- if invEnergy < opportunisticMargin:
548
- let chargerNearby = agent.cfg.getNearbyExtractor(agent.location, agent.map, agent.cfg.tags.charger)
549
- if chargerNearby.isSome():
550
- if manhattan(agent.location, chargerNearby.get()) < 2:
551
- measurePush("charge nearby")
552
- let action = agent.cfg.aStar(agent.location, chargerNearby.get(), agent.map)
553
- measurePop()
554
- if action.isSome():
555
- doAction(action.get().int32)
556
- log "charge nearby might as well charge"
557
- return
558
-
559
- # Deposit heart into the chest.
560
- if invHeart > 0:
561
-
562
- # Reset the targets when we deposit hearts.
563
- log "depositing hearts"
564
- agent.carbonTarget = PutCarbonAmount
565
- agent.oxygenTarget = PutOxygenAmount
566
- agent.germaniumTarget = PutGermaniumAmount
567
- agent.siliconTarget = PutSiliconAmount
568
-
569
- let depositAction = agent.cfg.actions.vibeHeartB
570
- let depositVibe = agent.cfg.vibes.heartB
571
- if depositAction != 0 and vibe != depositVibe:
572
- doAction(depositAction.int32)
573
- markPatternReapply(depositVibe)
574
- return
575
- let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
576
- if chestNearby.isSome():
577
- measurePush("chest nearby")
578
- let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
579
- measurePop()
580
- if action.isSome():
581
- doAction(action.get().int32)
582
- log "going to chest"
583
- return
584
-
585
- # Build a heart when we have the full recipe.
586
- if invCarbon >= agent.carbonTarget and invOxygen >= agent.oxygenTarget and invGermanium >= agent.germaniumTarget and invSilicon >= agent.siliconTarget:
587
- log "trying to build a heart"
588
-
589
- if vibe != agent.cfg.vibes.heartA:
590
- doAction(agent.cfg.actions.vibeHeartA.int32)
591
- markPatternReapply(agent.cfg.vibes.heartA)
592
- log "vibing heart for assembler"
593
- return
594
-
595
- let assemblerNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.assembler)
596
- if assemblerNearby.isSome():
597
- measurePush("assembler nearby")
598
- let action = agent.cfg.aStar(agent.location, assemblerNearby.get(), agent.map)
599
- measurePop()
600
- if action.isSome():
601
- doAction(action.get().int32)
602
- log "going to assembler to build heart"
603
- return
604
-
605
- # Dump excess resources.
606
- let atMaxInventory = invCarbon + invOxygen + invGermanium + invSilicon >= MaxResourceInventory
607
- let avgResource = (invCarbon + invOxygen + invGermanium + invSilicon) div 4
608
- if atMaxInventory:
609
- log "at max inventory"
610
-
611
- if atMaxInventory and invCarbon > avgResource and invCarbon > agent.carbonTarget + PutCarbonAmount and agent.carbonTarget == 0:
612
- let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
613
- if chestNearby.isSome():
614
- if vibe != agent.cfg.vibes.carbonB:
615
- doAction(agent.cfg.actions.vibeCarbonB.int32)
616
- markPatternReapply(agent.cfg.vibes.carbonB)
617
- log "vibing carbon B to dump excess carbon"
618
- return
619
- measurePush("chest nearby excess carbon")
620
- let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
621
- measurePop()
622
- if action.isSome():
623
- doAction(action.get().int32)
624
- log "going to chest to dump excess carbon"
625
- return
626
-
627
- if atMaxInventory and invSilicon > avgResource and invSilicon > agent.siliconTarget + PutSiliconAmount and agent.siliconTarget == 0:
628
- let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
629
- if chestNearby.isSome():
630
- if vibe != agent.cfg.vibes.siliconB:
631
- doAction(agent.cfg.actions.vibeSiliconB.int32)
632
- markPatternReapply(agent.cfg.vibes.siliconB)
633
- log "vibing silicon B to dump excess silicon"
634
- return
635
- let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
636
- if action.isSome():
637
- doAction(action.get().int32)
638
- log "going to chest to dump excess silicon"
639
- return
640
-
641
- if atMaxInventory and invOxygen > avgResource and invOxygen > agent.oxygenTarget + PutOxygenAmount and agent.oxygenTarget == 0:
642
- let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
643
- if chestNearby.isSome():
644
- if vibe != agent.cfg.vibes.oxygenB:
645
- doAction(agent.cfg.actions.vibeOxygenB.int32)
646
- markPatternReapply(agent.cfg.vibes.oxygenB)
647
- log "vibing oxygen B to dump excess oxygen"
648
- return
649
- measurePush("chest nearby excess oxygen")
650
- let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
651
- measurePop()
652
- if action.isSome():
653
- doAction(action.get().int32)
654
- log "going to chest to dump excess oxygen"
655
- return
656
-
657
- if atMaxInventory and invGermanium > avgResource and invGermanium > agent.germaniumTarget + PutGermaniumAmount and agent.germaniumTarget == 0:
658
- let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
659
- if chestNearby.isSome():
660
- if vibe != agent.cfg.vibes.germaniumB:
661
- doAction(agent.cfg.actions.vibeGermaniumB.int32)
662
- markPatternReapply(agent.cfg.vibes.germaniumB)
663
- log "vibing germanium B to dump excess germanium"
664
- return
665
- measurePush("chest nearby excess germanium")
666
- let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
667
- measurePop()
668
- if action.isSome():
669
- doAction(action.get().int32)
670
- log "going to chest to dump excess germanium"
671
- return
672
-
673
- # Distance-aware late return: aim to arrive before the episode ends.
674
- if agent.assemblerHome.isSome():
675
- let distAsm = manhattan(agent.location, agent.assemblerHome.get())
676
- let buffer = 30
677
- if stepsRemaining <= distAsm + buffer:
678
- if invHeart > 0:
679
- let target = if agent.chestHome.isSome(): agent.chestHome.get() else: agent.assemblerHome.get()
680
- let action = agent.cfg.aStar(agent.location, target, agent.map)
681
- if action.isSome():
682
- doAction(action.get().int32)
683
- log "late return: delivering hearts"
684
- return
685
- elif invCarbon + invOxygen + invGermanium + invSilicon > 0:
686
- let action = agent.cfg.aStar(agent.location, agent.assemblerHome.get(), agent.map)
687
- if action.isSome():
688
- doAction(action.get().int32)
689
- log "late return: heading to assembler with resources"
690
- return
691
-
692
- proc findAndTakeResource(
693
- agent: RaceCarAgent,
694
- vibe: int,
695
- resource: int,
696
- target: int,
697
- inventory: int,
698
- vibeGetResource: int,
699
- vibeAction: int,
700
- extractorTag: int,
701
- name: string
702
- ): bool {.measure.} =
703
- # Check the chest.
704
- var closeChest = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
705
- if closeChest.isSome():
706
- # Does it have the resources we need?
707
- let chestInventory = agent.cfg.getInventory(agent.map, resource, closeChest.get())
708
- if chestInventory > 0:
709
- # Vibe the right resource to take from the chest.
710
- if vibe != vibeGetResource:
711
- doAction(vibeAction.int32)
712
- log "vibing " & name & " to take from chest"
713
- return true
714
- measurePush("chest nearby to take " & name)
715
- let action = agent.cfg.aStar(agent.location, closeChest.get(), agent.map)
716
- measurePop()
717
- if action.isSome():
718
- doAction(action.get().int32)
719
- log "going to chest to take " & name & " from chest"
720
- return true
721
-
722
- # Check the carbon extractor.
723
- let extractorNearby = agent.cfg.getNearbyExtractor(agent.location, agent.map, extractorTag)
724
- if extractorNearby.isSome() and extractorNearby.get() notin agent.unreachable and extractorNearby.get() notin agent.depleted:
725
- measurePush("extractor nearby to take " & name)
726
- let action = agent.cfg.aStar(agent.location, extractorNearby.get(), agent.map)
727
- measurePop()
728
- if action.isSome():
729
- doAction(action.get().int32)
730
- log "going to " & name & ", need: " & $target & " have: " & $inventory
731
- markPatternReapply(vibe)
732
- # If we arrive and see it depleted, mark it.
733
- if extractorNearby.get() in agent.depleted:
734
- agent.unreachable.incl(extractorNearby.get())
735
- return true
736
- else:
737
- agent.unreachable.incl(extractorNearby.get())
738
-
739
- # Is there carbon nearby?
740
- if agent.carbonTarget > 0 and invCarbon < agent.carbonTarget:
741
- if agent.findAndTakeResource(
742
- vibe,
743
- agent.cfg.features.invCarbon,
744
- agent.carbonTarget,
745
- invCarbon,
746
- agent.cfg.vibes.carbonA,
747
- agent.cfg.actions.vibeCarbonA,
748
- agent.cfg.tags.carbonExtractor,
749
- "carbon"
750
- ):
751
- return
752
-
753
- # Is there silicon nearby?
754
- if agent.siliconTarget > 0 and invSilicon < agent.siliconTarget:
755
- if agent.findAndTakeResource(
756
- vibe,
757
- agent.cfg.features.invSilicon,
758
- agent.siliconTarget,
759
- invSilicon,
760
- agent.cfg.vibes.siliconA,
761
- agent.cfg.actions.vibeSiliconA,
762
- agent.cfg.tags.siliconExtractor,
763
- "silicon"
764
- ):
765
- return
766
-
767
- # Is there oxygen nearby?
768
- if agent.oxygenTarget > 0 and invOxygen < agent.oxygenTarget:
769
- if agent.findAndTakeResource(
770
- vibe,
771
- agent.cfg.features.invOxygen,
772
- agent.oxygenTarget,
773
- invOxygen,
774
- agent.cfg.vibes.oxygenA,
775
- agent.cfg.actions.vibeOxygenA,
776
- agent.cfg.tags.oxygenExtractor,
777
- "oxygen"
778
- ):
779
- return
780
-
781
- # Is there germanium nearby?
782
- if agent.germaniumTarget > 0 and invGermanium < agent.germaniumTarget:
783
- if agent.findAndTakeResource(
784
- vibe,
785
- agent.cfg.features.invGermanium,
786
- agent.germaniumTarget,
787
- invGermanium,
788
- agent.cfg.vibes.germaniumA,
789
- agent.cfg.actions.vibeGermaniumA,
790
- agent.cfg.tags.germaniumExtractor,
791
- "germanium"
792
- ):
793
- return
794
-
795
- # Do Exploration.
796
- if agent.exploreLocations.len == 0:
797
- measurePush("exploration")
798
- var locationFound = false
799
- var unexploredLocation: Location
800
- var visited: HashSet[Location]
801
- block exploration:
802
- var seedLocation = agent.location
803
- let assemblerNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.assembler)
804
- if assemblerNearby.isSome():
805
- seedLocation = assemblerNearby.get()
806
- var queue: Deque[Location]
807
- queue.addLast(seedLocation)
808
- visited.incl(seedLocation)
809
- while queue.len > 0:
810
- let location = queue.popLast()
811
- if location notin agent.seen:
812
- locationFound = true
813
- unexploredLocation = location
814
- break exploration
815
- for i, offset in Offsets4:
816
- let neighbor = location + offset
817
- # passable check
818
- if agent.cfg.isWalkable(agent.map, neighbor):
819
- if neighbor notin visited:
820
- visited.incl(neighbor)
821
- queue.addLast(neighbor)
822
- if locationFound:
823
- agent.exploreLocations.add(unexploredLocation)
824
- else:
825
- log "no unseen location found"
826
- # agent.cfg.drawMap(agent.map, visited)
827
- measurePop()
828
-
829
- measurePush("explore locations")
830
- log "explore locations: " & $agent.exploreLocations
831
- if agent.exploreLocations.len > 0:
832
- for location in agent.exploreLocations:
833
- if location notin agent.seen:
834
- let action = agent.cfg.aStar(agent.location, location, agent.map)
835
- if action.isSome():
836
- doAction(action.get().int32)
837
- log "going to explore location: " & $location
838
- return
839
- else:
840
- agent.exploreLocations.remove(location)
841
- break
842
- else:
843
- agent.exploreLocations.remove(location)
844
- break
845
- measurePop()
846
-
847
- # If all else fails, take a random move to explore the map or get unstuck.
848
- let action = agent.random.rand(1 .. 4).int32
849
- log "taking random action " & $action
850
- doAction(action.int32)
851
-
852
- except:
853
- echo getCurrentException().getStackTrace()
854
- echo getCurrentExceptionMsg()
855
- quit()
856
-
857
- proc newRaceCarPolicy*(environmentConfig: string): RaceCarPolicy =
858
- let cfg = parseConfig(environmentConfig)
859
- var agents: seq[RaceCarAgent] = @[]
860
- for id in 0 ..< cfg.config.numAgents:
861
- agents.add(newRaceCarAgent(id, environmentConfig))
862
- return RaceCarPolicy(agents: agents)
863
-
864
- proc stepBatch*(
865
- policy: RaceCarPolicy,
866
- agentIds: pointer,
867
- numAgentIds: int,
868
- numAgents: int,
869
- numTokens: int,
870
- sizeToken: int,
871
- rawObservations: pointer,
872
- numActions: int,
873
- rawActions: pointer
874
- ) {.measure.} =
875
- let ids = cast[ptr UncheckedArray[int32]](agentIds)
876
- let obsArray = cast[ptr UncheckedArray[uint8]](rawObservations)
877
- let actionArray = cast[ptr UncheckedArray[int32]](rawActions)
878
- let obsStride = numTokens * sizeToken
879
-
880
- for i in 0 ..< numAgentIds:
881
- let idx = int(ids[i])
882
- let obsPtr = cast[pointer](obsArray[idx * obsStride].addr)
883
- let actPtr = cast[ptr int32](actionArray[idx].addr)
884
- step(policy.agents[idx], numAgents, numTokens, sizeToken, obsPtr, numActions, actPtr)