Credibility pass: the code reads as one hand's work. Dead plumbing and copies removed, four claims the source does not support corrected, and inherited kit text re-read for this game

Engine: the facade re-exports the board in one block; describe() and the
dead letters preference, void ply, the King-taken line, the identity maps
and the unused imports are gone; SIDE_NAMES is the one spelling of a side;
the drake's hunter comment no longer claims only four kinds reach the air.
Prose: no shatranj and no sitting duck, which Gygax never wrote; the Thief
statistic and the Basilisk's "one trick" are gone; lessons say what the
board shows instead of repeating the panel; a Thief's leap counts four.
Browser: one keyboard walk, in BoardArtwork; the lesson board and page share
one game and one inspected piece; the chronicle's possessive is told, not
patched by a regex; the chronicle colours and the reading layout are global
rules instead of three copies; the guide and the about panel say what the
code and the article actually do.
Server and docs: the room server states that it is the kit's, kept whole;
the skills, the deploy README and the share card name files that exist.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-10-26 22:38:00 -04:00
1 parent 944e4e8306
commit 0be89a1dfe
28 files changed
+227 -340

No files matched your search

+30 -9
View File
@@ -13,12 +13,12 @@
<script lang="ts">
// The three boards, drawn one above the other: the air, the land and the
// underworld, each twelve by eight in the colours Gygax suggested, with
// the pieces as discs bearing his letters. Shared by the table, the hall's
// still of the opening and the share artwork.
import { FILES, RANKS, PER_LEVEL, fileOf, rankOf, levelOf, isLight, kindOf, sideOf, frozen, squareName, LETTERS, NAMES, type State } from '$lib/game';
// the pieces as discs bearing his letters. Shared by the table, the
// lessons and the hall's still of the opening.
import { FILES, RANKS, PER_LEVEL, fileOf, rankOf, levelOf, idx, isLight, kindOf, sideOf, frozen, squareName, LETTERS, NAMES, SIDE_NAMES, type State } from '$lib/game';
let {
g, tokens, levels = [3, 2, 1], showCoords = false, flipped = false, selected = null, targets = [], reach = [], shownSquares = [], shownCaptured = [], checkSquare = -1,
yourMove = false, calm = true, decorative = false, tap = () => {}, key = () => {}
calm = true, decorative = false, tap = () => {}, escape = () => {}
}: {
g: State;
tokens?: Token[];
@@ -34,13 +34,34 @@
shownSquares?: number[];
shownCaptured?: number[];
checkSquare?: number;
yourMove?: boolean;
calm?: boolean;
/** An illustration: no roles, no tab stops, nothing to tap. */
decorative?: boolean;
tap?: (i: number) => void | Promise<void>;
key?: (e: KeyboardEvent, i: number) => void;
/** Escape pressed on a square: the caller lets go of whatever it was doing. */
escape?: () => void;
} = $props();
let svg: SVGSVGElement | undefined = $state();
/** Arrows walk a board the way it is drawn, Page Up and Page Down climb and descend, Enter picks up and sets down. */
function key(e: KeyboardEvent, i: number) {
const step: Record<string, [number, number, number]> = {
ArrowLeft: [flipped ? 1 : -1, 0, 0], ArrowRight: [flipped ? -1 : 1, 0, 0], ArrowUp: [0, flipped ? -1 : 1, 0], ArrowDown: [0, flipped ? 1 : -1, 0],
PageUp: [0, 0, 1], PageDown: [0, 0, -1]
};
if (e.key in step) {
e.preventDefault();
const [df, dr, dl] = step[e.key];
const f = fileOf(i) + df;
const r = rankOf(i) + dr;
const lv = levelOf(i) + dl;
if (f >= 0 && f < FILES && r >= 0 && r < RANKS && levels.includes(lv)) svg?.querySelector<SVGRectElement>(`rect.square[data-i="${idx(lv, f, r)}"]`)?.focus();
} else if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
void tap(i);
} else if (e.key === 'Escape') escape();
}
const artworkTokens = $derived(tokens ?? g.cells.flatMap((piece, at) => (piece === '' ? [] : [{ id: at, piece, at, dying: false }])));
const CELL = 40;
const PAD_L = $derived(showCoords ? 18 : 6);
@@ -70,14 +91,14 @@
const describe = (i: number) => {
const piece = g.cells[i];
let text = squareName(i);
if (piece !== '') text += `, ${sideOf(piece) === 'gold' ? 'Gold' : 'Scarlet'} ${NAMES[kindOf(piece)]}${frozen(g.cells, i) ? ', frozen' : ''}`;
if (piece !== '') text += `, ${SIDE_NAMES[sideOf(piece)!]} ${NAMES[kindOf(piece)]}${frozen(g.cells, i) ? ', frozen' : ''}`;
if (targets.includes(i)) text += ', a legal move';
if (i === checkSquare) text += ', in check';
return text;
};
</script>
<svg xmlns="http://www.w3.org/2000/svg" class="artwork" class:calm viewBox="0 0 {width} {height}" role={decorative ? 'img' : 'grid'} aria-label={decorative ? 'The three boards in their opening position' : 'The three boards'}>
<svg xmlns="http://www.w3.org/2000/svg" bind:this={svg} class="artwork" class:calm viewBox="0 0 {width} {height}" role={decorative ? 'img' : 'grid'} aria-label={decorative ? 'The three boards in their opening position' : 'The three boards'}>
{#each levels as lv, slot (lv)}
{@const top = slot * (boardHeight + GAP)}
<g class="world" data-level={lv}>
@@ -114,7 +135,7 @@
role={decorative ? undefined : 'gridcell'}
aria-label={decorative ? undefined : describe(i)}
aria-selected={decorative ? undefined : selected === i}
tabindex={decorative ? undefined : yourMove ? 0 : -1}
tabindex={decorative ? undefined : 0}
onclick={decorative ? undefined : () => tap(i)}
onkeydown={decorative ? undefined : (e) => key(e, i)}
/>