decision-studio / tests /tetris_step_queue.test.mjs
Xunzhuo's picture
feat(space): flash electric board rims on line clears
8075da2
Raw History Blame Contribute Delete
6.05 kB
import assert from 'node:assert/strict'
import test from 'node:test'
import { createStepQueue } from '../static/tetris/step-queue.js'
const step = (side, number, raceElapsedMs = number) => ({
side, step: number, race_elapsed_ms: raceElapsedMs, game: { pieces: number },
})
test('a short race keeps every step on two independent boards', () => {
const queue = createStepQueue()
for (let number = 1; number <= 40; number += 1) {
assert.equal(queue.enqueueStep('left', step('left', number)), true)
assert.equal(queue.enqueueStep('right', step('right', number)), true)
}
assert.equal(queue.enqueueStep('left', step('left', 40)), false)
const left = []
const right = []
let processed = 0
let now = 0
while (queue.hasPending()) {
const frame = queue.nextFrame(now)
if (frame.steps.left) left.push(frame.steps.left.step)
if (frame.steps.right) right.push(frame.steps.right.step)
processed += frame.processed
now += 16
}
assert.equal(processed, 80)
assert.equal(left.at(-1), 40)
assert.equal(right.at(-1), 40)
assert.ok(left.every((value, index) => index === 0 || value > left[index - 1]))
assert.ok(right.every((value, index) => index === 0 || value > right[index - 1]))
})
test('a line clear survives when a later step replaces its board in the same frame', () => {
const queue = createStepQueue()
queue.enqueueStep('left', {
...step('left', 1, 10), cleared_rows: [19],
}, 0)
queue.enqueueStep('left', {
...step('left', 2, 10), cleared_rows: [],
}, 0)
queue.enqueueStep('right', {
...step('right', 1, 10), cleared_rows: [],
}, 0)
const frame = queue.nextFrame(0)
assert.equal(frame.steps.left.step, 2)
assert.equal(frame.steps.right.step, 1)
assert.equal(frame.lineClears.left, 1)
assert.equal(frame.lineClears.right, 0)
})
test('a 2,000-step-per-side burst finishes visual replay within a bounded interval', () => {
const queue = createStepQueue()
for (let number = 1; number <= 2_000; number += 1) {
queue.enqueueStep('left', step('left', number, number / 3), 0)
queue.enqueueStep('right', step('right', number, number / 3), 0)
}
queue.finishSide('left', { score: 10 }, 670, 0)
queue.finishSide('right', { score: 20 }, 675, 0)
const left = []
const right = []
let processed = 0
let now = 0
const finishedAt = {}
while (queue.hasPending()) {
const frame = queue.nextFrame(now)
if (frame.steps.left) left.push(frame.steps.left.step)
if (frame.steps.right) right.push(frame.steps.right.step)
if (frame.finished) finishedAt[frame.finished.side] = now
processed += frame.processed
now += 16
}
assert.equal(processed, 4_002)
assert.equal(left.at(-1), 2_000)
assert.equal(right.at(-1), 2_000)
assert.ok(finishedAt.left < finishedAt.right)
assert.ok(now <= 1_200, `4,000 decisions replayed in ${now} ms`)
})
test('a completed 120-step-per-side race does not incur a frame per decision', () => {
const queue = createStepQueue()
for (let number = 1; number <= 120; number += 1) {
queue.enqueueStep('left', step('left', number, number * 5), 0)
queue.enqueueStep('right', step('right', number, number * 5), 0)
}
queue.finishSide('left', { score: 10 }, 605, 0)
queue.finishSide('right', { score: 20 }, 610, 0)
let now = 0
let processed = 0
const last = { left: 0, right: 0 }
while (queue.hasPending()) {
const frame = queue.nextFrame(now)
processed += frame.processed
for (const side of ['left', 'right']) {
if (frame.steps[side]) last[side] = frame.steps[side].step
}
now += 16
}
assert.equal(processed, 242)
assert.deepEqual(last, { left: 120, right: 120 })
assert.ok(now <= 900, `240 decisions replayed in ${now} ms`)
})
test('terminal cards follow actual finish order and wait for each last board frame', () => {
const queue = createStepQueue()
for (let number = 1; number <= 3; number += 1) {
queue.enqueueStep('left', step('left', number, number * 10), 0)
}
queue.enqueueStep('right', step('right', 1, 15), 0)
queue.finishSide('left', { score: 10 }, 40, 0)
queue.finishSide('right', { score: 20 }, 50, 0)
const frames = []
for (let now = 0; queue.hasPending(); now += 16) frames.push(queue.nextFrame(now))
assert.equal(frames[0].steps.left.step, 1)
assert.equal(frames[1].steps.right.step, 1)
assert.equal(frames[0].finished, null)
assert.equal(frames[2].steps.left.step, 3)
assert.deepEqual(frames[2].finished, { side: 'left', result: { score: 10 } })
assert.deepEqual(frames[3].finished, { side: 'right', result: { score: 20 } })
})
test('snapshot recovery applies the server finish order before rendering cards', () => {
const queue = createStepQueue()
queue.setFinishOrder(['right', 'left'])
queue.finishSide('left', { score: 10 })
queue.finishSide('right', { score: 20 })
assert.equal(queue.nextFrame(0).finished.side, 'right')
assert.equal(queue.nextFrame(16).finished.side, 'left')
assert.equal(queue.hasPending(), false)
})
test('a completed replay keeps a large finish gap without dropping decisions', () => {
const queue = createStepQueue()
for (let number = 1; number <= 4; number += 1) {
queue.enqueueStep('left', step('left', number, number * 25), 0)
queue.enqueueStep('right', step('right', number, number * 10_000), 0)
}
queue.finishSide('left', { score: 10 }, 125, 0)
queue.finishSide('right', { score: 20 }, 41_000, 0)
const latest = { left: 0, right: 0 }
const finishedAt = {}
let processed = 0
for (let now = 0; queue.hasPending() && now <= 2400; now += 16) {
const frame = queue.nextFrame(now)
for (const side of ['left', 'right']) {
if (frame.steps[side]) latest[side] = frame.steps[side].step
}
processed += frame.processed
if (frame.finished) finishedAt[frame.finished.side] = now
}
assert.deepEqual(latest, { left: 4, right: 4 })
assert.equal(processed, 10)
assert.ok(finishedAt.left < finishedAt.right - 1000)
assert.ok(finishedAt.right <= 2000)
assert.equal(queue.hasPending(), false)
})