The Tablut board is cut in stone: mottled, chipped at a few corners, and cracked twice, as a slab from a long hall would be
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
dcfbd38ae6
commit
c5d487edbf
@@ -48,6 +48,34 @@
|
|||||||
};
|
};
|
||||||
const pieceWord = (p: string) => (p === 'k' ? 'the king' : p === 'a' ? 'an attacker' : 'a defender');
|
const pieceWord = (p: string) => (p === 'k' ? 'the king' : p === 'a' ? 'an attacker' : 'a defender');
|
||||||
|
|
||||||
|
// --- Age: the Tablut board came out of a long hall. A few chipped corners
|
||||||
|
// and two hairline cracks, placed by a fixed hand so the board is the same
|
||||||
|
// board every visit; nothing here moves or changes with play.
|
||||||
|
const chips = $derived.by(() => {
|
||||||
|
if (!worn) return [] as { x: number; y: number; r: number; d: string }[];
|
||||||
|
const spots = [
|
||||||
|
[1, 0, 3], [size - 1, 2, 0], [3, size - 1, 1], [size - 2, size - 2, 2], [0, size - 3, 3], [size - 4, 0, 1], [2, 4, 0]
|
||||||
|
] as const;
|
||||||
|
return spots.map(([cx, cy, corner]) => {
|
||||||
|
const x = PAD_L + cx * CELL + (corner === 1 || corner === 2 ? CELL : 0);
|
||||||
|
const y = PAD_T + cy * CELL + (corner >= 2 ? CELL : 0);
|
||||||
|
const sx = corner === 1 || corner === 2 ? -1 : 1;
|
||||||
|
const sy = corner >= 2 ? -1 : 1;
|
||||||
|
const a = 5 + ((cx * 7 + cy * 3) % 4);
|
||||||
|
const b = 4 + ((cx * 5 + cy) % 3);
|
||||||
|
return { x, y, r: 0, d: `M${x} ${y} l${sx * a} 0 l${-sx * (a - b)} ${sy * b} l${-sx * b} ${sy * (a - b + 1)} z` };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const cracks = $derived.by(() => {
|
||||||
|
if (!worn) return [] as string[];
|
||||||
|
const w = PAD_L + size * CELL;
|
||||||
|
const h = PAD_T + size * CELL;
|
||||||
|
return [
|
||||||
|
`M${PAD_L + CELL * 0.6} ${PAD_T + 2} c 6 14, -4 22, 8 36 s 2 18, 14 26 s 4 10, 2 22`,
|
||||||
|
`M${w - 3} ${h - CELL * 2.4} c -12 4, -18 -6, -30 2 s -14 12, -26 8 s -12 -4, -18 6`
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
// --- Tokens: pieces with a life of their own, so a move slides and a capture fades ---
|
// --- Tokens: pieces with a life of their own, so a move slides and a capture fades ---
|
||||||
interface Token {
|
interface Token {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -245,15 +273,23 @@
|
|||||||
<filter id="grain" x="0" y="0" width="100%" height="100%">
|
<filter id="grain" x="0" y="0" width="100%" height="100%">
|
||||||
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="7" result="noise" />
|
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="7" result="noise" />
|
||||||
<feColorMatrix in="noise" type="saturate" values="0" result="grey" />
|
<feColorMatrix in="noise" type="saturate" values="0" result="grey" />
|
||||||
<feComponentTransfer in="grey" result="soft"><feFuncA type="linear" slope={worn ? 0.42 : 0.14} /></feComponentTransfer>
|
<feComponentTransfer in="grey" result="soft"><feFuncA type="linear" slope={worn ? 0.5 : 0.14} /></feComponentTransfer>
|
||||||
<feBlend in="SourceGraphic" in2="soft" mode="multiply" />
|
<feBlend in="SourceGraphic" in2="soft" mode="multiply" />
|
||||||
</filter>
|
</filter>
|
||||||
<filter id="worn" x="-5%" y="-5%" width="110%" height="110%">
|
<filter id="worn" x="-5%" y="-5%" width="110%" height="110%">
|
||||||
<feTurbulence type="fractalNoise" baseFrequency="0.07" numOctaves="2" seed="3" result="warp" />
|
<feTurbulence type="fractalNoise" baseFrequency="0.07" numOctaves="2" seed="3" result="warp" />
|
||||||
<feDisplacementMap in="SourceGraphic" in2="warp" scale={worn ? 2.4 : 0} xChannelSelector="R" yChannelSelector="G" />
|
<feDisplacementMap in="SourceGraphic" in2="warp" scale={worn ? 2.8 : 0} xChannelSelector="R" yChannelSelector="G" />
|
||||||
|
</filter>
|
||||||
|
<filter id="mottle" x="0" y="0" width="100%" height="100%">
|
||||||
|
<feTurbulence type="fractalNoise" baseFrequency="0.018" numOctaves="3" seed="11" result="blotch" />
|
||||||
|
<feColorMatrix in="blotch" type="matrix" values="0 0 0 0 0.16 0 0 0 0 0.14 0 0 0 0 0.11 0 0 0 0.55 0" result="stain" />
|
||||||
|
<feBlend in="SourceGraphic" in2="stain" mode="multiply" />
|
||||||
</filter>
|
</filter>
|
||||||
</defs>
|
</defs>
|
||||||
<rect class="wood" x="0" y="0" {width} {height} rx="6" filter="url(#grain)" />
|
<rect class="wood" x="0" y="0" {width} {height} rx="6" filter="url(#grain)" />
|
||||||
|
{#if worn}
|
||||||
|
<rect class="stain" x="0" y="0" {width} {height} rx="6" filter="url(#mottle)" />
|
||||||
|
{/if}
|
||||||
{#each showCoords ? Array.from({ length: size }, (_, n) => n) : [] as n (n)}
|
{#each showCoords ? Array.from({ length: size }, (_, n) => n) : [] as n (n)}
|
||||||
<text class="coord" x={PAD_L + n * CELL + CELL / 2} y={height - 4} text-anchor="middle">{String.fromCharCode(97 + n)}</text>
|
<text class="coord" x={PAD_L + n * CELL + CELL / 2} y={height - 4} text-anchor="middle">{String.fromCharCode(97 + n)}</text>
|
||||||
<text class="coord" x={PAD_L - 4} y={PAD_T + n * CELL + CELL / 2 + 3} text-anchor="end">{size - n}</text>
|
<text class="coord" x={PAD_L - 4} y={PAD_T + n * CELL + CELL / 2 + 3} text-anchor="end">{size - n}</text>
|
||||||
@@ -287,6 +323,12 @@
|
|||||||
<circle class="hint" cx={x + CELL / 2} cy={y + CELL / 2} r={CELL * 0.14} pointer-events="none" />
|
<circle class="hint" cx={x + CELL / 2} cy={y + CELL / 2} r={CELL * 0.14} pointer-events="none" />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
{#each chips as chip (chip.d)}
|
||||||
|
<path class="chip" d={chip.d} />
|
||||||
|
{/each}
|
||||||
|
{#each cracks as crack (crack)}
|
||||||
|
<path class="crack" d={crack} />
|
||||||
|
{/each}
|
||||||
{#each tokens as t (t.id)}
|
{#each tokens as t (t.id)}
|
||||||
{@const c = cellOf(size, t.at)}
|
{@const c = cellOf(size, t.at)}
|
||||||
{@const cx = PAD_L + c.x * CELL + CELL / 2}
|
{@const cx = PAD_L + c.x * CELL + CELL / 2}
|
||||||
@@ -375,10 +417,10 @@
|
|||||||
--crown: #6b4a0f;
|
--crown: #6b4a0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tablut: the older game, on a board that has been in a barn. */
|
/* Tablut: the older game, cut in stone and kept in a long hall for a thousand years. */
|
||||||
.board[data-set='tablut'] {
|
.board[data-set='tablut'] {
|
||||||
--wood: #8d8577;
|
--wood: #8a8578;
|
||||||
--wood-line: rgba(40, 34, 28, 0.4);
|
--wood-line: rgba(36, 32, 27, 0.45);
|
||||||
--coord: rgba(30, 26, 20, 0.5);
|
--coord: rgba(30, 26, 20, 0.5);
|
||||||
--square: rgba(255, 250, 235, 0.05);
|
--square: rgba(255, 250, 235, 0.05);
|
||||||
--square-edge: rgba(255, 250, 235, 0.09);
|
--square-edge: rgba(255, 250, 235, 0.09);
|
||||||
@@ -472,6 +514,26 @@
|
|||||||
transition: fill 1.2s ease;
|
transition: fill 1.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stain {
|
||||||
|
fill: rgba(120, 110, 96, 0.22);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
fill: rgba(28, 24, 20, 0.42);
|
||||||
|
stroke: rgba(230, 222, 205, 0.18);
|
||||||
|
stroke-width: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crack {
|
||||||
|
fill: none;
|
||||||
|
stroke: rgba(24, 20, 16, 0.55);
|
||||||
|
stroke-width: 0.9;
|
||||||
|
stroke-linecap: round;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.coord {
|
.coord {
|
||||||
fill: var(--coord);
|
fill: var(--coord);
|
||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
|
|||||||
Reference in New Issue
Block a user