@its-not-rocket-science/ananke 0.1.14 → 0.1.15

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,33 @@ Versioning follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [0.1.15] — 2026-03-25
10
+
11
+ ### Added
12
+
13
+ - **CE-5 · WebAssembly Kernel** — shadow-mode WASM acceleration for push repulsion and
14
+ injury accumulation:
15
+ - `as/units.ts` — AssemblyScript port of `src/units.ts` (all 13 exports: SCALE constants,
16
+ `q()`, `clampQ()`, `qMul()`, `qDiv()`, `mulDiv()`, `sqrtQ()`, `cbrtQ()`, unit
17
+ converters). Compiled to `dist/as/units.wasm`.
18
+ - `as/push.ts` — pair-wise position repulsion kernel in flat WASM memory (64-entity
19
+ capacity, octagonal distance approximation, overflow-safe i64 arithmetic).
20
+ Compiled to `dist/as/push.wasm`.
21
+ - `as/injury.ts` — per-entity injury accumulation inner loop (clotting, bleed→fluid,
22
+ shock, consciousness, death check) matching `src/sim/step/injury.ts` constants exactly.
23
+ Compiled to `dist/as/injury.wasm`.
24
+ - `src/wasm-kernel.ts` — Node.js host bridge. `WasmKernel.shadowStep(world, tick)`
25
+ marshals entity state into WASM memory, runs both kernels, and returns a
26
+ `WasmStepReport` with per-entity velocity deltas and projected vitals. Shadow mode:
27
+ outputs are never applied to world state — used for validation and diagnostics only.
28
+ - `loadWasmKernel()` factory loads `push.wasm` + `injury.wasm` from `dist/as/` at
29
+ runtime via `import.meta.url` + `readFileSync`.
30
+ - Exported as `@its-not-rocket-science/ananke/wasm-kernel`.
31
+ - `dist/as/` (compiled WASM binaries) included in the published package.
32
+ - 61 WASM unit tests (`test/as/`) covering units, push repulsion, and injury
33
+ accumulation parity with the TypeScript reference implementation.
34
+ - Build scripts: `npm run build:wasm:all`, `npm run test:wasm`.
35
+
9
36
  ## [Unreleased]
10
37
 
11
38
  ### Added
