Coordinates on the board, moves in the record shown on the board, a status strip above it with the capture in words, notes under the variant selector and a still that follows it, keyboard play, and a way back to the game from the rules
From a tester's review. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
bac7182915
commit
5db89edbc1
@@ -15,6 +15,16 @@
|
||||
let code = $state('');
|
||||
/** The host's choices for the next table, from the game's declared options. */
|
||||
let options = $state<Record<string, string>>(Object.fromEntries((game.options ?? []).map((o) => [o.key, o.default])));
|
||||
|
||||
/** The hall's still of the opening position, on whichever board the selector names. */
|
||||
const still = $derived(
|
||||
options.set === 'tablut'
|
||||
? { n: 9, wood: '#8d8577', line: 'rgba(40,34,28,0.5)', dark: '#3d3530', darkRim: '#1c1714', pale: '#cfc4ad', paleRim: '#8c8069', king: '#c9b27a', kingRim: '#7d6636', corners: false,
|
||||
a: [[3,0],[4,0],[5,0],[4,1],[0,3],[8,3],[0,4],[1,4],[7,4],[8,4],[0,5],[8,5],[4,7],[3,8],[4,8],[5,8]], d: [[4,2],[4,3],[2,4],[3,4],[5,4],[6,4],[4,5],[4,6]] }
|
||||
: { n: 11, wood: '#8a5a2b', line: 'rgba(30,16,6,0.5)', dark: '#2a2320', darkRim: '#0d0a08', pale: '#ece4d0', paleRim: '#b9ab8b', king: '#f0d58a', kingRim: '#a67c1a', corners: true,
|
||||
a: [[3,0],[4,0],[5,0],[6,0],[7,0],[5,1],[0,3],[10,3],[0,4],[10,4],[0,5],[1,5],[9,5],[10,5],[0,6],[10,6],[0,7],[10,7],[5,9],[3,10],[4,10],[5,10],[6,10],[7,10]], d: [[5,3],[4,4],[5,4],[6,4],[3,5],[4,5],[6,5],[7,5],[4,6],[5,6],[6,6],[5,7]] }
|
||||
);
|
||||
const mid = $derived((still.n - 1) / 2);
|
||||
let busy = $state(false);
|
||||
let error = $state('');
|
||||
let seats = $state(allSeats());
|
||||
@@ -156,7 +166,7 @@
|
||||
<div class="lid-head">
|
||||
<h1>Hnefatafl</h1>
|
||||
<p class="pitch">The Viking board game. One king, one throne, four corners, and a ring of attackers closing in.</p>
|
||||
<p class="tag">Hnefatafl was played across the Norse world for a thousand years before chess. Every piece moves like a rook and is taken by being sandwiched; the attackers must capture the king, the king must reach a corner. Play a housecarl, or open a table for a friend and play by turns. Copenhagen rules on the big board, or Tablut, the older game Linnaeus wrote down in 1732.</p>
|
||||
<p class="tag">Hnefatafl was played across the Norse world for a thousand years before chess. Every piece moves like a rook and is taken by being sandwiched; the attackers must capture the king, the king must reach safety. Play a housecarl, or open a table for a friend and play by turns. Copenhagen rules on the big board, or Tablut, the older game Linnaeus wrote down in 1732.</p>
|
||||
<p class="links"><a href="/guide">how to play</a> · <a href="/rules">the rules</a> · <a class="about-link" href="#about" onclick={() => (aboutOpen = true)}>about this game — a labor of love</a></p>
|
||||
</div>
|
||||
|
||||
@@ -168,12 +178,16 @@
|
||||
{#if game.options?.length}
|
||||
<div class="options">
|
||||
{#each game.options as o (o.key)}
|
||||
<label class="option">
|
||||
<span>{o.label}</span>
|
||||
<select bind:value={options[o.key]}>
|
||||
{#each o.choices as c (c.value)}<option value={c.value}>{c.label}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
{@const chosen = o.choices.find((c) => c.value === options[o.key])}
|
||||
<div class="option-block">
|
||||
<label class="option">
|
||||
<span>{o.label}</span>
|
||||
<select bind:value={options[o.key]}>
|
||||
{#each o.choices as c (c.value)}<option value={c.value}>{c.label}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
{#if chosen?.note}<p class="option-note muted small">{chosen.note}</p>{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -196,20 +210,20 @@
|
||||
</div>
|
||||
|
||||
<div class="lid-look" aria-hidden="true">
|
||||
<svg class="still" viewBox="0 0 110 110" aria-hidden="true">
|
||||
<rect x="0" y="0" width="110" height="110" rx="4" fill="#8a5a2b" />
|
||||
{#each Array.from({ length: 11 }, (_, r) => r) as r (r)}
|
||||
{#each Array.from({ length: 11 }, (_, c) => c) as c (c)}
|
||||
<rect x={c * 10} y={r * 10} width="10" height="10" fill={(r === 0 || r === 10) && (c === 0 || c === 10) ? 'rgba(224,101,92,0.4)' : r === 5 && c === 5 ? 'rgba(240,165,58,0.4)' : 'rgba(255,240,210,0.08)'} stroke="rgba(30,16,6,0.5)" stroke-width="0.4" />
|
||||
<svg class="still" class:worn={options.set === 'tablut'} viewBox="0 0 {still.n * 10} {still.n * 10}" aria-hidden="true">
|
||||
<rect x="0" y="0" width={still.n * 10} height={still.n * 10} rx="4" fill={still.wood} />
|
||||
{#each Array.from({ length: still.n }, (_, r) => r) as r (r)}
|
||||
{#each Array.from({ length: still.n }, (_, c) => c) as c (c)}
|
||||
<rect x={c * 10} y={r * 10} width="10" height="10" fill={still.corners && (r === 0 || r === still.n - 1) && (c === 0 || c === still.n - 1) ? 'rgba(224,101,92,0.4)' : r === mid && c === mid ? 'rgba(240,165,58,0.4)' : 'rgba(255,240,210,0.08)'} stroke={still.line} stroke-width="0.4" />
|
||||
{/each}
|
||||
{/each}
|
||||
{#each [[3,0],[4,0],[5,0],[6,0],[7,0],[5,1],[0,3],[10,3],[0,4],[10,4],[0,5],[1,5],[9,5],[10,5],[0,6],[10,6],[0,7],[10,7],[5,9],[3,10],[4,10],[5,10],[6,10],[7,10]] as [x, y] (`${x},${y}`)}
|
||||
<circle cx={x * 10 + 5} cy={y * 10 + 5} r="3.4" fill="#2a2320" stroke="#0d0a08" stroke-width="0.6" />
|
||||
{#each still.a as [x, y] (`${x},${y}`)}
|
||||
<circle cx={x * 10 + 5} cy={y * 10 + 5} r="3.4" fill={still.dark} stroke={still.darkRim} stroke-width="0.6" />
|
||||
{/each}
|
||||
{#each [[5,3],[4,4],[5,4],[6,4],[3,5],[4,5],[6,5],[7,5],[4,6],[5,6],[6,6],[5,7]] as [x, y] (`${x},${y}`)}
|
||||
<circle cx={x * 10 + 5} cy={y * 10 + 5} r="3.4" fill="#ece4d0" stroke="#b9ab8b" stroke-width="0.6" />
|
||||
{#each still.d as [x, y] (`${x},${y}`)}
|
||||
<circle cx={x * 10 + 5} cy={y * 10 + 5} r="3.4" fill={still.pale} stroke={still.paleRim} stroke-width="0.6" />
|
||||
{/each}
|
||||
<circle cx="55" cy="55" r="3.6" fill="#f0d58a" stroke="#a67c1a" stroke-width="0.6" />
|
||||
<circle cx={mid * 10 + 5} cy={mid * 10 + 5} r="3.6" fill={still.king} stroke={still.kingRim} stroke-width="0.6" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -609,12 +623,22 @@
|
||||
gap: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.option-note {
|
||||
margin: 0.2rem 0 0;
|
||||
max-width: 22em;
|
||||
}
|
||||
.still {
|
||||
width: 100%;
|
||||
max-width: 18rem;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
border-radius: 4px;
|
||||
transition: filter 0.9s ease;
|
||||
}
|
||||
|
||||
.still.worn {
|
||||
filter: contrast(0.85) sepia(0.15);
|
||||
}
|
||||
.tally {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user