Credibility pass: one voice, no scars

A three-reviewer sweep for tells of piecemeal machine generation,
every finding verified against the code before touching it. No
behavior changes; the full suite passes unchanged (plus two
strengthened pins).

Engine: removed four void-silenced fossils (a parseEdgeKey call
voided where it stood, stoneEffect's ignored cardId parameter, the
actualTarget remnant in doCast, a voided loop variable in shadow
upkeep); fixed the initialize-then-overwrite narration in
spawnCreature; replaced a filter(() => false) no-op; waterwall now
rides waveFromEdge instead of carrying its own verbatim copy (and the
single-caller washBack wrapper went with it); blind wall-bumps and
LOS blockers each collapsed to one implementation; the wand-id list
and the "permanent" duration sentinel became named constants; the
ambush number local no longer shadows the imported numberValue
function; assorted reviewer-aimed phrasings rewritten as the
constraints they guard.

Server/deploy: the protocol header now documents all eleven message
types; dropped an eslint pragma with no eslint, a test script with no
tests, and an rsync exclude anchored at a path that never existed
(the real data/ dir now excluded); the Caddy vhost has one source of
truth; stale "pending DNS" note removed — the record resolves.

Web: ~90 lines of CSS swallowed verbatim into a mobile media query
deduplicated; the reduced-motion guard on the board now actually
stops the marked-cell pulse; one shared color module replaces two
drifted palettes; an orphaned doc comment rejoined its function.