Binary file
@@ -0,0 +1,382 @@
1
+ (module
2
+ (type $0 (func (param i32) (result i32)))
3
+ (type $1 (func (param i32 i32 i32 i32 i32 i32 i32)))
4
+ (type $2 (func (param i32 i32 i32 i32 i32 i32)))
5
+ (type $3 (func (param i32 i32) (result i32)))
6
+ (type $4 (func (param i32)))
7
+ (global $as/injury/SHOCK_FROM_FLUID i32 (i32.const 40))
8
+ (global $as/injury/SHOCK_FROM_INTERNAL i32 (i32.const 20))
9
+ (global $as/injury/CONSC_LOSS_FROM_SHOCK i32 (i32.const 100))
10
+ (global $as/injury/CONSC_LOSS_FROM_SUFF i32 (i32.const 200))
11
+ (global $as/injury/FATAL_FLUID_LOSS i32 (i32.const 8000))
12
+ (global $as/injury/CLOT_RATE_PER_TICK i32 (i32.const 2))
13
+ (global $as/injury/MAX_ENTITIES i32 (i32.const 64))
14
+ (global $as/injury/N_REGIONS i32 (i32.const 6))
15
+ (global $as/injury/ENTITY_STRIDE i32 (i32.const 120))
16
+ (memory $0 1)
17
+ (export "SHOCK_FROM_FLUID" (global $as/injury/SHOCK_FROM_FLUID))
18
+ (export "SHOCK_FROM_INTERNAL" (global $as/injury/SHOCK_FROM_INTERNAL))
19
+ (export "CONSC_LOSS_FROM_SHOCK" (global $as/injury/CONSC_LOSS_FROM_SHOCK))
20
+ (export "CONSC_LOSS_FROM_SUFF" (global $as/injury/CONSC_LOSS_FROM_SUFF))
21
+ (export "FATAL_FLUID_LOSS" (global $as/injury/FATAL_FLUID_LOSS))
22
+ (export "CLOT_RATE_PER_TICK" (global $as/injury/CLOT_RATE_PER_TICK))
23
+ (export "MAX_ENTITIES" (global $as/injury/MAX_ENTITIES))
24
+ (export "N_REGIONS" (global $as/injury/N_REGIONS))
25
+ (export "ENTITY_STRIDE" (global $as/injury/ENTITY_STRIDE))
26
+ (export "writeVitals" (func $as/injury/writeVitals))
27
+ (export "writeRegion" (func $as/injury/writeRegion))
28
+ (export "readFluidLoss" (func $as/injury/readFluidLoss))
29
+ (export "readShock" (func $as/injury/readShock))
30
+ (export "readConsciousness" (func $as/injury/readConsciousness))
31
+ (export "readDead" (func $as/injury/readDead))
32
+ (export "readBleedingRate" (func $as/injury/readBleedingRate))
33
+ (export "stepBleedAndShock" (func $as/injury/stepBleedAndShock))
34
+ (export "memory" (memory $0))
35
+ (func $as/injury/writeVitals (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32)
36
+ local.get $0
37
+ i32.const 120
38
+ i32.mul
39
+ local.tee $0
40
+ local.get $1
41
+ i32.store
42
+ local.get $0
43
+ local.get $2
44
+ i32.store offset=4
45
+ local.get $0
46
+ local.get $3
47
+ i32.store offset=8
48
+ local.get $0
49
+ local.get $4
50
+ i32.store offset=12
51
+ local.get $0
52
+ local.get $5
53
+ i32.store offset=16
54
+ local.get $0
55
+ local.get $6
56
+ i32.store offset=20
57
+ )
58
+ (func $as/injury/writeRegion (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
59
+ local.get $0
60
+ i32.const 120
61
+ i32.mul
62
+ local.tee $0
63
+ local.get $1
64
+ i32.const 2
65
+ i32.shl
66
+ local.tee $1
67
+ i32.const 6
68
+ i32.add
69
+ i32.const 2
70
+ i32.shl
71
+ i32.add
72
+ local.get $2
73
+ i32.store
74
+ local.get $0
75
+ local.get $1
76
+ i32.const 7
77
+ i32.add
78
+ i32.const 2
79
+ i32.shl
80
+ i32.add
81
+ local.get $3
82
+ i32.store
83
+ local.get $0
84
+ local.get $1
85
+ i32.const 8
86
+ i32.add
87
+ i32.const 2
88
+ i32.shl
89
+ i32.add
90
+ local.get $4
91
+ i32.store
92
+ local.get $0
93
+ local.get $1
94
+ i32.const 9
95
+ i32.add
96
+ i32.const 2
97
+ i32.shl
98
+ i32.add
99
+ local.get $5
100
+ i32.store
101
+ )
102
+ (func $as/injury/readFluidLoss (param $0 i32) (result i32)
103
+ local.get $0
104
+ i32.const 120
105
+ i32.mul
106
+ i32.load
107
+ )
108
+ (func $as/injury/readShock (param $0 i32) (result i32)
109
+ local.get $0
110
+ i32.const 120
111
+ i32.mul
112
+ i32.load offset=4
113
+ )
114
+ (func $as/injury/readConsciousness (param $0 i32) (result i32)
115
+ local.get $0
116
+ i32.const 120
117
+ i32.mul
118
+ i32.load offset=8
119
+ )
120
+ (func $as/injury/readDead (param $0 i32) (result i32)
121
+ local.get $0
122
+ i32.const 120
123
+ i32.mul
124
+ i32.load offset=12
125
+ )
126
+ (func $as/injury/readBleedingRate (param $0 i32) (param $1 i32) (result i32)
127
+ local.get $0
128
+ i32.const 120
129
+ i32.mul
130
+ local.get $1
131
+ i32.const 2
132
+ i32.shl
133
+ i32.const 6
134
+ i32.add
135
+ i32.const 2
136
+ i32.shl
137
+ i32.add
138
+ i32.load
139
+ )
140
+ (func $as/injury/stepBleedAndShock (param $0 i32)
141
+ (local $1 i32)
142
+ (local $2 i32)
143
+ (local $3 i32)
144
+ (local $4 i32)
145
+ (local $5 i32)
146
+ (local $6 i32)
147
+ (local $7 i32)
148
+ loop $for-loop|0
149
+ local.get $0
150
+ local.get $4
151
+ i32.gt_s
152
+ if
153
+ local.get $4
154
+ i32.const 120
155
+ i32.mul
156
+ i32.load offset=12
157
+ i32.eqz
158
+ if
159
+ i32.const 0
160
+ local.set $3
161
+ i32.const 0
162
+ local.set $2
163
+ loop $for-loop|1
164
+ local.get $2
165
+ i32.const 6
166
+ i32.lt_s
167
+ if
168
+ local.get $4
169
+ i32.const 120
170
+ i32.mul
171
+ local.tee $5
172
+ local.get $2
173
+ i32.const 2
174
+ i32.shl
175
+ local.tee $6
176
+ i32.const 6
177
+ i32.add
178
+ i32.const 2
179
+ i32.shl
180
+ i32.add
181
+ local.tee $7
182
+ i32.load
183
+ local.tee $1
184
+ i32.const 0
185
+ i32.gt_s
186
+ if
187
+ local.get $7
188
+ i32.const 10000
189
+ local.get $1
190
+ i32.const 10000
191
+ i32.const 10000
192
+ local.get $5
193
+ local.get $6
194
+ i32.const 7
195
+ i32.add
196
+ i32.const 2
197
+ i32.shl
198
+ i32.add
199
+ i32.load
200
+ i32.sub
201
+ local.tee $1
202
+ local.get $1
203
+ i32.const 10000
204
+ i32.gt_s
205
+ select
206
+ local.tee $1
207
+ i32.const 0
208
+ local.get $1
209
+ i32.const 0
210
+ i32.ge_s
211
+ select
212
+ i64.extend_i32_s
213
+ i64.const 1
214
+ i64.shl
215
+ i64.const 10000
216
+ i64.div_s
217
+ i32.wrap_i64
218
+ i32.sub
219
+ local.tee $1
220
+ local.get $1
221
+ i32.const 10000
222
+ i32.gt_s
223
+ select
224
+ local.tee $1
225
+ i32.const 0
226
+ local.get $1
227
+ i32.const 0
228
+ i32.ge_s
229
+ select
230
+ local.tee $1
231
+ i32.store
232
+ end
233
+ local.get $1
234
+ local.get $3
235
+ i32.add
236
+ local.set $3
237
+ local.get $2
238
+ i32.const 1
239
+ i32.add
240
+ local.set $2
241
+ br $for-loop|1
242
+ end
243
+ end
244
+ i32.const 10000
245
+ local.get $4
246
+ i32.const 120
247
+ i32.mul
248
+ local.tee $1
249
+ i32.load
250
+ local.get $3
251
+ i64.extend_i32_s
252
+ i64.const 500
253
+ i64.mul
254
+ i64.const 10000
255
+ i64.div_s
256
+ i32.wrap_i64
257
+ i32.add
258
+ local.tee $2
259
+ local.get $2
260
+ i32.const 10000
261
+ i32.gt_s
262
+ select
263
+ local.tee $2
264
+ i32.const 0
265
+ local.get $2
266
+ i32.const 0
267
+ i32.ge_s
268
+ select
269
+ local.set $2
270
+ local.get $1
271
+ local.get $2
272
+ i32.store
273
+ local.get $1
274
+ i32.const 10000
275
+ local.get $1
276
+ i64.load32_s offset=48
277
+ i64.const 20
278
+ i64.mul
279
+ i64.const 10000
280
+ i64.div_s
281
+ i32.wrap_i64
282
+ local.get $1
283
+ i32.const 4
284
+ i32.add
285
+ i32.load
286
+ local.get $2
287
+ i64.extend_i32_s
288
+ i64.const 40
289
+ i64.mul
290
+ i64.const 10000
291
+ i64.div_s
292
+ i32.wrap_i64
293
+ i32.add
294
+ i32.add
295
+ local.tee $3
296
+ local.get $3
297
+ i32.const 10000
298
+ i32.gt_s
299
+ select
300
+ local.tee $3
301
+ i32.const 0
302
+ local.get $3
303
+ i32.const 0
304
+ i32.ge_s
305
+ select
306
+ local.tee $3
307
+ i32.store offset=4
308
+ local.get $1
309
+ i32.const 10000
310
+ local.get $1
311
+ i32.const 8
312
+ i32.add
313
+ i32.load
314
+ i32.const 10000
315
+ local.get $3
316
+ i64.extend_i32_s
317
+ i64.const 100
318
+ i64.mul
319
+ i64.const 10000
320
+ i64.div_s
321
+ i32.wrap_i64
322
+ local.get $1
323
+ i64.load32_s offset=20
324
+ i64.const 200
325
+ i64.mul
326
+ i64.const 10000
327
+ i64.div_s
328
+ i32.wrap_i64
329
+ i32.add
330
+ local.tee $5
331
+ local.get $5
332
+ i32.const 10000
333
+ i32.gt_s
334
+ select
335
+ local.tee $5
336
+ i32.const 0
337
+ local.get $5
338
+ i32.const 0
339
+ i32.ge_s
340
+ select
341
+ i32.sub
342
+ local.tee $5
343
+ local.get $5
344
+ i32.const 10000
345
+ i32.gt_s
346
+ select
347
+ local.tee $5
348
+ i32.const 0
349
+ local.get $5
350
+ i32.const 0
351
+ i32.ge_s
352
+ select
353
+ local.tee $5
354
+ i32.store offset=8
355
+ local.get $5
356
+ i32.eqz
357
+ local.get $3
358
+ i32.const 10000
359
+ i32.ge_s
360
+ local.get $2
361
+ i32.const 8000
362
+ i32.ge_s
363
+ i32.or
364
+ i32.or
365
+ if
366
+ local.get $1
367
+ i32.const 1
368
+ i32.store offset=12
369
+ local.get $1
370
+ i32.const 0
371
+ i32.store offset=8
372
+ end
373
+ end
374
+ local.get $4
375
+ i32.const 1
376
+ i32.add
377
+ local.set $4
378
+ br $for-loop|0
379
+ end
380
+ end
381
+ )
382
+ )
Binary file
@@ -0,0 +1,326 @@
1
+ (module
2
+ (type $0 (func (param i32) (result i32)))
3
+ (type $1 (func (param i32 i32 i32 i32)))
4
+ (type $2 (func (param i32 i32) (result i32)))
5
+ (type $3 (func (param i32 i32 i32)))
6
+ (global $as/push/MAX_ENTITIES i32 (i32.const 64))
7
+ (global $as/push/OFFSET_POS_X i32 (i32.const 0))
8
+ (global $as/push/OFFSET_POS_Y i32 (i32.const 256))
9
+ (global $as/push/OFFSET_ALIVE i32 (i32.const 512))
10
+ (global $as/push/OFFSET_DV_X i32 (i32.const 768))
11
+ (global $as/push/OFFSET_DV_Y i32 (i32.const 1024))
12
+ (memory $0 1)
13
+ (export "MAX_ENTITIES" (global $as/push/MAX_ENTITIES))
14
+ (export "OFFSET_POS_X" (global $as/push/OFFSET_POS_X))
15
+ (export "OFFSET_POS_Y" (global $as/push/OFFSET_POS_Y))
16
+ (export "OFFSET_ALIVE" (global $as/push/OFFSET_ALIVE))
17
+ (export "OFFSET_DV_X" (global $as/push/OFFSET_DV_X))
18
+ (export "OFFSET_DV_Y" (global $as/push/OFFSET_DV_Y))
19
+ (export "writeEntity" (func $as/push/writeEntity))
20
+ (export "readDvX" (func $as/push/readDvX))
21
+ (export "readDvY" (func $as/push/readDvY))
22
+ (export "approxDist" (func $as/push/approxDist))
23
+ (export "stepRepulsionPairs" (func $as/push/stepRepulsionPairs))
24
+ (export "memory" (memory $0))
25
+ (func $as/push/writeEntity (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
26
+ local.get $0
27
+ i32.const 2
28
+ i32.shl
29
+ local.tee $0
30
+ local.get $1
31
+ i32.store
32
+ local.get $0
33
+ local.get $2
34
+ i32.store offset=256
35
+ local.get $0
36
+ local.get $3
37
+ i32.store offset=512
38
+ )
39
+ (func $as/push/readDvX (param $0 i32) (result i32)
40
+ local.get $0
41
+ i32.const 2
42
+ i32.shl
43
+ i32.load offset=768
44
+ )
45
+ (func $as/push/readDvY (param $0 i32) (result i32)
46
+ local.get $0
47
+ i32.const 2
48
+ i32.shl
49
+ i32.const 1024
50
+ i32.add
51
+ i32.load
52
+ )
53
+ (func $as/push/approxDist (param $0 i32) (param $1 i32) (result i32)
54
+ i32.const 0
55
+ local.get $0
56
+ i32.sub
57
+ local.get $0
58
+ local.get $0
59
+ i32.const 0
60
+ i32.lt_s
61
+ select
62
+ local.tee $0
63
+ i32.const 0
64
+ local.get $1
65
+ i32.sub
66
+ local.get $1
67
+ local.get $1
68
+ i32.const 0
69
+ i32.lt_s
70
+ select
71
+ local.tee $1
72
+ i32.const 1
73
+ i32.shr_s
74
+ i32.add
75
+ local.get $1
76
+ local.get $0
77
+ i32.const 1
78
+ i32.shr_s
79
+ i32.add
80
+ local.get $0
81
+ local.get $1
82
+ i32.gt_s
83
+ select
84
+ )
85
+ (func $as/push/stepRepulsionPairs (param $0 i32) (param $1 i32) (param $2 i32)
86
+ (local $3 i32)
87
+ (local $4 i64)
88
+ (local $5 i32)
89
+ (local $6 i32)
90
+ (local $7 i32)
91
+ (local $8 i64)
92
+ (local $9 i32)
93
+ (local $10 i32)
94
+ (local $11 i64)
95
+ (local $12 i32)
96
+ (local $13 i64)
97
+ (local $14 i64)
98
+ (local $15 i32)
99
+ loop $for-loop|0
100
+ local.get $0
101
+ local.get $3
102
+ i32.gt_s
103
+ if
104
+ local.get $3
105
+ i32.const 2
106
+ i32.shl
107
+ local.tee $5
108
+ i32.const 0
109
+ i32.store offset=768
110
+ local.get $5
111
+ i32.const 1024
112
+ i32.add
113
+ i32.const 0
114
+ i32.store
115
+ local.get $3
116
+ i32.const 1
117
+ i32.add
118
+ local.set $3
119
+ br $for-loop|0
120
+ end
121
+ end
122
+ local.get $1
123
+ i64.extend_i32_s
124
+ local.tee $4
125
+ local.get $4
126
+ i64.mul
127
+ local.set $4
128
+ i32.const 0
129
+ local.set $3
130
+ loop $for-loop|1
131
+ local.get $3
132
+ local.get $0
133
+ i32.const 1
134
+ i32.sub
135
+ i32.lt_s
136
+ if
137
+ local.get $3
138
+ i32.const 2
139
+ i32.shl
140
+ local.tee $5
141
+ i32.load offset=512
142
+ if
143
+ local.get $5
144
+ i32.load
145
+ local.set $6
146
+ local.get $5
147
+ i32.load offset=256
148
+ local.set $7
149
+ local.get $3
150
+ i32.const 1
151
+ i32.add
152
+ local.set $5
153
+ loop $for-loop|2
154
+ local.get $0
155
+ local.get $5
156
+ i32.gt_s
157
+ if
158
+ block $for-continue|2
159
+ local.get $5
160
+ i32.const 2
161
+ i32.shl
162
+ local.tee $9
163
+ i32.load offset=512
164
+ i32.eqz
165
+ br_if $for-continue|2
166
+ local.get $9
167
+ i32.load
168
+ local.get $6
169
+ i32.sub
170
+ local.tee $10
171
+ i64.extend_i32_s
172
+ local.tee $11
173
+ local.get $11
174
+ i64.mul
175
+ local.get $9
176
+ i32.load offset=256
177
+ local.get $7
178
+ i32.sub
179
+ local.tee $12
180
+ i64.extend_i32_s
181
+ local.tee $13
182
+ local.get $13
183
+ i64.mul
184
+ i64.add
185
+ local.tee $8
186
+ i64.eqz
187
+ local.get $4
188
+ local.get $8
189
+ i64.le_s
190
+ i32.or
191
+ br_if $for-continue|2
192
+ local.get $1
193
+ i32.const 0
194
+ local.get $10
195
+ i32.sub
196
+ local.get $10
197
+ local.get $10
198
+ i32.const 0
199
+ i32.lt_s
200
+ select
201
+ local.tee $10
202
+ i32.const 0
203
+ local.get $12
204
+ i32.sub
205
+ local.get $12
206
+ local.get $12
207
+ i32.const 0
208
+ i32.lt_s
209
+ select
210
+ local.tee $12
211
+ i32.const 1
212
+ i32.shr_s
213
+ i32.add
214
+ local.get $12
215
+ local.get $10
216
+ i32.const 1
217
+ i32.shr_s
218
+ i32.add
219
+ local.get $10
220
+ local.get $12
221
+ i32.gt_s
222
+ select
223
+ local.tee $10
224
+ i32.sub
225
+ local.tee $12
226
+ i32.const 0
227
+ i32.le_s
228
+ br_if $for-continue|2
229
+ i32.const 10000
230
+ local.get $12
231
+ i32.const 10000
232
+ i32.mul
233
+ local.get $1
234
+ i32.div_s
235
+ local.tee $12
236
+ local.get $12
237
+ i32.const 10000
238
+ i32.gt_s
239
+ select
240
+ local.tee $12
241
+ i32.const 0
242
+ local.get $12
243
+ i32.const 0
244
+ i32.ge_s
245
+ select
246
+ i64.extend_i32_s
247
+ local.tee $14
248
+ local.get $2
249
+ i64.extend_i32_s
250
+ local.tee $8
251
+ local.get $11
252
+ i64.mul
253
+ i64.mul
254
+ i32.const 1
255
+ local.get $10
256
+ local.get $10
257
+ i32.const 0
258
+ i32.le_s
259
+ select
260
+ i64.extend_i32_s
261
+ i64.const 10000
262
+ i64.mul
263
+ local.tee $11
264
+ i64.div_s
265
+ i32.wrap_i64
266
+ local.set $10
267
+ local.get $8
268
+ local.get $13
269
+ i64.mul
270
+ local.get $14
271
+ i64.mul
272
+ local.get $11
273
+ i64.div_s
274
+ i32.wrap_i64
275
+ local.set $12
276
+ local.get $3
277
+ i32.const 2
278
+ i32.shl
279
+ local.tee $15
280
+ local.get $15
281
+ i32.load offset=768
282
+ local.get $10
283
+ i32.sub
284
+ i32.store offset=768
285
+ local.get $15
286
+ i32.const 1024
287
+ i32.add
288
+ local.tee $15
289
+ local.get $15
290
+ i32.load
291
+ local.get $12
292
+ i32.sub
293
+ i32.store
294
+ local.get $9
295
+ local.get $9
296
+ i32.load offset=768
297
+ local.get $10
298
+ i32.add
299
+ i32.store offset=768
300
+ local.get $9
301
+ i32.const 1024
302
+ i32.add
303
+ local.tee $9
304
+ local.get $9
305
+ i32.load
306
+ local.get $12
307
+ i32.add
308
+ i32.store
309
+ end
310
+ local.get $5
311
+ i32.const 1
312
+ i32.add
313
+ local.set $5
314
+ br $for-loop|2
315
+ end
316
+ end
317
+ end
318
+ local.get $3
319
+ i32.const 1
320
+ i32.add
321
+ local.set $3
322
+ br $for-loop|1
323
+ end
324
+ end
325
+ )
326
+ )
Binary file
@@ -0,0 +1,290 @@
1
+ (module
2
+ (type $0 (func (param f64) (result i32)))
3
+ (type $1 (func (param i32) (result f64)))
4
+ (type $2 (func (param i32 i32 i32) (result i32)))
5
+ (type $3 (func (param i32 i32) (result i32)))
6
+ (type $4 (func (param i32) (result i32)))
7
+ (global $as/units/SCALE_Q i32 (i32.const 10000))
8
+ (global $as/units/SCALE_m i32 (i32.const 10000))
9
+ (global $as/units/SCALE_s i32 (i32.const 10000))
10
+ (global $as/units/SCALE_kg i32 (i32.const 1000))
11
+ (global $as/units/SCALE_N i32 (i32.const 100))
12
+ (global $as/units/SCALE_W i32 (i32.const 1))
13
+ (global $as/units/SCALE_J i32 (i32.const 1))
14
+ (global $as/units/SCALE_mps i32 (i32.const 10000))
15
+ (global $as/units/SCALE_mps2 i32 (i32.const 10000))
16
+ (global $as/units/G_mps2 i32 (i32.const 98067))
17
+ (memory $0 0)
18
+ (export "SCALE_Q" (global $as/units/SCALE_Q))
19
+ (export "SCALE_m" (global $as/units/SCALE_m))
20
+ (export "SCALE_s" (global $as/units/SCALE_s))
21
+ (export "SCALE_kg" (global $as/units/SCALE_kg))
22
+ (export "SCALE_N" (global $as/units/SCALE_N))
23
+ (export "SCALE_W" (global $as/units/SCALE_W))
24
+ (export "SCALE_J" (global $as/units/SCALE_J))
25
+ (export "SCALE_mps" (global $as/units/SCALE_mps))
26
+ (export "SCALE_mps2" (global $as/units/SCALE_mps2))
27
+ (export "G_mps2" (global $as/units/G_mps2))
28
+ (export "q" (func $as/units/q))
29
+ (export "clampQ" (func $as/units/clampQ))
30
+ (export "qMul" (func $as/units/qMul))
31
+ (export "qDiv" (func $as/units/qDiv))
32
+ (export "mulDiv" (func $as/units/mulDiv))
33
+ (export "to_m" (func $as/units/q))
34
+ (export "to_s" (func $as/units/q))
35
+ (export "to_kg" (func $as/units/to_kg))
36
+ (export "to_N" (func $as/units/to_N))
37
+ (export "to_W" (func $as/units/to_W))
38
+ (export "to_J" (func $as/units/to_W))
39
+ (export "to_mps" (func $as/units/q))
40
+ (export "to_mps2" (func $as/units/q))
41
+ (export "from_m" (func $as/units/from_m))
42
+ (export "from_s" (func $as/units/from_m))
43
+ (export "from_kg" (func $as/units/from_kg))
44
+ (export "from_N" (func $as/units/from_N))
45
+ (export "from_W" (func $as/units/from_W))
46
+ (export "from_J" (func $as/units/from_W))
47
+ (export "from_mps" (func $as/units/from_m))
48
+ (export "from_mps2" (func $as/units/from_m))
49
+ (export "sqrtQ" (func $as/units/sqrtQ))
50
+ (export "cbrtQ" (func $as/units/cbrtQ))
51
+ (export "memory" (memory $0))
52
+ (func $as/units/q (param $0 f64) (result i32)
53
+ (local $1 f64)
54
+ local.get $0
55
+ f64.const 1e4
56
+ f64.mul
57
+ local.tee $1
58
+ f64.ceil
59
+ local.tee $0
60
+ local.get $0
61
+ f64.const -1
62
+ f64.add
63
+ local.get $0
64
+ f64.const -0.5
65
+ f64.add
66
+ local.get $1
67
+ f64.le
68
+ select
69
+ i32.trunc_sat_f64_s
70
+ )
71
+ (func $as/units/clampQ (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
72
+ local.get $1
73
+ local.get $2
74
+ local.get $0
75
+ local.get $0
76
+ local.get $2
77
+ i32.gt_s
78
+ select
79
+ local.tee $0
80
+ local.get $0
81
+ local.get $1
82
+ i32.lt_s
83
+ select
84
+ )
85
+ (func $as/units/qMul (param $0 i32) (param $1 i32) (result i32)
86
+ local.get $0
87
+ i64.extend_i32_s
88
+ local.get $1
89
+ i64.extend_i32_s
90
+ i64.mul
91
+ i64.const 10000
92
+ i64.div_s
93
+ i32.wrap_i64
94
+ )
95
+ (func $as/units/qDiv (param $0 i32) (param $1 i32) (result i32)
96
+ local.get $0
97
+ i64.extend_i32_s
98
+ i64.const 10000
99
+ i64.mul
100
+ local.get $1
101
+ i64.extend_i32_s
102
+ i64.div_s
103
+ i32.wrap_i64
104
+ )
105
+ (func $as/units/mulDiv (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
106
+ local.get $0
107
+ i64.extend_i32_s
108
+ local.get $1
109
+ i64.extend_i32_s
110
+ i64.mul
111
+ local.get $2
112
+ i64.extend_i32_s
113
+ i64.div_s
114
+ i32.wrap_i64
115
+ )
116
+ (func $as/units/to_kg (param $0 f64) (result i32)
117
+ (local $1 f64)
118
+ local.get $0
119
+ f64.const 1e3
120
+ f64.mul
121
+ local.tee $1
122
+ f64.ceil
123
+ local.tee $0
124
+ local.get $0
125
+ f64.const -1
126
+ f64.add
127
+ local.get $0
128
+ f64.const -0.5
129
+ f64.add
130
+ local.get $1
131
+ f64.le
132
+ select
133
+ i32.trunc_sat_f64_s
134
+ )
135
+ (func $as/units/to_N (param $0 f64) (result i32)
136
+ (local $1 f64)
137
+ local.get $0
138
+ f64.const 100
139
+ f64.mul
140
+ local.tee $1
141
+ f64.ceil
142
+ local.tee $0
143
+ local.get $0
144
+ f64.const -1
145
+ f64.add
146
+ local.get $0
147
+ f64.const -0.5
148
+ f64.add
149
+ local.get $1
150
+ f64.le
151
+ select
152
+ i32.trunc_sat_f64_s
153
+ )
154
+ (func $as/units/to_W (param $0 f64) (result i32)
155
+ (local $1 f64)
156
+ local.get $0
157
+ f64.ceil
158
+ local.tee $1
159
+ local.get $1
160
+ f64.const -1
161
+ f64.add
162
+ local.get $1
163
+ f64.const -0.5
164
+ f64.add
165
+ local.get $0
166
+ f64.le
167
+ select
168
+ i32.trunc_sat_f64_s
169
+ )
170
+ (func $as/units/from_m (param $0 i32) (result f64)
171
+ local.get $0
172
+ f64.convert_i32_s
173
+ f64.const 1e4
174
+ f64.div
175
+ )
176
+ (func $as/units/from_kg (param $0 i32) (result f64)
177
+ local.get $0
178
+ f64.convert_i32_s
179
+ f64.const 1e3
180
+ f64.div
181
+ )
182
+ (func $as/units/from_N (param $0 i32) (result f64)
183
+ local.get $0
184
+ f64.convert_i32_s
185
+ f64.const 100
186
+ f64.div
187
+ )
188
+ (func $as/units/from_W (param $0 i32) (result f64)
189
+ local.get $0
190
+ f64.convert_i32_s
191
+ )
192
+ (func $as/units/sqrtQ (param $0 i32) (result i32)
193
+ (local $1 i64)
194
+ (local $2 i64)
195
+ (local $3 i32)
196
+ (local $4 i64)
197
+ i32.const 1
198
+ local.get $0
199
+ local.get $0
200
+ i32.const 0
201
+ i32.le_s
202
+ select
203
+ i64.extend_i32_s
204
+ i64.const 10000
205
+ i64.mul
206
+ local.set $4
207
+ i64.const 10000
208
+ local.set $1
209
+ loop $for-loop|0
210
+ local.get $3
211
+ i32.const 10
212
+ i32.lt_s
213
+ if
214
+ local.get $1
215
+ local.get $1
216
+ local.get $4
217
+ local.get $1
218
+ i64.div_s
219
+ i64.add
220
+ i64.const 2
221
+ i64.div_s
222
+ local.tee $2
223
+ i64.ne
224
+ if
225
+ local.get $2
226
+ local.set $1
227
+ local.get $3
228
+ i32.const 1
229
+ i32.add
230
+ local.set $3
231
+ br $for-loop|0
232
+ end
233
+ end
234
+ end
235
+ local.get $1
236
+ i32.wrap_i64
237
+ )
238
+ (func $as/units/cbrtQ (param $0 i32) (result i32)
239
+ (local $1 i64)
240
+ (local $2 i32)
241
+ (local $3 i64)
242
+ i32.const 1
243
+ local.get $0
244
+ local.get $0
245
+ i32.const 0
246
+ i32.le_s
247
+ select
248
+ i64.extend_i32_s
249
+ i64.const 100000000
250
+ i64.mul
251
+ local.set $3
252
+ i64.const 10000
253
+ local.set $1
254
+ loop $for-loop|0
255
+ local.get $2
256
+ i32.const 12
257
+ i32.lt_s
258
+ if
259
+ block $for-break0
260
+ local.get $3
261
+ local.get $1
262
+ local.get $1
263
+ i64.mul
264
+ i64.div_s
265
+ local.get $1
266
+ i64.const 1
267
+ i64.shl
268
+ i64.add
269
+ i64.const 3
270
+ i64.div_s
271
+ local.tee $1
272
+ i64.const 0
273
+ i64.le_s
274
+ if
275
+ i64.const 1
276
+ local.set $1
277
+ br $for-break0
278
+ end
279
+ local.get $2
280
+ i32.const 1
281
+ i32.add
282
+ local.set $2
283
+ br $for-loop|0
284
+ end
285
+ end
286
+ end
287
+ local.get $1
288
+ i32.wrap_i64
289
+ )
290
+ )
@@ -0,0 +1,58 @@
1
+ import type { WorldState } from "./sim/world.js";
2
+ interface PushExports {
3
+ MAX_ENTITIES: WebAssembly.Global;
4
+ writeEntity: (slot: number, posX: number, posY: number, alive: number) => void;
5
+ readDvX: (slot: number) => number;
6
+ readDvY: (slot: number) => number;
7
+ stepRepulsionPairs: (n: number, radius_m: number, repelAccel_mps2: number) => void;
8
+ }
9
+ interface InjuryExports {
10
+ MAX_ENTITIES: WebAssembly.Global;
11
+ writeVitals: (slot: number, fluidLoss: number, shock: number, consciousness: number, dead: number, fatigue: number, suffocation: number) => void;
12
+ writeRegion: (slot: number, r: number, bleedingRate: number, structuralDamage: number, internalDamage: number, surfaceDamage: number) => void;
13
+ readFluidLoss: (slot: number) => number;
14
+ readShock: (slot: number) => number;
15
+ readConsciousness: (slot: number) => number;
16
+ readDead: (slot: number) => number;
17
+ stepBleedAndShock: (n: number) => void;
18
+ }
19
+ export interface WasmEntityReport {
20
+ entityId: number;
21
+ /** Repulsion velocity delta computed by WASM push kernel (SCALE.mps units). */
22
+ pushDvX: number;
23
+ pushDvY: number;
24
+ /** Projected injury state after one WASM injury tick. */
25
+ projFluidLoss: number;
26
+ projShock: number;
27
+ projConsciousness: number;
28
+ projDead: boolean;
29
+ }
30
+ export interface WasmStepReport {
31
+ tick: number;
32
+ entities: WasmEntityReport[];
33
+ /** True if WASM is available and ran successfully. */
34
+ ok: boolean;
35
+ summary: string;
36
+ }
37
+ export declare class WasmKernel {
38
+ private readonly push;
39
+ private readonly injury;
40
+ private static readonly PUSH_RADIUS_M;
41
+ private static readonly PUSH_REPEL_MPS2;
42
+ constructor(push: PushExports, injury: InjuryExports);
43
+ /**
44
+ * Run WASM push + injury steps on the current world state (shadow mode — does not
45
+ * mutate world). Returns a per-entity report and a one-line summary string.
46
+ *
47
+ * Call this after stepWorld() each tick for validation / diagnostics.
48
+ */
49
+ shadowStep(world: WorldState, tick: number): WasmStepReport;
50
+ }
51
+ /**
52
+ * Load push.wasm and injury.wasm from dist/as/ (co-located with this compiled module)
53
+ * and return a WasmKernel ready for use.
54
+ *
55
+ * Throws if the WASM files are not found (e.g. npm run build:wasm:all not yet run).
56
+ */
57
+ export declare function loadWasmKernel(): Promise<WasmKernel>;
58
+ export {};
@@ -0,0 +1,96 @@
1
+ // src/wasm-kernel.ts — Node.js loader and host bridge for the AssemblyScript WASM modules.
2
+ //
3
+ // Provides a shadow-mode step that runs as/push.wasm + as/injury.wasm alongside the
4
+ // TypeScript kernel. In shadow mode the WASM outputs are NOT applied to world state;
5
+ // they are returned for the caller to log or validate.
6
+ //
7
+ // Usage:
8
+ // const kernel = await loadWasmKernel(); // once at startup
9
+ // const report = kernel.shadowStep(world); // after stepWorld() each tick
10
+ // console.log(report.summary);
11
+ import { readFileSync } from "node:fs";
12
+ import { fileURLToPath } from "node:url";
13
+ import { SCALE } from "./units.js";
14
+ // ── Region order shared with as/injury.ts ─────────────────────────────────────
15
+ const REGION_ORDER = ["head", "torso", "leftArm", "rightArm", "leftLeg", "rightLeg"];
16
+ // ── Kernel class ──────────────────────────────────────────────────────────────
17
+ export class WasmKernel {
18
+ push;
19
+ injury;
20
+ // Canonical kernel push tuning (mirrors src/sim/kernel.ts)
21
+ static PUSH_RADIUS_M = Math.trunc(0.45 * SCALE.m); // 4500
22
+ static PUSH_REPEL_MPS2 = Math.trunc(1.5 * SCALE.mps2); // 15000
23
+ constructor(push, injury) {
24
+ this.push = push;
25
+ this.injury = injury;
26
+ }
27
+ /**
28
+ * Run WASM push + injury steps on the current world state (shadow mode — does not
29
+ * mutate world). Returns a per-entity report and a one-line summary string.
30
+ *
31
+ * Call this after stepWorld() each tick for validation / diagnostics.
32
+ */
33
+ shadowStep(world, tick) {
34
+ const entities = world.entities;
35
+ const pushMax = this.push.MAX_ENTITIES.value;
36
+ const injMax = this.injury.MAX_ENTITIES.value;
37
+ const n = Math.min(entities.length, pushMax, injMax);
38
+ // ── Push pass (position-based repulsion) ──────────────────────────────────
39
+ for (let i = 0; i < n; i++) {
40
+ const e = entities[i];
41
+ this.push.writeEntity(i, e.position_m.x, e.position_m.y, e.injury.dead ? 0 : 1);
42
+ }
43
+ this.push.stepRepulsionPairs(n, WasmKernel.PUSH_RADIUS_M, WasmKernel.PUSH_REPEL_MPS2);
44
+ // ── Injury pass (clotting / bleed / shock / consciousness) ────────────────
45
+ for (let i = 0; i < n; i++) {
46
+ const e = entities[i];
47
+ const suff = e.condition["suffocation"] ?? 0;
48
+ this.injury.writeVitals(i, e.injury.fluidLoss, e.injury.shock, e.injury.consciousness, e.injury.dead ? 1 : 0, e.energy.fatigue, suff);
49
+ for (let r = 0; r < REGION_ORDER.length; r++) {
50
+ const reg = e.injury.byRegion[REGION_ORDER[r]];
51
+ this.injury.writeRegion(i, r, reg?.bleedingRate ?? 0, reg?.structuralDamage ?? 0, reg?.internalDamage ?? 0, reg?.["surfaceDamage"] ?? 0);
52
+ }
53
+ }
54
+ this.injury.stepBleedAndShock(n);
55
+ // ── Build report ──────────────────────────────────────────────────────────
56
+ const reports = [];
57
+ for (let i = 0; i < n; i++) {
58
+ const e = entities[i];
59
+ reports.push({
60
+ entityId: e.id,
61
+ pushDvX: this.push.readDvX(i),
62
+ pushDvY: this.push.readDvY(i),
63
+ projFluidLoss: this.injury.readFluidLoss(i),
64
+ projShock: this.injury.readShock(i),
65
+ projConsciousness: this.injury.readConsciousness(i),
66
+ projDead: this.injury.readDead(i) === 1,
67
+ });
68
+ }
69
+ const summary = reports
70
+ .map(r => `e${r.entityId} dv=(${r.pushDvX},${r.pushDvY}) ` +
71
+ `fl=${(r.projFluidLoss / SCALE.Q).toFixed(3)} ` +
72
+ `sh=${(r.projShock / SCALE.Q).toFixed(3)} ` +
73
+ `co=${(r.projConsciousness / SCALE.Q).toFixed(3)}` +
74
+ (r.projDead ? " DEAD" : ""))
75
+ .join(" | ");
76
+ return { tick, entities: reports, ok: true, summary: `[wasm] tick ${tick}: ${summary}` };
77
+ }
78
+ }
79
+ // ── Factory ───────────────────────────────────────────────────────────────────
80
+ /**
81
+ * Load push.wasm and injury.wasm from dist/as/ (co-located with this compiled module)
82
+ * and return a WasmKernel ready for use.
83
+ *
84
+ * Throws if the WASM files are not found (e.g. npm run build:wasm:all not yet run).
85
+ */
86
+ export async function loadWasmKernel() {
87
+ // Compiled to dist/src/wasm-kernel.js → WASM files are at ../as/*.wasm
88
+ const base = new URL("../as/", import.meta.url);
89
+ const pushBuf = readFileSync(fileURLToPath(new URL("push.wasm", base)));
90
+ const injuryBuf = readFileSync(fileURLToPath(new URL("injury.wasm", base)));
91
+ const [pushResult, injuryResult] = await Promise.all([
92
+ WebAssembly.instantiate(pushBuf),
93
+ WebAssembly.instantiate(injuryBuf),
94
+ ]);
95
+ return new WasmKernel(pushResult.instance.exports, injuryResult.instance.exports);
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@its-not-rocket-science/ananke",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "description": "Deterministic lockstep-friendly SI-units RPG/physics core (fixed-point TS)",
6
6
  "license": "MIT",
@@ -54,10 +54,15 @@
54
54
  "./competence": {
55
55
  "import": "./dist/src/competence/index.js",
56
56
  "types": "./dist/src/competence/index.d.ts"
57
+ },
58
+ "./wasm-kernel": {
59
+ "import": "./dist/src/wasm-kernel.js",
60
+ "types": "./dist/src/wasm-kernel.d.ts"
57
61
  }
58
62
  },
59
63
  "files": [
60
64
  "dist/src",
65
+ "dist/as",
61
66
  "docs/project-overview.md",
62
67
  "docs/host-contract.md",
63
68
  "docs/integration-primer.md",
@@ -118,12 +123,19 @@
118
123
  "benchmark-check:strict": "node dist/tools/benchmark-check.js --threshold=0.10",
119
124
  "benchmark-check:update": "node dist/tools/benchmark-check.js --update-baseline",
120
125
  "benchmark:guide": "node dist/tools/benchmark-guide.js",
121
- "benchmark:parallel": "node dist/tools/benchmark-parallel.js"
126
+ "benchmark:parallel": "node dist/tools/benchmark-parallel.js",
127
+ "run:renderer-bridge": "node dist/tools/renderer-bridge.js",
128
+ "build:wasm": "asc as/units.ts --outFile dist/as/units.wasm --textFile dist/as/units.wat --runtime stub --optimize",
129
+ "build:wasm:push": "asc as/push.ts --outFile dist/as/push.wasm --textFile dist/as/push.wat --runtime stub --optimize --initialMemory 1",
130
+ "build:wasm:injury": "asc as/injury.ts --outFile dist/as/injury.wasm --textFile dist/as/injury.wat --runtime stub --optimize --initialMemory 1",
131
+ "build:wasm:all": "npm run build:wasm && npm run build:wasm:push && npm run build:wasm:injury",
132
+ "test:wasm": "npm run build:wasm:all && vitest run test/as/"
122
133
  },
123
134
  "devDependencies": {
124
135
  "@eslint/js": "^9.39.3",
125
136
  "@types/node": "^20.0.0",
126
137
  "@vitest/coverage-v8": "^2.1.8",
138
+ "assemblyscript": "^0.27.37",
127
139
  "eslint": "^9.39.3",
128
140
  "fast-check": "^4.6.0",
129
141
  "typescript": "^5.5.4",