A new table plays Tablut unless the host chooses Copenhagen
The declared default on the rules option is now Tablut, listed first, and the engine plays it when no rules are given, so the hall's still, the preferences' table default and a bare createGame agree. Tests that read Copenhagen positions ask for Copenhagen; the Tablut layout test pins the default. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwFKMuQnPAEHJ5yA1q4orh
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
32152c05f0
commit
e95a090ac0
@@ -1,9 +1,9 @@
|
||||
# Hnefatafl
|
||||
|
||||
The Viking board game, in the browser: the attackers must take the king, the
|
||||
king must reach a corner. Copenhagen rules on the eleven-by-eleven board by
|
||||
default; Tablut, the nine-by-nine game Linnaeus wrote down in 1732, as an
|
||||
option, on a board that shows its age.
|
||||
king must reach the edge. Tablut, the nine-by-nine game Linnaeus wrote down in
|
||||
1732, by default, on a board that shows its age; Copenhagen rules on the
|
||||
eleven-by-eleven board, where the king needs a corner, as an option.
|
||||
|
||||
Built on the game kit: `src/lib/game/` is the game (rules as data in
|
||||
`rules.ts`, the board's mechanics in `board.ts`, the engine in `index.ts`,
|
||||
|
||||
@@ -21,7 +21,7 @@ const E9 = '.........';
|
||||
|
||||
describe('the opening', () => {
|
||||
it('lays Copenhagen out with 24 attackers, 12 defenders and the king on the throne', () => {
|
||||
const s = createGame(names);
|
||||
const s = createGame(names, 1, 1, { set: 'copenhagen', side: 'defenders' });
|
||||
expect(s.size).toBe(11);
|
||||
expect(s.cells.filter((p) => p === 'a').length).toBe(24);
|
||||
expect(s.cells.filter((p) => p === 'd').length).toBe(12);
|
||||
@@ -29,8 +29,8 @@ describe('the opening', () => {
|
||||
expect(s.toMove).toBe('attackers');
|
||||
});
|
||||
|
||||
it('lays Tablut out on nine by nine with 16 attackers and 8 defenders', () => {
|
||||
const s = createGame(names, 1, 1, { set: 'tablut', side: 'defenders' });
|
||||
it('lays Tablut out on nine by nine with 16 attackers and 8 defenders, and plays it when no rules are chosen', () => {
|
||||
const s = createGame(names);
|
||||
expect(s.size).toBe(9);
|
||||
expect(s.cells.filter((p) => p === 'a').length).toBe(16);
|
||||
expect(s.cells.filter((p) => p === 'd').length).toBe(8);
|
||||
@@ -70,7 +70,7 @@ describe('moving', () => {
|
||||
});
|
||||
|
||||
it('refuses the wrong side, a stranger, and an impossible square, with reasons', () => {
|
||||
const s = createGame(names);
|
||||
const s = createGame(names, 1, 1, { set: 'copenhagen', side: 'defenders' });
|
||||
expect(validateMove(s, 'A', { from: at(11, { x: 5, y: 3 }), to: at(11, { x: 5, y: 2 }) })).toMatch(/not your side/);
|
||||
expect(validateMove(s, 'B', { from: at(11, { x: 5, y: 3 }), to: at(11, { x: 5, y: 2 }) })).toMatch(/not one of your pieces/);
|
||||
expect(validateMove(s, 'B', { from: at(11, { x: 3, y: 0 }), to: at(11, { x: 3, y: 5 }) })).toMatch(/cannot move there/);
|
||||
@@ -139,7 +139,7 @@ describe('the king', () => {
|
||||
});
|
||||
|
||||
it('needs four attackers on the throne, and three beside it', () => {
|
||||
const s = createGame(names);
|
||||
const s = createGame(names, 1, 1, { set: 'copenhagen', side: 'defenders' });
|
||||
const t = throneOf(11);
|
||||
// Clear the defenders and ring the throne with three attackers; the fourth arrives.
|
||||
const onThrone = s.cells.map((p) => (p === 'd' ? '.' : p)) as Piece[];
|
||||
@@ -216,7 +216,7 @@ describe('the ends of the game', () => {
|
||||
}, 120_000);
|
||||
|
||||
it('opens with the same move every time, so a ledger replays to the same choice', () => {
|
||||
const s = createGame(names, 3);
|
||||
const s = createGame(names, 3, 1, { set: 'copenhagen', side: 'defenders' });
|
||||
expect(movesOf(rulesetOf(s), s.cells, 'attackers').length).toBe(116);
|
||||
expect(chooseMove(s, 'B')).toEqual({ from: 3, to: 2 });
|
||||
});
|
||||
|
||||
@@ -13,6 +13,8 @@ import { chooseMove } from './bot';
|
||||
export { at, beyond, cellOf, cornersOf, isEdge, pieceWord, sideOf, squareName, throneOf, type Input, type Piece, type Side } from './board';
|
||||
|
||||
const CURRENT_RULES = 1;
|
||||
/** The rules a table plays when the host chooses none. */
|
||||
const DEFAULT_SET: Ruleset['id'] = 'tablut';
|
||||
|
||||
export interface Player {
|
||||
id: SeatId;
|
||||
@@ -64,7 +66,7 @@ export function rulesetOf(state: State): Ruleset {
|
||||
// --- Setting up ---------------------------------------------------------------
|
||||
|
||||
export function createGame(names: Record<SeatId, string>, _seed = 0, rules = CURRENT_RULES, options?: TableOptions): State {
|
||||
const set = options?.set === 'tablut' ? RULESETS.tablut : RULESETS.copenhagen;
|
||||
const set = RULESETS[options?.set as Ruleset['id']] ?? RULESETS[DEFAULT_SET];
|
||||
const size = set.size;
|
||||
const cells: Piece[] = Array(size * size).fill('.');
|
||||
for (const c of set.attackers) cells[at(size, c)] = 'a';
|
||||
@@ -205,10 +207,10 @@ export const game: GameSpec<State, Input> = {
|
||||
key: 'set',
|
||||
label: 'Rules',
|
||||
choices: [
|
||||
{ value: 'copenhagen', label: 'Copenhagen, 11 by 11', note: 'The modern tournament game. The king escapes to a corner and falls only to four attackers, three beside the throne.' },
|
||||
{ value: 'tablut', label: 'Tablut, 9 by 9, after Linnaeus', note: 'The older game. The king escapes to any edge square, and away from the throne two attackers take him like any piece.' }
|
||||
{ value: 'tablut', label: 'Tablut, 9 by 9, after Linnaeus', note: 'The older game. The king escapes to any edge square, and away from the throne two attackers take him like any piece.' },
|
||||
{ value: 'copenhagen', label: 'Copenhagen, 11 by 11', note: 'The modern tournament game. The king escapes to a corner and falls only to four attackers, three beside the throne.' }
|
||||
],
|
||||
default: 'copenhagen'
|
||||
default: DEFAULT_SET
|
||||
},
|
||||
{ key: 'side', label: 'You play', choices: SIDES, default: 'defenders' }
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user