Tests: the ten-times-pasted helper block became test/helpers.ts;
wave-numbered files renamed for the behaviors they pin; deliberation
comments and void-ed corpses of unwritten assertions deleted; silent
seed-dependent early-returns now fail loudly; one assertion that
compared a value to itself now pins the home-translation it meant to;
the stored-log single-number command form gained the explicit
compatibility test it deserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 11:56:55 -04:00
co-authored by Claude Fable 5
parent f62fcf2510
commit 695307daa8
24 changed files with 240 additions and 716 deletions
@@ -0,0 +1,336 @@
import { describe, expect, it } from "vitest";
import { applyCommand, activePlayer, boardView, gameLos, sustainedOn, viewFor } from "../src";
import { cellKey, edgeKey, type Cell } from "../src/board";
import type { CardInstance } from "../src/cards";
import { newGame, must, giveCard, toRound2, emptyNeighborCell } from "./helpers";
describe("around the corner", () => {
it("bends line of sight past a wall that blocks a straight cast", () => {
let { state } = newGame();
state = toRound2(state);
const attacker = activePlayer(state);
const defender = state.players.find((p) => p.id !== attacker.id)!;
// Find a hidden cell (no direct LOS) that IS reachable with one bend.
const view = boardView(state);
let hidden: Cell | null = null;
outer: for (const key of Object.keys(view.cells)) {
const [x, y] = key.split(",").map(Number) as [number, number];
const cell = { x, y };
if (gameLos(state, attacker.position, cell)) continue;
for (const midKey of Object.keys(view.cells)) {
const [mx, my] = midKey.split(",").map(Number) as [number, number];
const mid = { x: mx, y: my };
if (gameLos(state, attacker.position, mid) && gameLos(state, mid, cell)) {
hidden = cell;
break outer;
}
}
}
expect(hidden).not.toBeNull();
defender.position = hidden!;
const fb = giveCard(state, attacker.id, "fireball", "F", 0);
giveCard(state, attacker.id, "around-the-corner", "ATC", 1);
// Straight cast: refused.
const straight = applyCommand(state, attacker.id, {
type: "cast", instanceId: fb.instanceId, target: { kind: "player", playerId: defender.id },
});
expect(straight.ok).toBe(false);
// Bent cast: lands.
state = must(state, attacker.id, {
type: "cast", instanceId: fb.instanceId, aroundCornerInstanceId: "around-the-corner#ATC",
target: { kind: "player", playerId: defender.id },
});
state = must(state, defender.id, { type: "pass" });
expect(state.players.find((p) => p.id === defender.id)!.life).toBe(10);
});
});
describe("blind", () => {
it("blinded movement lurches in rolled directions and bumps cost movement", () => {
let { state } = newGame(7);
state = toRound2(state);
const attacker = activePlayer(state);
const defender = state.players.find((p) => p.id !== attacker.id)!;
defender.position = { ...attacker.position };
const bl = giveCard(state, attacker.id, "blind");
giveCard(state, attacker.id, "number-3", "N", 1);
state = must(state, attacker.id, {
type: "cast", instanceId: bl.instanceId, numberInstanceIds: ["number-3#N"],
target: { kind: "player", playerId: defender.id },
});
state = must(state, defender.id, { type: "pass" });
expect(sustainedOn(state, defender.id, "blind").length).toBe(1);
state = must(state, attacker.id, { type: "endTurn", draw: 0 });
// Every move consumes exactly one movement point whether it lands or bumps.
const before = state.turn.movementUsed;
state = must(state, defender.id, { type: "move", direction: "N" });
expect(state.turn.movementUsed).toBe(before + 1);
});
it("blinded casts fly in a rolled direction and can miss entirely", () => {
// Across seeds: a blinded caster aiming at a real target sometimes hits
// (roll matches), usually misses (attack dissipates, card still spent).
let hits = 0, misses = 0;
for (let seed = 1; seed <= 10; seed++) {
let { state } = newGame(seed);
state = toRound2(state);
const attacker = activePlayer(state);
const defender = state.players.find((p) => p.id !== attacker.id)!;
// Stand the defender one cell east-ish with LOS.
const spot = emptyNeighborCell(state, attacker.position);
defender.position = spot.cell;
state.sustained.push({
id: "fx-test", cardId: "blind", casterId: defender.id,
targetId: attacker.id, remainingTurns: 3, data: {},
});
const fb = giveCard(state, attacker.id, "fireball", "F", 0);
const result = applyCommand(state, attacker.id, {
type: "cast", instanceId: fb.instanceId, target: { kind: "player", playerId: defender.id },
});
expect(result.ok).toBe(true);
if (!result.ok) continue;
if (result.state.stack) {
hits++; // the roll matched: attack proceeds normally
} else {
misses++;
expect(result.state.turn.attackUsed).toBe(true); // card spent anyway
}
}
expect(hits + misses).toBe(10);
expect(misses).toBeGreaterThan(0);
});
});
describe("ugly", () => {
it("drives every visible opponent out of line of sight", () => {
let { state } = newGame();
const caster = activePlayer(state);
const opp = state.players.find((p) => p.id !== caster.id)!;
opp.position = { ...caster.position }; // same square: definitely in LOS
const ug = giveCard(state, caster.id, "ugly");
state = must(state, caster.id, { type: "cast", instanceId: ug.instanceId });
const after = state.players.find((p) => p.id === opp.id)!;
expect(gameLos(state, state.players.find((p) => p.id === caster.id)!.position, after.position)).toBe(false);
});
});
describe("illusion wall", () => {
function setupIllusion(seed = 42) {
let { state } = newGame(seed);
const caster = activePlayer(state);
const spot = emptyNeighborCell(state, caster.position);
const key = edgeKey(caster.position, spot.side);
const iw = giveCard(state, caster.id, "illusion-wall");
state = must(state, caster.id, {
type: "cast", instanceId: iw.instanceId,
target: { kind: "edge", cell: caster.position, side: spot.side },
});
return { state, caster: caster.id, key, side: spot.side, cell: spot.cell };
}
it("the caster walks through their own illusion; others see a wall", () => {
const { state, caster, key, side } = setupIllusion();
// Caster's view knows it's fake; the opponent's view shows a wall.
const casterView = viewFor(state, caster);
expect(casterView.knownIllusionEdges).toContain(key);
const other = state.players.find((p) => p.id !== caster)!.id;
const otherView = viewFor(state, other);
expect(otherView.board.edges[key]).toBe("wall");
// And the caster can move through it freely.
const after = applyCommand(state, caster, { type: "move", direction: side });
expect(after.ok).toBe(true);
});
it("opponents test the illusion when they bump it — some see through, some believe", () => {
let believed = 0, sawThrough = 0;
for (let seed = 1; seed <= 12; seed++) {
const { state, caster, side } = setupIllusion(seed);
const other = state.players.find((p) => p.id !== caster)!;
other.position = { ...state.players.find((p) => p.id === caster)!.position };
let s = must(state, caster, { type: "endTurn", draw: 0 });
const result = applyCommand(s, other.id, { type: "move", direction: side });
if (result.ok) sawThrough++;
else believed++;
}
expect(believed + sawThrough).toBe(12);
expect(believed).toBeGreaterThan(0);
expect(sawThrough).toBeGreaterThan(0);
});
it("dispel creation removes an illusion wall", () => {
let { state, caster, key, side } = setupIllusion();
const me = state.players.find((p) => p.id === caster)!;
const dc = giveCard(state, caster, "dispel-creation", "DC", 1);
state = must(state, caster, {
type: "cast", instanceId: dc.instanceId,
target: { kind: "edge", cell: me.position, side },
});
expect(state.illusionWalls[key]).toBeUndefined();
});
});
describe("sector manipulation", () => {
it("rotate sector turns walls and pieces together; the home stays centered", () => {
let { state } = newGame();
const me = activePlayer(state);
const idx = state.board.placements.findIndex(
(p) => me.position.x >= p.origin.x && me.position.x < p.origin.x + 5 &&
me.position.y >= p.origin.y && me.position.y < p.origin.y + 5,
);
const wallsBefore = Object.values(boardView(state).edges).filter((e) => e === "wall").length;
const homeBefore = { ...me.home };
const rs = giveCard(state, me.id, "rotate-sector");
state = must(state, me.id, {
type: "cast", instanceId: rs.instanceId,
target: { kind: "cell", cell: me.position }, params: { clockwise: true },
});
const after = state.players.find((p) => p.id === me.id)!;
// Home star is the exact center: rotation cannot move it.
expect(cellKey(after.home)).toBe(cellKey(homeBefore));
// Wherever they stood, rotation keeps them inside the sector.
const p = state.board.placements[idx]!;
expect(after.position.x).toBeGreaterThanOrEqual(p.origin.x);
expect(after.position.x).toBeLessThan(p.origin.x + 5);
// Wall count is invariant under rotation.
const wallsAfter = Object.values(boardView(state).edges).filter((e) => e === "wall").length;
expect(wallsAfter).toBe(wallsBefore);
// Treasures rotated with the sector (diagonal flips to the other diagonal
// or stays, but they remain on the board inside the sector).
for (const t of state.treasures.filter((t) => t.owner === after.id)) {
expect(state.board.cells[cellKey(t.position!)]).toBe(true);
}
});
it("relocate sector slides everything and keeps adjacency", () => {
let { state } = newGame(); // 2 players: 5x10 column, sectors at y=0 and y=5
const me = activePlayer(state);
const idx = state.board.placements.findIndex(
(p) => me.position.y >= p.origin.y && me.position.y < p.origin.y + 5,
);
const myOrigin = state.board.placements[idx]!.origin;
const otherIdx = idx === 0 ? 1 : 0;
const otherOrigin = state.board.placements[otherIdx]!.origin;
// Move my sector to the EAST side of the other sector (still adjacent).
const dest = { x: otherOrigin.x + 5, y: otherOrigin.y };
const posBefore = { ...me.position };
const homeBefore = { ...me.home };
const rel = giveCard(state, me.id, "relocate-sector");
state = must(state, me.id, {
type: "cast", instanceId: rel.instanceId,
target: { kind: "cell", cell: dest }, params: { cell: me.position },
});
const after = state.players.find((p) => p.id === me.id)!;
const dx = dest.x - myOrigin.x, dy = dest.y - myOrigin.y;
expect(after.position).toEqual({ x: posBefore.x + dx, y: posBefore.y + dy });
expect(after.home).toEqual({ x: homeBefore.x + dx, y: homeBefore.y + dy });
expect(state.board.placements[idx]!.origin).toEqual(dest);
// The map reassembled: every treasure/wizard cell exists on the new board.
for (const t of state.treasures) {
if (t.position) expect(state.board.cells[cellKey(t.position)]).toBe(true);
}
// An island move is refused.
const rel2 = giveCard(state, me.id, "relocate-sector", "R2");
const refused = applyCommand(state, me.id, {
type: "cast", instanceId: rel2.instanceId,
target: { kind: "cell", cell: { x: 40, y: 40 } }, params: { cell: after.position },
});
expect(refused.ok).toBe(false);
});
});
describe("6e card-face corrections", () => {
it("no spell does not block pick lock — 'This is not a spell'", () => {
let { state } = newGame();
state = toRound2(state);
const attacker = activePlayer(state);
const defender = state.players.find((p) => p.id !== attacker.id)!;
defender.position = { ...attacker.position };
const ns = giveCard(state, attacker.id, "no-spell");
giveCard(state, attacker.id, "number-3", "N", 1);
state = must(state, attacker.id, {
type: "cast", instanceId: ns.instanceId, numberInstanceIds: ["number-3#N"],
target: { kind: "player", playerId: defender.id },
});
state = must(state, defender.id, { type: "pass" });
state = must(state, attacker.id, { type: "endTurn", draw: 0 });
// The silenced defender cannot cast a spell...
const fb = giveCard(state, defender.id, "fireball", "F", 0);
expect(applyCommand(state, defender.id, {
type: "cast", instanceId: fb.instanceId, target: { kind: "player", playerId: attacker.id },
}).ok).toBe(false);
// ...but picking a lock is a physical action, not a spell.
const view = boardView(state);
const doorEntry = Object.entries(view.edges).find(([, st]) => st === "door")!;
const [kind, coords] = doorEntry[0].split(":") as [string, string];
const [dx, dy] = coords.split(",").map(Number) as [number, number];
const d = state.players.find((p) => p.id === defender.id)!;
d.position = { x: dx, y: dy };
const pl = giveCard(state, defender.id, "pick-lock", "PL", 1);
const result = applyCommand(state, defender.id, {
type: "cast", instanceId: pl.instanceId,
target: { kind: "edge", cell: { x: dx, y: dy }, side: kind === "V" ? "E" : "S" },
});
expect(result.ok).toBe(true);
});
it("blinded punches flail on the die — 'engage in combat'", () => {
let hits = 0, misses = 0;
for (let seed = 1; seed <= 10; seed++) {
let { state } = newGame(seed);
state = toRound2(state);
const attacker = activePlayer(state);
const defender = state.players.find((p) => p.id !== attacker.id)!;
defender.position = { ...attacker.position };
state.sustained.push({
id: "fx-test", cardId: "blind", casterId: defender.id,
targetId: attacker.id, remainingTurns: 3, data: {},
});
const result = applyCommand(state, attacker.id, { type: "punch", targetId: defender.id });
expect(result.ok).toBe(true);
if (!result.ok) continue;
if (result.state.stack) hits++;
else {
misses++;
expect(result.state.turn.attackUsed).toBe(true); // the flail spends the attack
}
}
expect(hits + misses).toBe(10);
expect(misses).toBeGreaterThan(0);
});
it("wall of fire, as a counteraction, stops a waterbolt cold", () => {
let { state } = newGame();
state = toRound2(state);
const attacker = activePlayer(state);
const defender = state.players.find((p) => p.id !== attacker.id)!;
defender.position = { ...attacker.position };
const wb = giveCard(state, attacker.id, "waterbolt");
giveCard(state, attacker.id, "number-4", "N", 1);
giveCard(state, defender.id, "wall-of-fire", "WOF", 0);
state = must(state, attacker.id, {
type: "cast", instanceId: wb.instanceId, numberInstanceIds: ["number-4#N"],
target: { kind: "player", playerId: defender.id },
params: { damage: 4, knockback: 0 },
});
state = must(state, defender.id, { type: "counteract", instanceId: "wall-of-fire#WOF" });
state = must(state, attacker.id, { type: "pass" });
state = must(state, defender.id, { type: "pass" });
expect(state.players.find((p) => p.id === defender.id)!.life).toBe(15);
// But it cannot counter a fireball.
state = must(state, attacker.id, { type: "endTurn", draw: 0 });
state = must(state, defender.id, { type: "endTurn", draw: 0 });
const fb = giveCard(state, attacker.id, "fireball", "F", 0);
giveCard(state, defender.id, "wall-of-fire", "WOF2", 0);
state = must(state, attacker.id, {
type: "cast", instanceId: fb.instanceId, target: { kind: "player", playerId: defender.id },
});
expect(applyCommand(state, defender.id, { type: "counteract", instanceId: "wall-of-fire#WOF2" }).ok).toBe(false);
});
});