Tap any piece and a panel tells what it is, what it can do from where it stands, how it moves, why Gygax put it in, and how to play it

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 18:12:46 -04:00
1 parent 9e223c5dcf
commit 4212bf9419
6 files changed
+344 -5

No files matched your search

+52 -4
View File
@@ -4,9 +4,10 @@
// strip says whose move it is; beside them the record, each move a click // strip says whose move it is; beside them the record, each move a click
// from being shown on the boards, and the talk. // from being shown on the boards, and the talk.
import TableTalk from './TableTalk.svelte'; import TableTalk from './TableTalk.svelte';
import PiecePanel from './PiecePanel.svelte';
import BoardArtwork, { type Token } from './BoardArtwork.svelte'; import BoardArtwork, { type Token } from './BoardArtwork.svelte';
import { import {
describe, legalMovesFrom, squareName, sideOf, kindOf, kingSquare, inCheck, levelOf, fileOf, rankOf, idx, frozen, notation, NAMES, FILES, RANKS, LEVELS, describe, legalMovesFrom, movesFrom, squareName, sideOf, kindOf, kingSquare, inCheck, levelOf, fileOf, rankOf, idx, frozen, notation, NAMES, FILES, RANKS, LEVELS,
type Move, type Side type Move, type Side
} from '$lib/game'; } from '$lib/game';
import type { Room } from '$lib/net/room.svelte'; import type { Room } from '$lib/net/room.svelte';
@@ -100,6 +101,15 @@
// --- Choosing and moving --------------------------------------------------- // --- Choosing and moving ---------------------------------------------------
let selected = $state<number | null>(null); let selected = $state<number | null>(null);
const targets = $derived(selected === null ? [] : legalMovesFrom(g.cells, selected, g.setup)); const targets = $derived(selected === null ? [] : legalMovesFrom(g.cells, selected, g.setup));
/** Any piece, tapped to read about it; the panel beside the boards tells what it is and what it can do from there. */
let inspected = $state<number | null>(null);
$effect(() => {
// A piece that moved or was taken is no longer where the panel looked.
void g.moves.length;
inspected = null;
});
/** Where the inspected piece could go, shown faintly when it is not yours to move. */
const reach = $derived(inspected !== null && inspected !== selected && g.cells[inspected] !== '' && !g.over ? movesFrom(g.cells, inspected, g.setup) : []);
let liveText = $state(''); let liveText = $state('');
function announce(text: string) { function announce(text: string) {
liveText = text; liveText = text;
@@ -117,8 +127,14 @@
const wordFor = (i: number) => `${sideOf(g.cells[i]) === 'gold' ? 'Gold' : 'Scarlet'} ${NAMES[kindOf(g.cells[i])]}`; const wordFor = (i: number) => `${sideOf(g.cells[i]) === 'gold' ? 'Gold' : 'Scarlet'} ${NAMES[kindOf(g.cells[i])]}`;
async function tap(i: number) { async function tap(i: number) {
if (!yourMove || room.sending || animating) return; if (room.sending || animating) return;
reviewing = null; reviewing = null;
if (!yourMove) {
// Not your move, or no seat: a tap on a piece still opens its panel.
inspected = g.cells[i] !== '' ? (inspected === i ? null : i) : null;
if (inspected !== null) announce(`${wordFor(i)} on ${label(i)}. About this piece is shown beside the boards.`);
return;
}
if (selected !== null && targets.includes(i)) { if (selected !== null && targets.includes(i)) {
if (prefs.confirmMoves === true) { if (prefs.confirmMoves === true) {
pending = { from: selected, to: i }; pending = { from: selected, to: i };
@@ -129,8 +145,10 @@
return; return;
} }
pending = null; pending = null;
inspected = g.cells[i] !== '' ? (inspected === i && selected === i ? null : i) : null;
if (sideOf(g.cells[i]) === mySide) { if (sideOf(g.cells[i]) === mySide) {
selected = selected === i ? null : i; selected = selected === i ? null : i;
if (selected === null) inspected = null;
if (selected !== null) { if (selected !== null) {
const to = legalMovesFrom(g.cells, i, g.setup); const to = legalMovesFrom(g.cells, i, g.setup);
const why = frozen(g.cells, i) ? ' It is frozen by the Basilisk beneath it.' : ''; const why = frozen(g.cells, i) ? ' It is frozen by the Basilisk beneath it.' : '';
@@ -166,6 +184,7 @@
void tap(i); void tap(i);
} else if (e.key === 'Escape') { } else if (e.key === 'Escape') {
selected = null; selected = null;
inspected = null;
reviewing = null; reviewing = null;
pending = null; pending = null;
announce('Selection cleared.'); announce('Selection cleared.');
@@ -207,7 +226,7 @@
{#if room.error}<p class="warning">{room.error}</p>{/if} {#if room.error}<p class="warning">{room.error}</p>{/if}
<div class="frame"> <div class="frame">
<BoardArtwork {g} {tokens} {showCoords} {flipped} {selected} {targets} {shownSquares} {shownCaptured} {checkSquare} {yourMove} {calm} {tap} {key} /> <BoardArtwork {g} {tokens} {showCoords} {flipped} {selected} {targets} {reach} {shownSquares} {shownCaptured} {checkSquare} yourMove={true} {calm} {tap} {key} />
</div> </div>
<p class="visually-hidden" aria-live="polite">{liveText}</p> <p class="visually-hidden" aria-live="polite">{liveText}</p>
@@ -216,11 +235,16 @@
<a class="commit" href="/">Back to the hall</a> <a class="commit" href="/">Back to the hall</a>
</div> </div>
{:else if yourMove} {:else if yourMove}
<p class="muted small how">Tap a piece, then a square on any board. With a keyboard: arrows to move about a board, Page Up and Page Down to change boards, Enter to pick up and to set down, Escape to cancel.</p> <p class="muted small how">Tap a piece, then a square on any board. Tap any piece, yours or not, to read what it is and what it can do. With a keyboard: arrows to move about a board, Page Up and Page Down to change boards, Enter to pick up and to set down, Escape to cancel.</p>
{/if} {/if}
</section> </section>
<aside class="rail"> <aside class="rail">
{#if inspected !== null && g.cells[inspected] !== ''}
<div class="panel">
<PiecePanel {g} at={inspected} {mySide} close={() => (inspected = null)} />
</div>
{/if}
<section class="sides"> <section class="sides">
<h2>At the table</h2> <h2>At the table</h2>
<ul> <ul>
@@ -341,6 +365,30 @@
margin: 0.8rem auto 0; margin: 0.8rem auto 0;
} }
.panel {
position: sticky;
top: 0.5rem;
z-index: 3;
margin-bottom: 0.9rem;
}
@media (max-width: 860px) {
/* On a phone the boards run long: the panel rises from the bottom over them, and scrolls within itself. */
.panel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: auto;
margin: 0;
max-height: 48vh;
overflow-y: auto;
padding: 0 var(--gutter) 0.5rem;
background: var(--slate-deep);
box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
}
}
.sides h2 { .sides h2 {
font-size: 1.1rem; font-size: 1.1rem;
font-style: italic; font-style: italic;
+17 -1
View File
@@ -17,7 +17,7 @@
// still of the opening and the share artwork. // 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'; import { FILES, RANKS, PER_LEVEL, fileOf, rankOf, levelOf, isLight, kindOf, sideOf, frozen, squareName, LETTERS, NAMES, type State } from '$lib/game';
let { let {
g, tokens, levels = [3, 2, 1], showCoords = false, flipped = false, selected = null, targets = [], shownSquares = [], shownCaptured = [], checkSquare = -1, 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 = () => {} yourMove = false, calm = true, decorative = false, tap = () => {}, key = () => {}
}: { }: {
g: State; g: State;
@@ -29,6 +29,8 @@
flipped?: boolean; flipped?: boolean;
selected?: number | null; selected?: number | null;
targets?: number[]; targets?: number[];
/** Where a piece being read about could go: shown faintly, since it is not the viewer's to move. */
reach?: number[];
shownSquares?: number[]; shownSquares?: number[];
shownCaptured?: number[]; shownCaptured?: number[];
checkSquare?: number; checkSquare?: number;
@@ -135,6 +137,12 @@
</g> </g>
{/if} {/if}
{/each} {/each}
{#each reach as i (i)}
{@const p = place(i)}
{#if p}
<rect class="reach" x={p.x + 4} y={p.y + 4} width={CELL - 8} height={CELL - 8} rx="3" pointer-events="none" />
{/if}
{/each}
{#each targets as i (i)} {#each targets as i (i)}
{@const p = place(i)} {@const p = place(i)}
{#if p} {#if p}
@@ -259,6 +267,14 @@
opacity: 0.9; opacity: 0.9;
} }
.reach {
fill: none;
stroke: #1b1410;
stroke-width: 1.2;
stroke-dasharray: 3 2;
opacity: 0.55;
}
.hint-ring { .hint-ring {
fill: none; fill: none;
stroke: var(--frost); stroke: var(--frost);
+146
View File
@@ -0,0 +1,146 @@
<script lang="ts">
// The panel a tap on any piece opens: what it is, where it stands and
// what it can do from there, then how it moves, why Gygax put it in, and
// how to play it. The lore is src/lib/game/lore.ts; the rest is read off
// the board.
import { LORE } from '$lib/game/lore';
import { NAMES, LETTERS, kindOf, sideOf, levelOf, rankOf, frozen, movesFrom, legalMovesFrom, squareName, inCheck, type State, type Side } from '$lib/game';
import { VALUES, PER_LEVEL } from '$lib/game/board';
let { g, at, mySide, close }: { g: State; at: number; mySide: Side | null; close: () => void } = $props();
const cell = $derived(g.cells[at]);
const kind = $derived(kindOf(cell));
const side = $derived(sideOf(cell)!);
const lore = $derived(LORE[kind]);
const sideWord = $derived(side === 'gold' ? 'Gold' : 'Scarlet');
const owner = $derived(mySide === null ? `${sideWord}’s` : side === mySide ? 'Your' : `${sideWord}’s`);
const world = (i: number) => (levelOf(i) === 3 ? 'in the air' : levelOf(i) === 1 ? 'in the underworld' : 'on the land');
const value = $derived(kind === 'K' ? 'beyond price' : String(VALUES[kind]).replace('.5', '½'));
/** Whose piece a victim is, from the viewer's side of the table. */
const whose = (i: number) => {
const s = sideOf(g.cells[i])!;
return mySide === null ? `${s === 'gold' ? 'Gold' : 'Scarlet'}’s` : s === mySide ? 'your' : 'the';
};
const list = (items: string[]) => (items.length <= 1 ? items[0] ?? '' : `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`);
/** Where this piece stands and what it can do from there, read off the board. */
const situation = $derived.by(() => {
const lines: string[] = [];
const level = levelOf(at);
lines.push(`It stands ${world(at)} at ${squareName(at)}.`);
if (frozen(g.cells, at)) lines.push('It is frozen by the Basilisk beneath it, and can do nothing until that Basilisk moves or is taken.');
if (kind === 'B' && g.cells[at + PER_LEVEL] !== '' && sideOf(g.cells[at + PER_LEVEL]) !== side) lines.push(`Its gaze holds ${whose(at + PER_LEVEL)} ${NAMES[kindOf(g.cells[at + PER_LEVEL])]} at ${squareName(at + PER_LEVEL)}.`);
if (kind === 'K' && level !== 2) lines.push('It is off the land, where it can do nothing but step back.');
if (kind === 'S' && level === 2) lines.push('It is stranded on the land until it rises.');
if (kind === 'H' && level !== 2) lines.push('Off the land, its only move is back to it.');
if (kind === 'E' && level === 2) lines.push('On the land it can do nothing but come down.');
if (kind === 'G' && level === 2) lines.push('On the land it has lost its wings.');
if (kind === 'W' && rankOf(at) === (side === 'gold' ? 6 : 1)) lines.push('One more step and it stands up a Hero.');
if (g.over) return lines;
const reach = movesFrom(g.cells, at, g.setup);
const toMove = side === g.toMove;
const legal = toMove ? legalMovesFrom(g.cells, at, g.setup) : reach;
const takes = legal.filter((i) => g.cells[i] !== '').map((i) => `${whose(i)} ${NAMES[kindOf(g.cells[i])]} at ${squareName(i)}`);
if (!frozen(g.cells, at)) {
if (legal.length === 0) lines.push(toMove ? 'It has no move this turn.' : 'It has no move from there.');
else lines.push(`${toMove ? 'This turn it' : 'From there it'} can go to ${legal.length} ${legal.length === 1 ? 'square' : 'squares'}${takes.length ? `, and take ${list(takes)}` : ''}.`);
if (toMove && legal.length < reach.length) lines.push(legal.length === 0 ? 'Every move it has would leave its King in check.' : 'Some of its moves would leave its King in check, and are not offered.');
}
if (kind === 'K' && toMove && inCheck(g.cells, side)) lines.push('It is in check.');
return lines;
});
const letter = $derived(LETTERS[kind]);
</script>
<aside class="lore" aria-label="About this piece">
<header>
<span class="disc {side}" aria-hidden="true"><span class:wide={letter.length > 1}>{letter}</span></span>
<div>
<h2>{owner} {NAMES[kind]}</h2>
<p class="muted small">{lore.count}, valued at {value} by Gygax{kind === 'R' ? ', and higher by most who have played' : ''}.</p>
</div>
<button type="button" class="dismiss" title="Close" aria-label="Close" onclick={close}>×</button>
</header>
<p class="now">{situation.join(' ')}</p>
<p>{lore.is}</p>
<h3>How it moves</h3>
<p>{lore.moves}</p>
<h3>Why it is here</h3>
<p>{lore.why}</p>
<h3>How to play it</h3>
<p>{lore.play}</p>
</aside>
<style>
.lore {
border: 1px solid var(--rule-strong);
border-radius: 8px;
background: var(--slate);
padding: 0.8rem 0.9rem 0.9rem;
font-size: 0.92rem;
line-height: 1.5;
}
header {
display: flex;
align-items: center;
gap: 0.7rem;
margin-bottom: 0.5rem;
}
header div {
flex: 1;
min-width: 0;
}
h2 {
font-size: 1.15rem;
font-weight: 500;
}
h3 {
font-size: 0.8rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--bone-dim);
margin: 0.8rem 0 0.2rem;
font-weight: 500;
}
.now {
color: var(--bone);
border-left: 2px solid var(--ember);
padding-left: 0.6rem;
margin-bottom: 0.7rem;
}
p {
color: var(--bone-dim);
}
.disc {
flex: none;
width: 2.3rem;
height: 2.3rem;
border-radius: 50%;
display: grid;
place-items: center;
font-weight: 700;
font-size: 1.1rem;
background: #d8a63e;
border: 1.5px solid #8a6218;
color: #2b1d05;
}
.disc.scarlet {
background: #a4302a;
border-color: #5e1612;
color: #fbe9dc;
}
.disc .wide {
font-size: 0.9rem;
}
</style>
+127
View File
@@ -0,0 +1,127 @@
// What each piece is, in the game's own voice: the panel a player opens by
// tapping a piece. The moves are Gygax's, restated for a reader at the
// table. The rest is ours, written from his strategic notes and from the
// shape of the rules, and every claim in it is checked against board.ts.
import type { Kind } from './board';
export interface Lore {
/** The piece's company: "six a side". */
count: string;
/** What it is. */
is: string;
/** How it moves, as the rules have it. */
moves: string;
/** Why it is in the game. */
why: string;
/** How to play it. */
play: string;
}
export const LORE: Record<Kind, Lore> = {
S: {
count: 'six a side',
is: 'The foot soldiers of the air. Six slight spirits of wind drift above each army, and each of them runs on the light squares or the dark, never both, until a capture turns it onto the other colour.',
moves: 'In the air a Sylph drifts one square diagonally forward and captures by stepping straight ahead, a pawn turned inside out. It can also strike straight down at the land, taking the enemy piece directly beneath it. Once landed it is stranded. Its only move is to rise again, to the square above it or to any empty square the Sylphs began on, and it takes nothing on the way up. A Sylph on the far rank can only strike down, and once it has, it can climb back and begin again.',
why: 'Gygax gave the sky a screen of cheap pieces so the Griffons and the Dragon would not be alone up there, and gave each of them one blow downward so the land can never quite forget the air above it.',
play: 'Keep them in a line and they shield one another. Their real threat is the drop. A Sylph hovering over an enemy Oliphant is a one-point piece threatening a five-point one, and the land has few pieces that can reach up and answer. Spend them freely late in the game, when a Sylph that has struck and climbed home is a piece reborn.'
},
G: {
count: 'two a side',
is: 'Half eagle and half lion, and the only piece that leaps through the air. Two of them flank each Dragon, on the c and k files.',
moves: 'In the air a Griffon leaps two squares one way and three the other, over anything in between, to as many as eight squares. It can drop to the land onto any of the four squares diagonally beside the square beneath it, taking whatever is there. On the land it loses its wings and creeps one square diagonally, until it climbs back onto one of the four squares diagonally above it.',
why: 'The Griffon is the sky’s raider. Its leap crosses three ranks at once, so nothing on the land sees it coming, and its drop lands beside the square beneath it rather than on it, so a piece that guards the sky over its own head is not safe.',
play: 'Keep it in the air, where it is worth a little more than a Hero. On the land it is a lame Thief. Drop to pick off a defender and climb before the land can gather. A Griffon and a Dragon hunting together can take anything that strays onto the ranks beneath them.'
},
R: {
count: 'one a side',
is: 'The great wyrm of the sky, roosting above its own King on the g file. The game takes its name from this one.',
moves: 'It never leaves the air. It slides any distance along a diagonal, or steps one square in any direction. And it strikes from afar. Without moving, it can take an enemy on the land directly beneath it or on any of the four squares beside that one. The victim is lifted away and the Dragon stays where it is.',
why: 'Gygax set the Dragon directly over the King on purpose. Its strike covers the five land squares beneath it, so a Dragon at home is a roof over the King, and a Dragon that flies off to hunt leaves the King under the other Dragon’s shadow.',
play: 'Gygax rated it at eight, and players since have called that too low. The drake values it as a Paladin. Early, keep it home and let it threaten. Later, a Dragon over the enemy’s back rank is a terror, since only five kinds of land piece can reach the air at all. Mind the Sylphs, which capture straight ahead on its board and cost a point to lose.'
},
W: {
count: 'twelve a side',
is: 'The levy of the land, twelve spears in a wall across the second rank.',
moves: 'One square straight ahead, never two, and it captures one square diagonally ahead. On reaching the far rank it stands up as a Hero, with no say in the matter. It never leaves the land.',
why: 'A chess pawn with the double step and the capture in passing taken away. Gygax said he limited it to the single step of shatranj, the old game chess came from, which slows the opening and makes every advance a commitment.',
play: 'Twelve files is a wide front. Push in the middle and leave the wings to the Oliphants. Chains guard one another as in chess. A Warrior that gets through becomes a Hero, worth four and a half, a real gain but not a queen, so do not spend three pieces to get one there.'
},
O: {
count: 'two a side',
is: 'The war elephant, a rook with tusks, standing at the corners of the land.',
moves: 'Any distance along a rank or a file, as far as the way is clear. It never leaves the land.',
why: 'On a board twelve squares wide, an open file or rank runs a long way, and the Oliphant is the piece that owns it.',
play: 'Open files are precious on so wide a board, and two Oliphants on one file break almost anything. Remember what the sky can do to the land. An Oliphant standing under an enemy Dragon is as good as lost, because an Oliphant cannot strike back at the sky.'
},
U: {
count: 'two a side',
is: 'The knight of the land, and the cheapest piece on it after the Warrior.',
moves: 'One square along a rank or file and then one diagonally outward, leaping whatever is between. It never leaves the land.',
why: 'A knight is slow on a board twelve by eight, and Gygax rates the Unicorn below a Basilisk. It is here for the squares the long pieces cannot reach.',
play: 'Use it for forks and for the awkward squares, and trade it gladly for a Hero or a Thief. Early on it is the piece to develop first, since it leaps the wall of Warriors.'
},
H: {
count: 'two a side',
is: 'A champion of the land who can climb to the sky and dig into the earth. Every Warrior that reaches the far rank becomes one.',
moves: 'On the land a Hero goes one or two squares along a diagonal, leaping the square between. It can rise to the air or drop to the underworld, onto any of the four squares diagonally next to the square above or below it, and may take a piece as it goes. Off the land its only move is back down or up, to one of the four squares diagonally next to the square below or above.',
why: 'The Hero is the land’s answer to the Griffon, a leaper that can go where the enemy’s pieces hide.',
play: 'A Hero can kill a Dragon by rising onto its square from the land beneath, one of few pieces that can, but once in the air it can do nothing except come down, so go up to kill and come straight back. A Hero in the underworld is the same, a visitor with one move. On the land, its two-square leap over a Warrior is how it gets out early.'
},
T: {
count: 'two a side',
is: 'The bishop of the land, quick along the diagonals and quick to be elsewhere.',
moves: 'Any distance along a diagonal, as far as the way is clear. It never leaves the land.',
why: 'Two Thieves cover both colours of square, and on a board this wide the diagonals run far.',
play: 'A Thief is the piece most often lost to a Dragon’s strike, because its work is in the middle ranks where the Dragon hunts. Keep it on a diagonal the Dragon does not cover, and remember that a Basilisk beneath a Thief freezes it mid-flight.'
},
C: {
count: 'one a side',
is: 'The one piece at home on every board.',
moves: 'One square in any direction wherever it stands, and one step straight up or down to the board above or below, taking what is there.',
why: 'Gygax says not to underestimate it. Of the five kinds of piece that can visit all three boards, the Cleric is the only one that moves and captures the same way on each.',
play: 'Gygax values it at nine, above the Dragon, because it can go anywhere and threaten everything when it arrives. A Cleric in the underworld hunts Basilisks that few others can reach, and a Cleric in the air harries Sylphs. Walk it rather than hurrying it. It is slow, and every square it passes is one it controls.'
},
M: {
count: 'one a side',
is: 'The queen of the land, and a traveller between worlds with little to do once it has arrived.',
moves: 'On the land a Mage slides any distance along a rank, file or diagonal. It can also move straight up or down, one board or two, though to pass through the land the square there must be empty. In the air or the underworld it is reduced to one square sideways, forward or back.',
why: 'The most valuable piece on the board, worth eleven, and Gygax warns against the queen hunt. A Mage brought out early is chased by lesser pieces that develop as they chase it.',
play: 'Keep it back until the Unicorns and Heroes are out. Its step between boards is a way to flee a Dragon’s gaze, or to drop into the underworld and take a Basilisk from beside it, but a Mage down there is a shadow of itself and takes three moves to go and come back. Use the vertical move to escape, not to visit.'
},
K: {
count: 'one a side',
is: 'The King of the land, and the whole game. Lose it and nothing else counts.',
moves: 'One square in any direction on the land, and one step straight up to the air or down to the underworld, taking what is there. Off the land it can do nothing but come straight back.',
why: 'As in chess, the object is to checkmate it. A King attacked with no escape loses the game, and a King frozen by a Basilisk beneath it is mated by any attack that cannot be blocked or captured.',
play: 'Keep the square beneath it in mind. An enemy Basilisk that reaches the underworld under your King freezes it where it stands. Never step off the land unless the land is death, since off it the King can only return, a sitting duck. The Dragon above it is its roof, and the Paladin beside it its shield.'
},
P: {
count: 'one a side',
is: 'The holy knight, the piece that can be on any board from any other in a single leap.',
moves: 'On the land the Paladin has the king’s step and the knight’s leap together. Between boards it leaps the way a knight does on one, one board up or down and two squares along a rank or file, or two boards and one square, over anything in between. In the air or the underworld it keeps only the king’s step.',
why: 'After the Mage it is the game’s most powerful piece, worth ten, and the only one whose move between boards vaults what is in the way.',
play: 'Gygax counsels keeping it back until the minor pieces are developed. Then its leaps let it threaten the enemy’s sky and underworld where few can follow, and come back in one move. A Paladin can land beside a Basilisk and take it next turn, or rise beneath a Dragon’s roost and strike it from the air.'
},
D: {
count: 'six a side',
is: 'The miners of the underworld, six to a side, tunnelling along the second rank of the earth.',
moves: 'A Dwarf walks one square straight forward or one square sideways without capturing, and captures one square diagonally forward. From the underworld it can strike straight up, taking the enemy piece on the land directly above it, and on the land it keeps the same moves. It comes back down only onto an empty square beneath it. It never reaches the air.',
why: 'Gygax says the pieces of the underworld play little part in the opening, but Dwarves left at home make outposts in the endgame, when the enemy’s pieces come deep into your side of the board.',
play: 'Keep them home. A Dwarf is a mine beneath the land, and a piece that settles on the land above one is taken. Their sideways step lets them shift to the file where they are needed without advancing, which is the move a Warrior cannot make.'
},
B: {
count: 'two a side',
is: 'The serpent whose gaze turns to stone. Whatever enemy stands on the land directly above a Basilisk cannot move, cannot take, and threatens nothing, until the Basilisk moves or dies.',
moves: 'It moves one square straight or diagonally forward, taking what is there, or one square straight back onto an empty square. It never leaves the underworld. The freeze is not a move. It simply holds for as long as the Basilisk stays beneath its victim.',
why: 'The Basilisk is the game’s one trick that chess does not have. It is how the underworld reaches the land without ever rising.',
play: 'Gygax’s own advice is to stand a piece of yours over your own Basilisk. Whatever captures it is frozen on the spot, and you take it at your leisure. A Basilisk arriving under a piece that guards the enemy King can be a checkmate on its own, and a Basilisk under the King itself leaves it unable to step out of any check. Walk it forward early. It is slow, and the squares that matter are far away.'
},
E: {
count: 'one a side',
is: 'The one heavy thing in the underworld, a bruiser of living stone that climbs only to kill.',
moves: 'In the underworld it moves one or two squares along a rank or file and captures the same way, with the nearer square empty before it can reach the further, and it moves one square diagonally without capturing. It rises to the land only to capture, one square along a rank or file onto an empty square and then straight up onto an enemy. On the land it can do nothing but come down, straight down through an empty square and then one square along a rank or file.',
why: 'The Elemental gives the underworld a piece with reach, so that a visitor from the land cannot walk the earth unopposed.',
play: 'Its climb is the thing to plan around. It needs an empty square beside it and an enemy above that square, and once up it is a rock on the land until it comes down. Use it to kill Dwarves that wander and Heroes that dig, and to rise beneath a weak point in the enemy’s back rank when nothing can answer it.'
}
};
+2
View File
@@ -44,6 +44,8 @@
<h2>The boards</h2> <h2>The boards</h2>
<figure><img src="/guide/boards.png" alt="The three boards one above the other in the opening position, the air in blue and white, the land in green and amber, the underworld in red and brown" loading="lazy" /></figure> <figure><img src="/guide/boards.png" alt="The three boards one above the other in the opening position, the air in blue and white, the land in green and amber, the underworld in red and brown" loading="lazy" /></figure>
<p>The three boards stand one above the other, the air at the top, the land in the middle and the underworld at the bottom, in the colours Gygax suggested. Your side is nearest you on every board. Each piece is a disc bearing the initial Gygax gave it, with Dr for the Dragon so it is not mistaken for a Dwarf. The square names along the edges are his too, and you can hide them under <em>preferences</em>.</p> <p>The three boards stand one above the other, the air at the top, the land in the middle and the underworld at the bottom, in the colours Gygax suggested. Your side is nearest you on every board. Each piece is a disc bearing the initial Gygax gave it, with Dr for the Dragon so it is not mistaken for a Dwarf. The square names along the edges are his too, and you can hide them under <em>preferences</em>.</p>
<figure><img src="/guide/panel.png" alt="The panel for Scarlet's Dragon: where it stands and what it can take, what it is, how it moves, why it is in the game, and how to play it" loading="lazy" /></figure>
<p>Tap any piece, yours or the other side's, and a panel opens beside the boards, or rises from the foot of the screen on a phone, saying what the piece is, where it stands and what it can reach from there, how it moves, why Gygax put it in the game, and how to play it. Dotted squares show where a piece that is not yours to move could go. Tap the piece again, or the cross, to close it.</p>
<p>The strip above the boards says whose move it is and warns when a King is in check. The record beside the boards lists the moves played in Gygax's notation. Click one to see it on the boards, and click again, or make your move, to come back to now.</p> <p>The strip above the boards says whose move it is and warns when a King is in check. The record beside the boards lists the moves played in Gygax's notation. Click one to see it on the boards, and click again, or make your move, to come back to now.</p>
</section> </section>
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB