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