From b3234ad19da0b94843c5529ee1b1b9f194175193 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Mon, 26 Oct 2026 20:14:29 -0400 Subject: [PATCH] In the told game, tap any piece to see where it could go next and read about it, before the game shows what it did Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx --- src/routes/learn/game/+page.svelte | 47 ++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/routes/learn/game/+page.svelte b/src/routes/learn/game/+page.svelte index 2ff896b..89510e0 100644 --- a/src/routes/learn/game/+page.svelte +++ b/src/routes/learn/game/+page.svelte @@ -2,9 +2,10 @@ // A game told: the boards, one move at a time, each move as the chronicle // tells it, and a callout where there is something to see. import BoardArtwork from '$lib/components/BoardArtwork.svelte'; + 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 } from '$lib/game'; + import { notation, kingSquare, inCheck, movesFrom, legalMovesFrom, levelOf, fileOf, rankOf, idx, FILES, RANKS, LEVELS } from '$lib/game'; import { prefs, reducedMotion } from '$lib/prefs.svelte'; const states = toldStates(); @@ -22,10 +23,39 @@ const showCoords = $derived(prefs.coordinates !== false); const marks = Object.keys(CALLOUTS).map(Number).filter((n) => n > 0); + /** A piece tapped to read about it and to see where it could go from here, before the game shows what it did. */ + let inspected = $state(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); + }); + function go(n: number) { ply = Math.max(0, Math.min(TOLD_MOVES.length, n)); + inspected = null; } + function tap(i: number) { + inspected = g.cells[i] !== '' && inspected !== i ? i : null; + } + function squareKey(e: KeyboardEvent, i: number) { + const step: Record = { 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(`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; if (e.key === 'ArrowRight') go(ply + 1); else if (e.key === 'ArrowLeft') go(ply - 1); else return; @@ -43,12 +73,12 @@
Back to the hall · Learn the pieces · How to play · The rules

A game told

-

Twenty-seven moves, written to show what a first game should see. A Dragon leaves its roost too soon and a Hero kills it, a Basilisk walks slowly under a piece that came too deep and freezes it, a Griffon swoops, and a Dragon gives checkmate from the air. Step through it with the buttons or the arrow keys.

+

Twenty-seven moves, written to show what a first game should see. A Dragon leaves its roost too soon and a Hero kills it, a Basilisk walks slowly under a piece that came too deep and freezes it, a Griffon swoops, and a Dragon gives checkmate from the air. Step through it with the buttons or the arrow keys, and tap any piece to see where it could go next and to read about it.

- +

{ply === 0 ? 'Before the first move' : `Move ${ply} of ${TOLD_MOVES.length}`}{#if move} · {notation(move)}{/if}

@@ -67,6 +97,11 @@ Jump to a callout: {#each marks as n (n)}{/each}

+ {#if inspected !== null && g.cells[inspected] !== ''} +
+ (inspected = null)} /> +
+ {/if}
@@ -119,6 +154,8 @@ .words { position: sticky; top: 0.5rem; + max-height: calc(100vh - 1rem); + overflow-y: auto; } .told { @@ -182,4 +219,8 @@ .mark.current { color: var(--ember); } + + .panel { + margin-top: 1rem; + }