// Valley-blocks referee sim — verdict fidelity against game2's Party.evaluate.
import test from "node:test";
import assert from "node:assert/strict";
import { ValleyBlocksSim, ROUND_LOBBY_MS } from "../dist/games/host/valley-blocks-sim.js";
const board = (score, over = false) => ({
grid: ".".repeat(200),
score,
lines: 0,
level: 1,
snake: null,
over,
});
function makeSim(ids, mode, target) {
const wins = new Map(ids.map((id) => [id, 0]));
const sim = new ValleyBlocksSim({
roster: () => ids.map((id) => [id, id, wins.get(id)]),
addWin: (id) => wins.set(id, wins.get(id) + 1),
mode,
target,
});
return { sim, wins };
}
test("startRound deals seats and a shared seed; solo rounds are refused", () => {
const { sim } = makeSim(["pa", "pb"], "race", 500);
assert.deepEqual(sim.startRound(["pa"], 1).frames, [], "one player never starts");
const emit = sim.startRound(["pa", "pb"], 12345);
const st = emit.frames[0].f;
assert.equal(st.t, "st");
assert.equal(st.seed, 12345);
assert.deepEqual(st.order, ["pa", "pb"]);
assert.equal(st.mode, "race");
assert.equal(st.target, 500);
assert.equal(sim.running, true);
});
test("crossing the target wins a race and the win is credited", () => {
const { sim, wins } = makeSim(["pa", "pb"], "race", 500);
sim.startRound(["pa", "pb"], 1);
assert.deepEqual(sim.onBoardState("pa", 1, 0, board(300)).frames, [], "no verdict below target");
const emit = sim.onBoardState("pb", 1, 1, board(520));
const ov = emit.frames[0].f;
assert.equal(ov.t, "ov");
assert.equal(ov.w, "pb");
assert.equal(ov.reason, "target");
assert.deepEqual(ov.standings, [["pb", "pb", 520, 0], ["pa", "pa", 300, 0]], "standings sort by score");
assert.deepEqual(emit.matchEnd, [["pa", "pa", 300], ["pb", "pb", 520]]);
assert.deepEqual(emit.next, { step: "lobby", inMs: ROUND_LOBBY_MS });
assert.equal(wins.get("pb"), 1);
assert.equal(sim.running, false);
});
test("survival ignores the target and pays the last one standing", () => {
const { sim } = makeSim(["pa", "pb", "pc"], "survival", 500);
sim.startRound(["pa", "pb", "pc"], 1);
assert.deepEqual(sim.onBoardState("pa", 1, 0, board(9999)).frames, [], "target means nothing in survival");
assert.deepEqual(sim.onBoardState("pb", 1, 1, board(100, true)).frames, [], "two still alive");
const ov = sim.onBoardState("pa", 1, 0, board(9999, true)).frames[0].f;
assert.equal(ov.w, "pc", "the seat that never even reported is the survivor");
assert.equal(ov.reason, "survivor");
});
test("race mode has no survivor rule; all-out goes to the highest score", () => {
const { sim } = makeSim(["pa", "pb"], "race", 100000);
sim.startRound(["pa", "pb"], 1);
assert.deepEqual(sim.onBoardState("pa", 1, 0, board(200, true)).frames, [], "race: a lone survivor plays on");
const ov = sim.onBoardState("pb", 1, 1, board(150, true)).frames[0].f;
assert.equal(ov.reason, "score");
assert.equal(ov.w, "pa", "everyone out → highest score takes it");
});
test("a leaver hands race-survival to the remaining player", () => {
const { sim } = makeSim(["pa", "pb"], "race-survival", 5000);
sim.startRound(["pa", "pb"], 1);
const ov = sim.playerLeft("pb").frames[0].f;
assert.equal(ov.w, "pa");
assert.equal(ov.reason, "survivor");
});
test("seat spoofing and stale rounds are rejected", () => {
const { sim } = makeSim(["pa", "pb"], "race", 100);
sim.startRound(["pa", "pb"], 1);
assert.deepEqual(sim.onBoardState("pa", 1, 1, board(500)).frames, [], "seat must match the dealt order");
assert.deepEqual(sim.onBoardState("pa", 7, 0, board(500)).frames, [], "wrong round is ignored");
assert.deepEqual(sim.onBoardState("px", 1, 0, board(500)).frames, [], "unknown sender is ignored");
assert.equal(sim.running, true);
});
test("late joiners get a spectator snapshot only mid-round", () => {
const { sim } = makeSim(["pa", "pb"], "race", 100);
assert.deepEqual(sim.snapshot("pc").frames, [], "no snapshot outside a round");
sim.startRound(["pa", "pb"], 9);
const emit = sim.snapshot("pc");
assert.equal(emit.frames[0].to, "pc");
const sn = emit.frames[0].f;
assert.equal(sn.t, "sn");
assert.deepEqual(sn.order, ["pa", "pb"]);
});