Files
scrabble-game/ui/src/lib/premiums.ts
T
Ilia Denisov 26aa154547
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Successful in 37s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
R1: schema & naming reset — squash migrations, rename variants
Squash the 12 goose migrations into one 00001_baseline.sql (there is no prod
data; verified schema-identical to the chain via a pg_dump diff + the green
integration suite) and rename the game-variant labels
english/russian_scrabble/erudit -> scrabble_en/scrabble_ru/erudit_ru across the
backend, the FlatBuffers wire values and the UI.

dawg filenames and the Go enum identifiers are unchanged; the i18n display keys
are kept. Adds PRERELEASE.md (the R1-R7 pre-release tracker), linked from
CLAUDE.md. Contour DB wipe and the scrabble-dictionary tidy are follow-ups.
2026-06-09 12:09:50 +02:00

90 lines
2.5 KiB
TypeScript

// Board premium layout — the 15x15 premium-square geometry, ported from the engine source
// of truth, scrabble-solver/rules/rules.go (standardBoard / eruditBoard). The board is not
// transmitted on the wire (StateView has no board), so the client renders the premiums
// locally; only the centre differs by variant. A Vitest parity test pins the geometry.
// Tile values and the alphabet moved to the server-sent per-variant table in Stage 13 (see
// lib/alphabet.ts), so this file is geometry only.
import type { Variant } from './model';
export const BOARD_SIZE = 15;
export type Premium = '' | 'TW' | 'DW' | 'TL' | 'DL';
// Legend (rules.go): T=triple word, D=double word, t=triple letter, d=double
// letter, .=plain, *=centre (a double word), +=centre with no premium.
const standardBoard = [
'T..d...T...d..T',
'.D...t...t...D.',
'..D...d.d...D..',
'd..D...d...D..d',
'....D.....D....',
'.t...t...t...t.',
'..d...d.d...d..',
'T..d...*...d..T',
'..d...d.d...d..',
'.t...t...t...t.',
'....D.....D....',
'd..D...d...D..d',
'..D...d.d...D..',
'.D...t...t...D.',
'T..d...T...d..T',
];
// Эрудит: the standard layout but a non-doubling centre ('+').
const eruditBoard = [
'T..d...T...d..T',
'.D...t...t...D.',
'..D...d.d...D..',
'd..D...d...D..d',
'....D.....D....',
'.t...t...t...t.',
'..d...d.d...d..',
'T..d...+...d..T',
'..d...d.d...d..',
'.t...t...t...t.',
'....D.....D....',
'd..D...d...D..d',
'..D...d.d...D..',
'.D...t...t...D.',
'T..d...T...d..T',
];
function template(variant: Variant): string[] {
return variant === 'erudit_ru' ? eruditBoard : standardBoard;
}
function premiumOf(ch: string): Premium {
switch (ch) {
case 'T':
return 'TW';
case 'D':
case '*':
return 'DW';
case 't':
return 'TL';
case 'd':
return 'DL';
default:
return '';
}
}
/** premiumGrid returns the 15x15 premium layout for a variant (row-major). */
export function premiumGrid(variant: Variant): Premium[][] {
return template(variant).map((line) => Array.from(line, premiumOf));
}
/** centre returns the first-move anchor square (row, col). */
export function centre(variant: Variant): { row: number; col: number } {
const lines = template(variant);
for (let r = 0; r < lines.length; r++) {
const c = lines[r].search(/[*+]/);
if (c >= 0) return { row: r, col: c };
}
return { row: 7, col: 7 };
}
// Tile values and the per-variant alphabet now arrive from the server (lib/alphabet.ts,
// Stage 13); the board geometry above is all this module owns.