cogames-agents 0.0.0.7__cp312-cp312-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. cogames_agents/__init__.py +0 -0
  2. cogames_agents/evals/__init__.py +5 -0
  3. cogames_agents/evals/planky_evals.py +415 -0
  4. cogames_agents/policy/__init__.py +0 -0
  5. cogames_agents/policy/evolution/__init__.py +0 -0
  6. cogames_agents/policy/evolution/cogsguard/__init__.py +0 -0
  7. cogames_agents/policy/evolution/cogsguard/evolution.py +695 -0
  8. cogames_agents/policy/evolution/cogsguard/evolutionary_coordinator.py +540 -0
  9. cogames_agents/policy/nim_agents/__init__.py +20 -0
  10. cogames_agents/policy/nim_agents/agents.py +98 -0
  11. cogames_agents/policy/nim_agents/bindings/generated/libnim_agents.dylib +0 -0
  12. cogames_agents/policy/nim_agents/bindings/generated/nim_agents.py +215 -0
  13. cogames_agents/policy/nim_agents/cogsguard_agents.nim +555 -0
  14. cogames_agents/policy/nim_agents/cogsguard_align_all_agents.nim +569 -0
  15. cogames_agents/policy/nim_agents/common.nim +1054 -0
  16. cogames_agents/policy/nim_agents/install.sh +1 -0
  17. cogames_agents/policy/nim_agents/ladybug_agent.nim +954 -0
  18. cogames_agents/policy/nim_agents/nim_agents.nim +68 -0
  19. cogames_agents/policy/nim_agents/nim_agents.nims +14 -0
  20. cogames_agents/policy/nim_agents/nimby.lock +3 -0
  21. cogames_agents/policy/nim_agents/racecar_agents.nim +844 -0
  22. cogames_agents/policy/nim_agents/random_agents.nim +68 -0
  23. cogames_agents/policy/nim_agents/test_agents.py +53 -0
  24. cogames_agents/policy/nim_agents/thinky_agents.nim +677 -0
  25. cogames_agents/policy/nim_agents/thinky_eval.py +230 -0
  26. cogames_agents/policy/scripted_agent/README.md +360 -0
  27. cogames_agents/policy/scripted_agent/__init__.py +0 -0
  28. cogames_agents/policy/scripted_agent/baseline_agent.py +1031 -0
  29. cogames_agents/policy/scripted_agent/cogas/__init__.py +5 -0
  30. cogames_agents/policy/scripted_agent/cogas/context.py +68 -0
  31. cogames_agents/policy/scripted_agent/cogas/entity_map.py +152 -0
  32. cogames_agents/policy/scripted_agent/cogas/goal.py +115 -0
  33. cogames_agents/policy/scripted_agent/cogas/goals/__init__.py +27 -0
  34. cogames_agents/policy/scripted_agent/cogas/goals/aligner.py +160 -0
  35. cogames_agents/policy/scripted_agent/cogas/goals/gear.py +197 -0
  36. cogames_agents/policy/scripted_agent/cogas/goals/miner.py +441 -0
  37. cogames_agents/policy/scripted_agent/cogas/goals/scout.py +40 -0
  38. cogames_agents/policy/scripted_agent/cogas/goals/scrambler.py +174 -0
  39. cogames_agents/policy/scripted_agent/cogas/goals/shared.py +160 -0
  40. cogames_agents/policy/scripted_agent/cogas/goals/stem.py +60 -0
  41. cogames_agents/policy/scripted_agent/cogas/goals/survive.py +100 -0
  42. cogames_agents/policy/scripted_agent/cogas/navigator.py +401 -0
  43. cogames_agents/policy/scripted_agent/cogas/obs_parser.py +238 -0
  44. cogames_agents/policy/scripted_agent/cogas/policy.py +525 -0
  45. cogames_agents/policy/scripted_agent/cogas/trace.py +69 -0
  46. cogames_agents/policy/scripted_agent/cogsguard/CLAUDE.md +517 -0
  47. cogames_agents/policy/scripted_agent/cogsguard/README.md +252 -0
  48. cogames_agents/policy/scripted_agent/cogsguard/__init__.py +74 -0
  49. cogames_agents/policy/scripted_agent/cogsguard/aligned_junction_held_investigation.md +152 -0
  50. cogames_agents/policy/scripted_agent/cogsguard/aligner.py +333 -0
  51. cogames_agents/policy/scripted_agent/cogsguard/behavior_hooks.py +44 -0
  52. cogames_agents/policy/scripted_agent/cogsguard/control_agent.py +323 -0
  53. cogames_agents/policy/scripted_agent/cogsguard/debug_agent.py +533 -0
  54. cogames_agents/policy/scripted_agent/cogsguard/miner.py +589 -0
  55. cogames_agents/policy/scripted_agent/cogsguard/options.py +67 -0
  56. cogames_agents/policy/scripted_agent/cogsguard/parity_metrics.py +36 -0
  57. cogames_agents/policy/scripted_agent/cogsguard/policy.py +1967 -0
  58. cogames_agents/policy/scripted_agent/cogsguard/prereq_trace.py +33 -0
  59. cogames_agents/policy/scripted_agent/cogsguard/role_trace.py +50 -0
  60. cogames_agents/policy/scripted_agent/cogsguard/roles.py +31 -0
  61. cogames_agents/policy/scripted_agent/cogsguard/rollout_trace.py +40 -0
  62. cogames_agents/policy/scripted_agent/cogsguard/scout.py +69 -0
  63. cogames_agents/policy/scripted_agent/cogsguard/scrambler.py +350 -0
  64. cogames_agents/policy/scripted_agent/cogsguard/targeted_agent.py +418 -0
  65. cogames_agents/policy/scripted_agent/cogsguard/teacher.py +224 -0
  66. cogames_agents/policy/scripted_agent/cogsguard/types.py +381 -0
  67. cogames_agents/policy/scripted_agent/cogsguard/v2_agent.py +49 -0
  68. cogames_agents/policy/scripted_agent/common/__init__.py +0 -0
  69. cogames_agents/policy/scripted_agent/common/geometry.py +24 -0
  70. cogames_agents/policy/scripted_agent/common/roles.py +34 -0
  71. cogames_agents/policy/scripted_agent/common/tag_utils.py +48 -0
  72. cogames_agents/policy/scripted_agent/demo_policy.py +242 -0
  73. cogames_agents/policy/scripted_agent/pathfinding.py +126 -0
  74. cogames_agents/policy/scripted_agent/pinky/DESIGN.md +317 -0
  75. cogames_agents/policy/scripted_agent/pinky/__init__.py +5 -0
  76. cogames_agents/policy/scripted_agent/pinky/behaviors/__init__.py +17 -0
  77. cogames_agents/policy/scripted_agent/pinky/behaviors/aligner.py +400 -0
  78. cogames_agents/policy/scripted_agent/pinky/behaviors/base.py +119 -0
  79. cogames_agents/policy/scripted_agent/pinky/behaviors/miner.py +632 -0
  80. cogames_agents/policy/scripted_agent/pinky/behaviors/scout.py +138 -0
  81. cogames_agents/policy/scripted_agent/pinky/behaviors/scrambler.py +433 -0
  82. cogames_agents/policy/scripted_agent/pinky/policy.py +570 -0
  83. cogames_agents/policy/scripted_agent/pinky/services/__init__.py +7 -0
  84. cogames_agents/policy/scripted_agent/pinky/services/map_tracker.py +808 -0
  85. cogames_agents/policy/scripted_agent/pinky/services/navigator.py +864 -0
  86. cogames_agents/policy/scripted_agent/pinky/services/safety.py +189 -0
  87. cogames_agents/policy/scripted_agent/pinky/state.py +299 -0
  88. cogames_agents/policy/scripted_agent/pinky/types.py +138 -0
  89. cogames_agents/policy/scripted_agent/planky/CLAUDE.md +124 -0
  90. cogames_agents/policy/scripted_agent/planky/IMPROVEMENTS.md +160 -0
  91. cogames_agents/policy/scripted_agent/planky/NOTES.md +153 -0
  92. cogames_agents/policy/scripted_agent/planky/PLAN.md +254 -0
  93. cogames_agents/policy/scripted_agent/planky/README.md +214 -0
  94. cogames_agents/policy/scripted_agent/planky/STRATEGY.md +100 -0
  95. cogames_agents/policy/scripted_agent/planky/__init__.py +5 -0
  96. cogames_agents/policy/scripted_agent/planky/context.py +68 -0
  97. cogames_agents/policy/scripted_agent/planky/entity_map.py +152 -0
  98. cogames_agents/policy/scripted_agent/planky/goal.py +107 -0
  99. cogames_agents/policy/scripted_agent/planky/goals/__init__.py +27 -0
  100. cogames_agents/policy/scripted_agent/planky/goals/aligner.py +168 -0
  101. cogames_agents/policy/scripted_agent/planky/goals/gear.py +179 -0
  102. cogames_agents/policy/scripted_agent/planky/goals/miner.py +416 -0
  103. cogames_agents/policy/scripted_agent/planky/goals/scout.py +40 -0
  104. cogames_agents/policy/scripted_agent/planky/goals/scrambler.py +174 -0
  105. cogames_agents/policy/scripted_agent/planky/goals/shared.py +160 -0
  106. cogames_agents/policy/scripted_agent/planky/goals/stem.py +49 -0
  107. cogames_agents/policy/scripted_agent/planky/goals/survive.py +96 -0
  108. cogames_agents/policy/scripted_agent/planky/navigator.py +388 -0
  109. cogames_agents/policy/scripted_agent/planky/obs_parser.py +238 -0
  110. cogames_agents/policy/scripted_agent/planky/policy.py +485 -0
  111. cogames_agents/policy/scripted_agent/planky/tests/__init__.py +0 -0
  112. cogames_agents/policy/scripted_agent/planky/tests/conftest.py +66 -0
  113. cogames_agents/policy/scripted_agent/planky/tests/helpers.py +152 -0
  114. cogames_agents/policy/scripted_agent/planky/tests/test_aligner.py +24 -0
  115. cogames_agents/policy/scripted_agent/planky/tests/test_miner.py +30 -0
  116. cogames_agents/policy/scripted_agent/planky/tests/test_scout.py +15 -0
  117. cogames_agents/policy/scripted_agent/planky/tests/test_scrambler.py +29 -0
  118. cogames_agents/policy/scripted_agent/planky/tests/test_stem.py +36 -0
  119. cogames_agents/policy/scripted_agent/planky/trace.py +69 -0
  120. cogames_agents/policy/scripted_agent/types.py +239 -0
  121. cogames_agents/policy/scripted_agent/unclipping_agent.py +461 -0
  122. cogames_agents/policy/scripted_agent/utils.py +381 -0
  123. cogames_agents/policy/scripted_registry.py +80 -0
  124. cogames_agents/py.typed +0 -0
  125. cogames_agents-0.0.0.7.dist-info/METADATA +98 -0
  126. cogames_agents-0.0.0.7.dist-info/RECORD +128 -0
  127. cogames_agents-0.0.0.7.dist-info/WHEEL +6 -0
  128. cogames_agents-0.0.0.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,677 @@
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
+
11
+ PutCarbonAmount = 10
12
+ PutOxygenAmount = 10
13
+ PutGermaniumAmount = 1
14
+ PutSiliconAmount = 25
15
+
16
+ type
17
+ ThinkyAgent* = ref object
18
+ agentId*: int
19
+
20
+ map: Table[Location, seq[FeatureValue]]
21
+ seen: HashSet[Location]
22
+ cfg: Config
23
+ random: Rand
24
+ location: Location
25
+ lastActions: seq[int]
26
+ recipes: seq[RecipeInfo]
27
+
28
+ carbonTarget: int
29
+ oxygenTarget: int
30
+ germaniumTarget: int
31
+ siliconTarget: int
32
+
33
+ bump: bool
34
+ offsets4: seq[Location] # 4 cardinal but random for each agent
35
+ seenHub: bool
36
+ seenChest: bool
37
+ exploreLocations: seq[Location]
38
+
39
+ ThinkyPolicy* = ref object
40
+ agents*: seq[ThinkyAgent]
41
+
42
+ proc log(message: string) =
43
+ when defined(debug):
44
+ echo message
45
+
46
+ const Offsets4 = [
47
+ Location(x: -1, y: +0),
48
+ Location(x: +0, y: +1),
49
+ Location(x: +0, y: -1),
50
+ Location(x: +1, y: +0),
51
+ ]
52
+
53
+ const Offsets8 = [
54
+ Location(x: +0, y: +0),
55
+ Location(x: +0, y: +1),
56
+ Location(x: +0, y: -1),
57
+ Location(x: +1, y: +0),
58
+ Location(x: +1, y: +1),
59
+ Location(x: +1, y: -1),
60
+ Location(x: -1, y: +0),
61
+ Location(x: -1, y: +1),
62
+ Location(x: -1, y: -1),
63
+ ]
64
+
65
+ proc getActiveRecipe(agent: ThinkyAgent): RecipeInfo {.measure.} =
66
+ ## Get the recipes form the hub protocol inputs.
67
+ let hubLocation = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.hub)
68
+ if hubLocation.isSome():
69
+ let location = hubLocation.get()
70
+ # Get the vibe key.
71
+ result.pattern = @[]
72
+ for offsets in Offsets8:
73
+ let vibeKey = agent.cfg.getVibe(agent.map, location + offsets)
74
+ if vibeKey != -1:
75
+ result.pattern.add(vibeKey)
76
+
77
+ # Get the required resources.
78
+ let hubFeatures = agent.map[location]
79
+ for feature in hubFeatures:
80
+ updateRecipeFromProtocol(agent.cfg, feature, result)
81
+
82
+ proc newThinkyAgent*(agentId: int, environmentConfig: string): ThinkyAgent =
83
+ ## Create a new thinky agent, the fastest and the smartest agent.
84
+
85
+ var config = parseConfig(environmentConfig)
86
+ result = ThinkyAgent(agentId: agentId, cfg: config)
87
+ result.random = initRand(agentId)
88
+ result.map = initTable[Location, seq[FeatureValue]]()
89
+ result.seen = initHashSet[Location]()
90
+ result.location = Location(x: 0, y: 0)
91
+ result.lastActions = @[]
92
+ # Randomize the offsets4 for each agent, so they take different directions.
93
+ var offsets4 = Offsets4
94
+ result.random.shuffle(offsets4)
95
+ for i in 0 ..< offsets4.len:
96
+ offsets4[i] = offsets4[i]
97
+
98
+ result.exploreLocations = @[
99
+ Location(x: -7, y: 0),
100
+ Location(x: 0, y: +7),
101
+ Location(x: +7, y: 0),
102
+ Location(x: 0, y: -7),
103
+ ]
104
+ result.random.shuffle(result.exploreLocations)
105
+
106
+ proc updateMap(agent: ThinkyAgent, visible: Table[Location, seq[FeatureValue]]) {.measure.} =
107
+ ## Update the big map with the small visible map.
108
+
109
+ if agent.map.len == 0:
110
+ # First time we're called, just copy the visible map to the big map.
111
+ agent.map = visible
112
+ agent.location = Location(x: 0, y: 0)
113
+ for x in -5 .. 5:
114
+ for y in -5 .. 5:
115
+ let visibleLocation = Location(x: x, y: y)
116
+ agent.seen.incl(visibleLocation)
117
+ return
118
+
119
+ var newLocation = agent.location
120
+ let lastAction = agent.cfg.getLastAction(visible)
121
+ if lastAction == agent.cfg.actions.moveNorth:
122
+ newLocation.y -= 1
123
+ elif lastAction == agent.cfg.actions.moveSouth:
124
+ newLocation.y += 1
125
+ elif lastAction == agent.cfg.actions.moveWest:
126
+ newLocation.x -= 1
127
+ elif lastAction == agent.cfg.actions.moveEast:
128
+ newLocation.x += 1
129
+
130
+ # Does the new location have force any tags to no be there?
131
+ # If so we did a bump instead of a move.
132
+ block bumpCheck:
133
+ agent.bump = false
134
+ for x in -5 .. 5:
135
+ for y in -5 .. 5:
136
+ let visibleLocation = Location(x: x, y: y)
137
+ let mapLocation = Location(x: x + newLocation.x, y: y + newLocation.y)
138
+ if mapLocation notin agent.seen:
139
+ continue
140
+ var visibleTag = agent.cfg.getTag(visible, visibleLocation)
141
+ if visibleTag == agent.cfg.tags.agent:
142
+ # Ignore agents.
143
+ visibleTag = -1
144
+ var mapTag = agent.cfg.getTag(agent.map, mapLocation)
145
+ if mapTag == agent.cfg.tags.agent:
146
+ # Ignore agents.
147
+ mapTag = -1
148
+ if visibleTag != mapTag:
149
+ newLocation = agent.location
150
+ agent.bump = true
151
+ break bumpCheck
152
+
153
+ # Update the seen set.
154
+ agent.location = newLocation
155
+ for x in -5 .. 5:
156
+ for y in -5 .. 5:
157
+ let visibleLocation = Location(x: x, y: y)
158
+ let mapLocation = Location(x: x + agent.location.x, y: y + agent.location.y)
159
+ if visibleLocation in visible:
160
+ agent.map[mapLocation] = visible[visibleLocation]
161
+ else:
162
+ agent.map[mapLocation] = @[]
163
+ agent.seen.incl(mapLocation)
164
+
165
+ # agent.cfg.drawMap(agent.map, agent.seen)
166
+
167
+ proc getNumAgentsNearby*(
168
+ cfg: Config,
169
+ location: Location,
170
+ map: Table[Location, seq[FeatureValue]]
171
+ ): int {.measure.} =
172
+ ## Get the number of agents nearby.
173
+ for offset in Offsets8:
174
+ let at = location + offset
175
+ if at in map:
176
+ for featureValue in map[at]:
177
+ if featureValue.featureId == cfg.features.group:
178
+ result += 1
179
+ break
180
+
181
+ proc getNearbyExtractor*(
182
+ cfg: Config,
183
+ currentLocation: Location,
184
+ map: Table[Location, seq[FeatureValue]],
185
+ tagId: int
186
+ ): Option[Location] =
187
+ ## Get if there is a nearby location with the given tag.
188
+ var
189
+ found = false
190
+ closestLocation = Location(x: 0, y: 0)
191
+ closestDistance = 9999
192
+ for location, featureValues in map:
193
+ for featureValue in featureValues:
194
+ if featureValue.featureId == cfg.features.tag and featureValue.value == tagId:
195
+ let agentsNearby = cfg.getNumAgentsNearby(location, map)
196
+ if agentsNearby > 1:
197
+ continue
198
+ var skip = false
199
+ for f in map[location]:
200
+ if f.featureId == cfg.features.remainingUses and f.value == 0:
201
+ skip = true
202
+ break
203
+ # if f.featureId == cfg.features.cooldownRemaining and f.value > 50:
204
+ # skip = true
205
+ # break
206
+ if skip:
207
+ continue
208
+ let distance = manhattan(location, currentLocation)
209
+ if distance < closestDistance:
210
+ closestDistance = distance
211
+ closestLocation = location
212
+ found = true
213
+ if found:
214
+ return some(closestLocation)
215
+ return none(Location)
216
+
217
+ proc step*(
218
+ agent: ThinkyAgent,
219
+ numAgents: int,
220
+ numTokens: int,
221
+ sizeToken: int,
222
+ rawObservation: pointer,
223
+ numActions: int,
224
+ agentAction: ptr int32
225
+ ) {.measure.} =
226
+ try:
227
+
228
+ let observations = cast[ptr UncheckedArray[uint8]](rawObservation)
229
+
230
+ # Parse the tokens into a vision map.
231
+ var visible: Table[Location, seq[FeatureValue]]
232
+ for token in 0 ..< numTokens:
233
+ let locationPacked = observations[token * sizeToken]
234
+ let featureId = observations[token * sizeToken + 1]
235
+ let value = observations[token * sizeToken + 2]
236
+ if locationPacked == 255 and featureId == 255 and value == 255:
237
+ break
238
+ var location: Location
239
+ if locationPacked != 0xFF:
240
+ location.y = (locationPacked shr 4).int - 5
241
+ location.x = (locationPacked and 0x0F).int - 5
242
+ if location notin visible:
243
+ visible[location] = @[]
244
+ visible[location].add(FeatureValue(featureId: featureId.int, value: value.int))
245
+
246
+ proc doAction(action: int) {.measure.} =
247
+
248
+ # Get last action from observations, in case something else moved us.
249
+ agent.lastActions.add(agent.cfg.getLastAction(visible))
250
+
251
+ # Stuck prevention: if last 2 actions are left, right and this is left.
252
+ if agent.lastActions.len >= 2 and
253
+ action == agent.cfg.actions.moveWest and
254
+ agent.lastActions[^1] == agent.cfg.actions.moveEast and
255
+ agent.lastActions[^2] == agent.cfg.actions.moveWest:
256
+ # Noop 50% of the time.
257
+ if agent.random.rand(1 .. 2) == 1:
258
+ log "Stuck prevention: left, right, left"
259
+ doAction(agent.cfg.actions.noop.int32)
260
+ return
261
+ if agent.lastActions.len >= 2 and
262
+ action == agent.cfg.actions.moveEast and
263
+ agent.lastActions[^1] == agent.cfg.actions.moveWest and
264
+ agent.lastActions[^2] == agent.cfg.actions.moveEast:
265
+ # Noop 50% of the time.
266
+ if agent.random.rand(1 .. 2) == 1:
267
+ log "Stuck prevention: right, left, right"
268
+ doAction(agent.cfg.actions.noop.int32)
269
+ return
270
+ if agent.lastActions.len >= 2 and
271
+ action == agent.cfg.actions.moveNorth and
272
+ agent.lastActions[^1] == agent.cfg.actions.moveSouth and
273
+ agent.lastActions[^2] == agent.cfg.actions.moveNorth:
274
+ # Noop 50% of the time.
275
+ if agent.random.rand(1 .. 2) == 1:
276
+ log "Stuck prevention: north, south, north"
277
+ doAction(agent.cfg.actions.noop.int32)
278
+ return
279
+ if agent.lastActions.len >= 2 and
280
+ action == agent.cfg.actions.moveSouth and
281
+ agent.lastActions[^1] == agent.cfg.actions.moveNorth and
282
+ agent.lastActions[^2] == agent.cfg.actions.moveSouth:
283
+ # Noop 50% of the time.
284
+ if agent.random.rand(1 .. 2) == 1:
285
+ log "Stuck prevention: south, north, south"
286
+ doAction(agent.cfg.actions.noop.int32)
287
+ return
288
+
289
+ agentAction[] = action.int32
290
+
291
+ updateMap(agent, visible)
292
+
293
+ let
294
+ vibe = agent.cfg.getVibe(visible, Location(x: 0, y: 0))
295
+ invEnergy = agent.cfg.getInventory(visible, agent.cfg.features.invEnergy)
296
+ invCarbon = agent.cfg.getInventory(visible, agent.cfg.features.invCarbon)
297
+ invOxygen = agent.cfg.getInventory(visible, agent.cfg.features.invOxygen)
298
+ invGermanium = agent.cfg.getInventory(visible, agent.cfg.features.invGermanium)
299
+ invSilicon = agent.cfg.getInventory(visible, agent.cfg.features.invSilicon)
300
+ invHeart = agent.cfg.getInventory(visible, agent.cfg.features.invHeart)
301
+ invDecoder = agent.cfg.getInventory(visible, agent.cfg.features.invDecoder)
302
+ invModulator = agent.cfg.getInventory(visible, agent.cfg.features.invModulator)
303
+ invResonator = agent.cfg.getInventory(visible, agent.cfg.features.invResonator)
304
+ invScrambler = agent.cfg.getInventory(visible, agent.cfg.features.invScrambler)
305
+
306
+ log &"vibe:{vibe} H:{invHeart} E:{invEnergy} C:{invCarbon} O2:{invOxygen} Ge:{invGermanium} Si:{invSilicon} D:{invDecoder} M:{invModulator} R:{invResonator} S:{invScrambler}"
307
+
308
+ let activeRecipe = agent.getActiveRecipe()
309
+ log "active recipe: " & $activeRecipe
310
+
311
+ if activeRecipe.pattern.len > 0:
312
+ # Split the cost evenly across the agents.
313
+ proc divUp(a, b: int): int =
314
+ ## Like div, but rounds up instead of down.
315
+ let extra = if a mod b > 0: 1 else: 0
316
+ return a div b + extra
317
+ agent.carbonTarget = max(agent.carbonTarget, activeRecipe.carbonCost.divUp(activeRecipe.pattern.len))
318
+ agent.oxygenTarget = max(agent.oxygenTarget, activeRecipe.oxygenCost.divUp(activeRecipe.pattern.len))
319
+ agent.germaniumTarget = max(agent.germaniumTarget, activeRecipe.germaniumCost.divUp(activeRecipe.pattern.len))
320
+ agent.siliconTarget = max(agent.siliconTarget, activeRecipe.siliconCost.divUp(activeRecipe.pattern.len))
321
+ else:
322
+ agent.carbonTarget = max(agent.carbonTarget, activeRecipe.carbonCost)
323
+ agent.oxygenTarget = max(agent.oxygenTarget, activeRecipe.oxygenCost)
324
+ agent.germaniumTarget = max(agent.germaniumTarget, activeRecipe.germaniumCost)
325
+ agent.siliconTarget = max(agent.siliconTarget, activeRecipe.siliconCost)
326
+
327
+ # Are we running low on energy?
328
+ if invEnergy < MaxEnergy div 4:
329
+ let junctionNearby = agent.cfg.getNearbyExtractor(agent.location, agent.map, agent.cfg.tags.junction)
330
+ if junctionNearby.isSome():
331
+ measurePush("junction nearby")
332
+ let action = agent.cfg.aStar(agent.location, junctionNearby.get(), agent.map)
333
+ measurePop()
334
+ if action.isSome():
335
+ doAction(action.get().int32)
336
+ log "going to junction"
337
+ return
338
+
339
+ # Charge opportunistically.
340
+ if invEnergy < MaxEnergy - 20:
341
+ let junctionNearby = agent.cfg.getNearbyExtractor(agent.location, agent.map, agent.cfg.tags.junction)
342
+ if junctionNearby.isSome():
343
+ if manhattan(agent.location, junctionNearby.get()) < 2:
344
+ measurePush("charge nearby")
345
+ let action = agent.cfg.aStar(agent.location, junctionNearby.get(), agent.map)
346
+ measurePop()
347
+ if action.isSome():
348
+ doAction(action.get().int32)
349
+ log "charge nearby might as well charge"
350
+ return
351
+
352
+ # Deposit heart into the chest.
353
+ if invHeart > 0:
354
+
355
+ # Reset the targets when we deposit hearts.
356
+ # Reset to 0 (not hardcoded defaults) so the recipe-reading logic
357
+ # can set correct targets from activeRecipe on the next cycle.
358
+ log "depositing hearts"
359
+ agent.carbonTarget = 0
360
+ agent.oxygenTarget = 0
361
+ agent.germaniumTarget = 0
362
+ agent.siliconTarget = 0
363
+
364
+ let depositAction = agent.cfg.actions.vibeHeartB
365
+ let depositVibe = agent.cfg.vibes.heartB
366
+ if depositAction != 0 and vibe != depositVibe:
367
+ doAction(depositAction.int32)
368
+ return
369
+ let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
370
+ if chestNearby.isSome():
371
+ measurePush("chest nearby")
372
+ let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
373
+ measurePop()
374
+ if action.isSome():
375
+ doAction(action.get().int32)
376
+ log "going to chest"
377
+ return
378
+
379
+ if invCarbon >= agent.carbonTarget and invOxygen >= agent.oxygenTarget and invGermanium >= agent.germaniumTarget and invSilicon >= agent.siliconTarget:
380
+ # We have all the resources we need, so we can build a heart.
381
+ log "trying to build a heart"
382
+
383
+ if vibe != agent.cfg.vibes.heartA:
384
+ doAction(agent.cfg.actions.vibeHeartA.int32)
385
+ log "vibing heart for hub"
386
+ return
387
+
388
+ let hubNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.hub)
389
+ if hubNearby.isSome():
390
+ measurePush("hub nearby")
391
+ let action = agent.cfg.aStar(agent.location, hubNearby.get(), agent.map)
392
+ measurePop()
393
+ if action.isSome():
394
+ doAction(action.get().int32)
395
+ log "going to hub to build heart"
396
+ return
397
+
398
+ # Dump excess resources.
399
+ let atMaxInventory = invCarbon + invOxygen + invGermanium + invSilicon >= MaxResourceInventory
400
+ let avgResource = (invCarbon + invOxygen + invGermanium + invSilicon) div 4
401
+ if atMaxInventory:
402
+ log "at max inventory"
403
+
404
+ if atMaxInventory and invCarbon > avgResource and invCarbon > agent.carbonTarget + PutCarbonAmount:
405
+ let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
406
+ if chestNearby.isSome():
407
+ if vibe != agent.cfg.vibes.carbonB:
408
+ doAction(agent.cfg.actions.vibeCarbonB.int32)
409
+ log "vibing carbon B to dump excess carbon"
410
+ return
411
+ measurePush("chest nearby excess carbon")
412
+ let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
413
+ measurePop()
414
+ if action.isSome():
415
+ doAction(action.get().int32)
416
+ log "going to chest to dump excess carbon"
417
+ return
418
+
419
+ if atMaxInventory and invSilicon > avgResource and invSilicon > agent.siliconTarget + PutSiliconAmount:
420
+ let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
421
+ if chestNearby.isSome():
422
+ if vibe != agent.cfg.vibes.siliconB:
423
+ doAction(agent.cfg.actions.vibeSiliconB.int32)
424
+ log "vibing silicon B to dump excess silicon"
425
+ return
426
+ let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
427
+ if action.isSome():
428
+ doAction(action.get().int32)
429
+ log "going to chest to dump excess silicon"
430
+ return
431
+
432
+ if atMaxInventory and invOxygen > avgResource and invOxygen > agent.oxygenTarget + PutOxygenAmount:
433
+ let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
434
+ if chestNearby.isSome():
435
+ if vibe != agent.cfg.vibes.oxygenB:
436
+ doAction(agent.cfg.actions.vibeOxygenB.int32)
437
+ log "vibing oxygen B to dump excess oxygen"
438
+ return
439
+ measurePush("chest nearby excess oxygen")
440
+ let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
441
+ measurePop()
442
+ if action.isSome():
443
+ doAction(action.get().int32)
444
+ log "going to chest to dump excess oxygen"
445
+ return
446
+
447
+ if atMaxInventory and invGermanium > avgResource and invGermanium > agent.germaniumTarget + PutGermaniumAmount:
448
+ let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
449
+ if chestNearby.isSome():
450
+ if vibe != agent.cfg.vibes.germaniumB:
451
+ doAction(agent.cfg.actions.vibeGermaniumB.int32)
452
+ log "vibing germanium B to dump excess germanium"
453
+ return
454
+ measurePush("chest nearby excess germanium")
455
+ let action = agent.cfg.aStar(agent.location, chestNearby.get(), agent.map)
456
+ measurePop()
457
+ if action.isSome():
458
+ doAction(action.get().int32)
459
+ log "going to chest to dump excess germanium"
460
+ return
461
+
462
+ proc findAndTakeResource(
463
+ agent: ThinkyAgent,
464
+ vibe: int,
465
+ resource: int,
466
+ target: int,
467
+ inventory: int,
468
+ vibeGetResource: int,
469
+ vibeAction: int,
470
+ extractorTag: int,
471
+ name: string
472
+ ): bool {.measure.} =
473
+ # Check the chest.
474
+ var closeChest = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
475
+ if closeChest.isSome():
476
+ # Does it have the resources we need?
477
+ let chestInventory = agent.cfg.getInventory(agent.map, resource, closeChest.get())
478
+ if chestInventory > 0:
479
+ # Vibe the right resource to take from the chest.
480
+ if vibe != vibeGetResource:
481
+ doAction(vibeAction.int32)
482
+ log "vibing " & name & " to take from chest"
483
+ return true
484
+ measurePush("chest nearby to take " & name)
485
+ let action = agent.cfg.aStar(agent.location, closeChest.get(), agent.map)
486
+ measurePop()
487
+ if action.isSome():
488
+ doAction(action.get().int32)
489
+ log "going to chest to take " & name & " from chest"
490
+ return true
491
+
492
+ # Check the carbon extractor.
493
+ let extractorNearby = agent.cfg.getNearbyExtractor(agent.location, agent.map, extractorTag)
494
+ if extractorNearby.isSome():
495
+ measurePush("extractor nearby to take " & name)
496
+ let action = agent.cfg.aStar(agent.location, extractorNearby.get(), agent.map)
497
+ measurePop()
498
+ if action.isSome():
499
+ doAction(action.get().int32)
500
+ log "going to " & name & ", need: " & $target & " have: " & $inventory
501
+ return true
502
+
503
+ # Is there carbon nearby?
504
+ if agent.carbonTarget > 0 and invCarbon < agent.carbonTarget:
505
+ if agent.findAndTakeResource(
506
+ vibe,
507
+ agent.cfg.features.invCarbon,
508
+ agent.carbonTarget,
509
+ invCarbon,
510
+ agent.cfg.vibes.carbonA,
511
+ agent.cfg.actions.vibeCarbonA,
512
+ agent.cfg.tags.carbonExtractor,
513
+ "carbon"
514
+ ):
515
+ return
516
+
517
+ # Is there silicon nearby?
518
+ if agent.siliconTarget > 0 and invSilicon < agent.siliconTarget:
519
+ if agent.findAndTakeResource(
520
+ vibe,
521
+ agent.cfg.features.invSilicon,
522
+ agent.siliconTarget,
523
+ invSilicon,
524
+ agent.cfg.vibes.siliconA,
525
+ agent.cfg.actions.vibeSiliconA,
526
+ agent.cfg.tags.siliconExtractor,
527
+ "silicon"
528
+ ):
529
+ return
530
+
531
+ # Is there oxygen nearby?
532
+ if agent.oxygenTarget > 0 and invOxygen < agent.oxygenTarget:
533
+ if agent.findAndTakeResource(
534
+ vibe,
535
+ agent.cfg.features.invOxygen,
536
+ agent.oxygenTarget,
537
+ invOxygen,
538
+ agent.cfg.vibes.oxygenA,
539
+ agent.cfg.actions.vibeOxygenA,
540
+ agent.cfg.tags.oxygenExtractor,
541
+ "oxygen"
542
+ ):
543
+ return
544
+
545
+ # Is there germanium nearby?
546
+ if agent.germaniumTarget > 0 and invGermanium < agent.germaniumTarget:
547
+ if agent.findAndTakeResource(
548
+ vibe,
549
+ agent.cfg.features.invGermanium,
550
+ agent.germaniumTarget,
551
+ invGermanium,
552
+ agent.cfg.vibes.germaniumA,
553
+ agent.cfg.actions.vibeGermaniumA,
554
+ agent.cfg.tags.germaniumExtractor,
555
+ "germanium"
556
+ ):
557
+ return
558
+
559
+ # Explore locations around the hub.
560
+ block:
561
+ if not agent.seenHub:
562
+ let hubNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.hub)
563
+ if hubNearby.isSome():
564
+ agent.seenHub = true
565
+ let keyLocations = [
566
+ Location(x: -10, y: -10),
567
+ Location(x: -10, y: +10),
568
+ Location(x: +10, y: -10),
569
+ Location(x: +10, y: +10),
570
+ ]
571
+ for keyLocation in keyLocations:
572
+ let location = hubNearby.get() + keyLocation
573
+ agent.exploreLocations.add(location)
574
+ if not agent.seenChest:
575
+ let chestNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.chest)
576
+ if chestNearby.isSome():
577
+ agent.seenChest = true
578
+ let keyLocations = [
579
+ Location(x: -3, y: 0),
580
+ Location(x: 0, y: +3),
581
+ Location(x: +3, y: 0),
582
+ Location(x: 0, y: -3),
583
+ ]
584
+ for keyLocation in keyLocations:
585
+ let location = chestNearby.get() + keyLocation
586
+ agent.exploreLocations.add(location)
587
+
588
+ # Do Exploration.
589
+ if agent.exploreLocations.len == 0:
590
+ measurePush("exploration")
591
+ var locationFound = false
592
+ var unexploredLocation: Location
593
+ var visited: HashSet[Location]
594
+ block exploration:
595
+ var seedLocation = agent.location
596
+ let hubNearby = agent.cfg.getNearby(agent.location, agent.map, agent.cfg.tags.hub)
597
+ if hubNearby.isSome():
598
+ seedLocation = hubNearby.get()
599
+ var queue: Deque[Location]
600
+ queue.addLast(seedLocation)
601
+ visited.incl(seedLocation)
602
+ while queue.len > 0:
603
+ let location = queue.popLast()
604
+ if location notin agent.seen:
605
+ locationFound = true
606
+ unexploredLocation = location
607
+ break exploration
608
+ for i, offset in Offsets4:
609
+ let neighbor = location + offset
610
+ # passable check
611
+ if agent.cfg.isWalkable(agent.map, neighbor):
612
+ if neighbor notin visited:
613
+ visited.incl(neighbor)
614
+ queue.addLast(neighbor)
615
+ if locationFound:
616
+ agent.exploreLocations.add(unexploredLocation)
617
+ else:
618
+ log "no unseen location found"
619
+ # agent.cfg.drawMap(agent.map, visited)
620
+ measurePop()
621
+
622
+ measurePush("explore locations")
623
+ log "explore locations: " & $agent.exploreLocations
624
+ if agent.exploreLocations.len > 0:
625
+ for location in agent.exploreLocations:
626
+ if location notin agent.seen:
627
+ let action = agent.cfg.aStar(agent.location, location, agent.map)
628
+ if action.isSome():
629
+ doAction(action.get().int32)
630
+ log "going to explore location: " & $location
631
+ return
632
+ else:
633
+ agent.exploreLocations.remove(location)
634
+ break
635
+ else:
636
+ agent.exploreLocations.remove(location)
637
+ break
638
+ measurePop()
639
+
640
+ # If all else fails, take a random move to explore the map or get unstuck.
641
+ let action = agent.random.rand(1 .. 4).int32
642
+ log "taking random action " & $action
643
+ doAction(action.int32)
644
+
645
+ except:
646
+ echo getCurrentException().getStackTrace()
647
+ echo getCurrentExceptionMsg()
648
+ quit()
649
+
650
+ proc newThinkyPolicy*(environmentConfig: string): ThinkyPolicy =
651
+ let cfg = parseConfig(environmentConfig)
652
+ var agents: seq[ThinkyAgent] = @[]
653
+ for id in 0 ..< cfg.config.numAgents:
654
+ agents.add(newThinkyAgent(id, environmentConfig))
655
+ return ThinkyPolicy(agents: agents)
656
+
657
+ proc stepBatch*(
658
+ policy: ThinkyPolicy,
659
+ agentIds: pointer,
660
+ numAgentIds: int,
661
+ numAgents: int,
662
+ numTokens: int,
663
+ sizeToken: int,
664
+ rawObservations: pointer,
665
+ numActions: int,
666
+ rawActions: pointer
667
+ ) {.measure.} =
668
+ let ids = cast[ptr UncheckedArray[int32]](agentIds)
669
+ let obsArray = cast[ptr UncheckedArray[uint8]](rawObservations)
670
+ let actionArray = cast[ptr UncheckedArray[int32]](rawActions)
671
+ let obsStride = numTokens * sizeToken
672
+
673
+ for i in 0 ..< numAgentIds:
674
+ let idx = int(ids[i])
675
+ let obsPtr = cast[pointer](obsArray[idx * obsStride].addr)
676
+ let actPtr = cast[ptr int32](actionArray[idx].addr)
677
+ step(policy.agents[idx], numAgents, numTokens, sizeToken, obsPtr, numActions, actPtr)