// The late-joiner snapshot: RLE encode of the mosaic and the paint pass that
// rebuilds it. Fixtures recorded off the real sendSnap/applySnap while the TS
// was canon — a mid-round joiner must see exactly the host's terrace.
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import * as lib from "../test-dist/testlib.js";
const { GW, GH, snapshots } = JSON.parse(
readFileSync(new URL("./vectors/snapshot.json", import.meta.url), "utf8"),
);
const plain = (x) => JSON.parse(JSON.stringify(x));
for (const s of snapshots) {
test(`snapshot ${s.name}: RLE encodes byte-identically to TS`, () => {
const grid = new Uint8Array(GW * GH);
for (const [x, y, v] of s.cells) grid[y * GW + x] = v;
assert.deepEqual(plain(lib.rleEncode(grid)), s.runs);
});
test(`snapshot ${s.name}: the paint pass rebuilds the same cells`, () => {
// rleDecodeCells yields [x, y, seat]; the TS resolved the colour from the
// seat's pilgrim, so compare on the [x, y, seat] triple the fixture holds
const expected = s.painted.map(([x, y, seat]) => [x, y, seat]);
assert.deepEqual(plain(lib.rleDecodeCells(s.runs)), expected);
});
test(`snapshot ${s.name}: encode/decode is a round trip over the mosaic`, () => {
const grid = new Uint8Array(GW * GH);
for (const [x, y, v] of s.cells) grid[y * GW + x] = v;
const back = new Uint8Array(GW * GH);
for (const [x, y, seat] of lib.rleDecodeCells(lib.rleEncode(grid))) back[y * GW + x] = seat + 1;
assert.deepEqual([...back], [...grid]);
});
}
test("an all-zero mosaic encodes as a single empty run", () => {
const grid = new Uint8Array(GW * GH);
assert.deepEqual(plain(lib.rleEncode(grid)), [0, GW * GH]);
assert.deepEqual(plain(lib.rleDecodeCells([0, GW * GH])), []);
});