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:
1 parent
944e4e8306
commit
0be89a1dfe
28 files changed
+227
-340
No files matched your search
@@ -31,7 +31,7 @@
|
||||
<h2>The hall</h2>
|
||||
<figure><img src="/guide/hall.png" alt="The hall, with a name field, the two table choices, the button to play the drake, and the opening position drawn beside them" loading="lazy" /></figure>
|
||||
<p>Write your name and choose your company. <strong>Play now against a drake</strong> seats you across from the house player and begins at once. <strong>Open a table</strong> lays a table and hands you a link and a four-letter code to send to a friend. <strong>Join one</strong> takes a code someone sent you.</p>
|
||||
<p>Two choices sit above the buttons. <em>You play</em> picks your side, and Gold moves first. <em>The Sylphs</em> picks where the six Sylphs start. As Gygax printed it, Gold's Dragon can take an undefended Sylph on the very first move, which a reader of the Chess Variant Pages took for a misprint. The shifted setup moves the Sylphs one file over, out of reach. Beneath the buttons, <em>your games</em> lists every table this browser holds a seat at and whose move it is.</p>
|
||||
<p>Two choices sit above the buttons. <em>You play</em> picks your side, and Gold moves first. <em>The Sylphs</em> picks where the six Sylphs start. As Gygax printed it, Gold's Dragon can take an undefended Sylph on the very first move, which a reader of the Chess Variant Pages took for a misprint. The shifted setup moves the Sylphs one file over, out of reach. <em>Your games</em>, which comes first once you have one, lists every table this browser holds a seat at and whose move it is.</p>
|
||||
</section>
|
||||
|
||||
<section id="table">
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
import PiecePanel from '$lib/components/PiecePanel.svelte';
|
||||
import { SCENES, sceneCells } from '$lib/game/lessons';
|
||||
import { tell } from '$lib/game/chronicle';
|
||||
import { createGame, playOn, square, squareName, type Cell, type Move } from '$lib/game';
|
||||
import { kindOf, inCheck, hasLegalMove, other } from '$lib/game/board';
|
||||
import { createGame, playOn, square, kindOf, inCheck, hasLegalMove, other, SIDE_NAMES, type Cell, type Move, type Side } from '$lib/game';
|
||||
|
||||
const room = $derived((page.url.searchParams.get('room') ?? '').toUpperCase());
|
||||
const withRoom = (path: string) => (room ? `${path}?room=${room}` : path);
|
||||
@@ -31,17 +30,17 @@
|
||||
const right = scene.moves.some(([a, b]) => square(a) === from && square(b) === to);
|
||||
const played = playOn(cells, from, to);
|
||||
const move: Move = { by: scene.side, from, to, kind: kindOf(cells[from]), captured: played.captured, afar: played.afar, promoted: played.promoted, check: inCheck(played.cells, other(scene.side)) };
|
||||
const name = (s: typeof scene.side) => (s === scene.side ? 'You' : 'Scarlet');
|
||||
let told = tell(cells, move, name).replace(/You’s/g, 'Your');
|
||||
const owner = (s: Side) => (s === scene.side ? 'Your' : `${SIDE_NAMES[s]}’s`);
|
||||
let told = tell(cells, move, owner);
|
||||
const mated = move.check && !hasLegalMove(played.cells, other(scene.side), 'printed');
|
||||
if (mated) told += ' Checkmate.';
|
||||
outcome = { told, text: right ? scene.after : (scene.aside ?? scene.after), right };
|
||||
cells = played.cells;
|
||||
}
|
||||
|
||||
/** The panel reads a game; this one is the scene's. */
|
||||
/** The board and the panel read a game; this one is the scene's, with the reader on the scene's side. */
|
||||
const g = $derived.by(() => {
|
||||
const s = createGame({ A: 'You', B: 'Scarlet' });
|
||||
const s = createGame({ A: scene.side === 'gold' ? 'You' : SIDE_NAMES.gold, B: scene.side === 'scarlet' ? 'You' : SIDE_NAMES.scarlet });
|
||||
s.cells = cells;
|
||||
s.toMove = scene.side;
|
||||
return s;
|
||||
@@ -52,7 +51,7 @@
|
||||
<title>Wyrm: learn the pieces</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="lesson">
|
||||
<main class="reading wide">
|
||||
<header>
|
||||
{#if room}<a class="back" href={`/join/${room}`}>Back to your game</a>{' · '}{/if}<a class="back" href="/">Back to the hall</a> · <a class="back" href={withRoom('/guide')}>How to play</a> · <a class="back" href={withRoom('/rules')}>The rules</a>
|
||||
<h1>Learn the pieces</h1>
|
||||
@@ -67,7 +66,7 @@
|
||||
|
||||
<section class="stage">
|
||||
<div class="boards">
|
||||
<LessonBoard {cells} side={scene.side} levels={scene.levels} locked={outcome !== null} onMove={moved} onInspect={(at) => (inspected = at)} />
|
||||
<LessonBoard {g} levels={scene.levels} locked={outcome !== null} onMove={moved} bind:inspected />
|
||||
</div>
|
||||
<div class="words">
|
||||
<p class="eyebrow">Scene {n + 1} of {SCENES.length}</p>
|
||||
@@ -97,28 +96,6 @@
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.lesson {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem var(--gutter) 4rem;
|
||||
}
|
||||
|
||||
.lesson a.back {
|
||||
color: var(--frost);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.4rem;
|
||||
font-weight: 300;
|
||||
margin: 0.5rem 0 0.3rem;
|
||||
}
|
||||
|
||||
.lede {
|
||||
color: var(--bone-dim);
|
||||
max-width: 44em;
|
||||
}
|
||||
|
||||
.scenes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import PiecePanel from '$lib/components/PiecePanel.svelte';
|
||||
import { CALLOUTS, TOLD_MOVES, toldStates } from '$lib/game/told';
|
||||
import { narrate } from '$lib/game/chronicle';
|
||||
import { notation, kingSquare, inCheck, movesFrom, legalMovesFrom, levelOf, fileOf, rankOf, idx, FILES, RANKS, LEVELS } from '$lib/game';
|
||||
import { notation, kingSquare, inCheck, movesFrom, legalMovesFrom, sideOf } from '$lib/game';
|
||||
import { prefs, reducedMotion } from '$lib/prefs.svelte';
|
||||
|
||||
const states = toldStates();
|
||||
@@ -27,8 +27,7 @@
|
||||
let inspected = $state<number | null>(null);
|
||||
const reach = $derived.by(() => {
|
||||
if (inspected === null || g.cells[inspected] === '' || g.over) return [];
|
||||
const side = g.cells[inspected][0] === 'g' ? 'gold' : 'scarlet';
|
||||
return side === g.toMove ? legalMovesFrom(g.cells, inspected, g.setup) : movesFrom(g.cells, inspected, g.setup);
|
||||
return sideOf(g.cells[inspected]) === g.toMove ? legalMovesFrom(g.cells, inspected, g.setup) : movesFrom(g.cells, inspected, g.setup);
|
||||
});
|
||||
|
||||
function go(n: number) {
|
||||
@@ -38,21 +37,6 @@
|
||||
function tap(i: number) {
|
||||
inspected = g.cells[i] !== '' && inspected !== i ? i : null;
|
||||
}
|
||||
function squareKey(e: KeyboardEvent, i: number) {
|
||||
const step: Record<string, [number, number, number]> = { ArrowLeft: [-1, 0, 0], ArrowRight: [1, 0, 0], ArrowUp: [0, 1, 0], ArrowDown: [0, -1, 0], PageUp: [0, 0, 1], PageDown: [0, 0, -1] };
|
||||
if (e.key in step) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const [df, dr, dl] = step[e.key];
|
||||
const nf = fileOf(i) + df;
|
||||
const nr = rankOf(i) + dr;
|
||||
const nl = levelOf(i) + dl;
|
||||
if (nf >= 0 && nf < FILES && nr >= 0 && nr < RANKS && nl >= 1 && nl <= LEVELS) document.querySelector<SVGRectElement>(`rect.square[data-i="${idx(nl, nf, nr)}"]`)?.focus();
|
||||
} else if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
tap(i);
|
||||
} else if (e.key === 'Escape') inspected = null;
|
||||
}
|
||||
/** Left and right step the game, unless the keyboard is on a square, where the arrows walk the board. */
|
||||
function key(e: KeyboardEvent) {
|
||||
if ((e.target as HTMLElement | null)?.tagName === 'rect') return;
|
||||
@@ -69,7 +53,7 @@
|
||||
|
||||
<svelte:window onkeydown={key} />
|
||||
|
||||
<main class="lesson">
|
||||
<main class="reading wide">
|
||||
<header>
|
||||
<a class="back" href="/">Back to the hall</a> · <a class="back" href="/learn">Learn the pieces</a> · <a class="back" href="/guide">How to play</a> · <a class="back" href="/rules">The rules</a>
|
||||
<h1>A game told</h1>
|
||||
@@ -78,12 +62,12 @@
|
||||
|
||||
<section class="stage">
|
||||
<div class="boards">
|
||||
<BoardArtwork {g} {showCoords} {shownSquares} {shownCaptured} {checkSquare} selected={inspected} {reach} {calm} yourMove={true} {tap} key={squareKey} />
|
||||
<BoardArtwork {g} {showCoords} {shownSquares} {shownCaptured} {checkSquare} selected={inspected} {reach} {calm} {tap} escape={() => (inspected = null)} />
|
||||
</div>
|
||||
<div class="words">
|
||||
<p class="eyebrow">{ply === 0 ? 'Before the first move' : `Move ${ply} of ${TOLD_MOVES.length}`}{#if move} · {notation(move)}{/if}</p>
|
||||
{#if telling}
|
||||
<ul class="told">
|
||||
<ul class="told telling">
|
||||
{#each telling.lines as l, i (i)}<li class={l.kind}>{l.text}</li>{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
@@ -107,26 +91,7 @@
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.lesson {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem var(--gutter) 4rem;
|
||||
}
|
||||
|
||||
.lesson a.back {
|
||||
color: var(--frost);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.4rem;
|
||||
font-weight: 300;
|
||||
margin: 0.5rem 0 0.3rem;
|
||||
}
|
||||
|
||||
.lede {
|
||||
color: var(--bone-dim);
|
||||
max-width: 44em;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
@@ -167,25 +132,6 @@
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.told li.capture {
|
||||
color: var(--ember);
|
||||
}
|
||||
|
||||
.told li.check,
|
||||
.told li.freeze,
|
||||
.told li.promotion {
|
||||
color: var(--frost);
|
||||
}
|
||||
|
||||
.told li.note {
|
||||
color: var(--bone-dim);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.told li.end {
|
||||
color: var(--blood);
|
||||
}
|
||||
|
||||
.callout {
|
||||
margin-top: 0.8rem;
|
||||
color: var(--bone);
|
||||
|
||||
Reference in new issue
Block a user