aboutsummaryrefslogtreecommitdiffstats
path: root/prototypes/live-rules/adapter.test.mjs
blob: 95af13bc06f0f2436b92041bf683469fa7420cb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { loadCore, snapshot, storage, parseCommand, execute } from './runtime.mjs';

test('the actual Wasm preserves airborne state through a rule change, then permits the second jump', async () => {
  const c = await loadCore();
  execute(c, parseCommand('jump'));
  execute(c, parseCommand('step 8'));
  execute(c, parseCommand('jump'));
  const before = snapshot(c);
  assert.equal(before.state.lastReason, 3);
  const after = execute(c, parseCommand('rules double 12 on'));
  for (const field of ['tick', 'y', 'vy', 'distance', 'obstacleX', 'jumpsUsed', 'hits', 'passed']) {
    assert.equal(after.state[field], before.state[field], field);
  }
  assert.deepEqual(after.decision, before.decision);
  assert.equal(after.state.revision, 1);
  execute(c, parseCommand('jump'));
  assert.equal(c.get(10), 1);
  assert.equal(c.get(2), 1200);
  assert.equal(c.get(7), 2);
  assert.equal(c.input(2), 0);
  assert.equal(c.get(10), 4);
});

test('bad CLI edits and malformed numeric input cannot silently coerce into a different rule', async () => {
  const c = await loadCore();
  const before = snapshot(c);
  for (const command of ['rules double 8.5 on', 'rules double 4294967306 on', 'rules ground 5 on',
    'rules other 10 on', 'rules double 10 maybe', 'rules double 10 on extra', 'step 0', 'step 601', 'jump extra']) {
    assert.throws(() => execute(c, parseCommand(command)), undefined, command);
    assert.deepEqual(snapshot(c), before);
  }
  assert.equal(c.apply_rules(2, 10, 1), 1);
  assert.deepEqual(snapshot(c), before);
});

test('keyboard, pointer and terminal requests give the same simulation result', async () => {
  const states = [];
  for (const source of [0, 1, 2]) {
    const c = await loadCore();
    c.apply_rules(1, 12, 1);
    c.input(source);
    for (let i = 0; i < 8; i++) c.step();
    c.input(source);
    const { lastSource, ...state } = snapshot(c).state;
    assert.equal(lastSource, source);
    states.push(state);
  }
  assert.deepEqual(states[0], states[1]);
  assert.deepEqual(states[1], states[2]);
});

test('paused input has an inspectable decision; advance cannot move paused state', async () => {
  const c = await loadCore();
  assert.equal(snapshot(c).decision, null);
  assert.equal(c.input(0), 1);
  assert.equal(c.get(1), 0);
  assert.equal(c.get(17), 0);
  const before = snapshot(c);
  assert.equal(c.advance(60), 0);
  assert.deepEqual(snapshot(c), before);
  c.step();
  assert.equal(c.get(1), 945);
  assert.equal(c.get(0), 1);
});

test('bounded history and fixed linear memory survive repeated play and edits', async () => {
  const c = await loadCore();
  const before = storage(c);
  c.set_running(1);
  for (let i = 0; i < 5000; i++) {
    c.apply_rules(i % 2, 6 + i % 11, 1);
    c.input(i % 3);
    c.advance(8);
  }
  assert.equal(c.history_count(), 16);
  assert.equal(c.history_get(16, 0), -1);
  assert.equal(c.get(0), 40000);
  assert.deepEqual(storage(c), before);
  assert.equal(before.wasmLinearMemoryBytes, 65536);
  assert.throws(() => c.memory.grow(1), RangeError);
  const b = before.layoutBytes;
  assert.equal(b.logicalTotal, b.engine + b.graph);
  assert.ok(b.engine >= b.rules + b.state + b.decision + b.history);
});

test('replaying the same CLI operations produces identical snapshots and journal', async () => {
  const trace = ['jump', 'step 8', 'jump', 'rules double 10 on', 'jump', 'step 60', 'rules ground 8 off', 'jump'];
  const snapshots = [];
  for (let i = 0; i < 2; i++) {
    const c = await loadCore();
    snapshots.push(trace.map((line) => execute(c, parseCommand(line))));
  }
  assert.deepEqual(snapshots[0], snapshots[1]);
});