e4cf143e9f
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 1m9s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m39s
The offline vs_ai game engine — a faithful TS port of backend/internal/engine that drives a whole local game with no backend. Composes with the move generator (#188) and robot strategy (#189) from Phase A; not yet wired into the UI (Phase B2/B3). - ui/src/lib/localgame/ruleset.ts: static per-variant tile values / bag counts / blank count, mirrored from rules.go (offline scoring is self-contained; online uses the server alphabet). Pinned by ruleset.parity.test.ts against a Go fixture. - bag.ts: the tile bag (fill from counts/blanks, draw-from-end, return+reshuffle) on a deterministic in-house PRNG — a game replays from its seed, not bit-identical to a server game (per plan). - board.ts: the mutable board, satisfying the validator/generator read view + set(). - engine.ts: LocalGame — deal / play (reusing validate.ts) / pass / exchange / resign, scoreless(6) & out-of-tiles end detection, end-of-game rack penalties, winner; mirrors game.go. The end-game math is exported as pure functions, pinned against the Go engine (engine.parity.test.ts, 9 constructed positions). - engine.test.ts: a full-loop smoke — two robots play a whole vs_ai game to completion via generateMoves + decide, and it is reproducible from the seed. - backend: movegen now dumps the per-variant rulesets; a new in-package engine emitter (endfixture_test.go, env-gated) produces the end-game golden. Pure additive library code; no runtime behavior change (unused at runtime, bundle unchanged).
57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
// Static per-variant ruleset data for the offline engine — tile point values, bag tile counts
|
|
// and blank count, indexed by alphabet letter index. Mirrored from scrabble-solver/rules/rules.go
|
|
// (English / RussianScrabble / Erudit) so a local game is fully self-contained: online scoring
|
|
// reads the server-sent alphabet (lib/alphabet.ts), but offline there is no server, so the values
|
|
// live here. Board geometry, the centre and RACK_SIZE/BINGO already live in lib/premiums.ts; this
|
|
// table adds the values (offline copy), the bag distribution and the blank count. Pinned to the Go
|
|
// rulesets by ruleset.parity.test.ts.
|
|
|
|
import type { Variant } from '../model';
|
|
|
|
/** VariantRuleset is the offline scoring + bag data for one variant. */
|
|
export interface VariantRuleset {
|
|
/** Number of letters in the alphabet (bag distribution and values are indexed 0..size-1). */
|
|
size: number;
|
|
/** Tiles drawn to a full rack (7 for every variant). */
|
|
rackSize: number;
|
|
/** All-tiles (bingo) bonus. */
|
|
bingo: number;
|
|
/** Number of blank tiles in the bag. */
|
|
blanks: number;
|
|
/** Tile point value per letter index; a blank scores 0 (handled by the caller). */
|
|
values: readonly number[];
|
|
/** Bag tile count per letter index (how many of each letter the bag holds). */
|
|
counts: readonly number[];
|
|
}
|
|
|
|
/** RULESETS is the static ruleset table, one entry per variant, mirrored from rules.go. */
|
|
export const RULESETS: Record<Variant, VariantRuleset> = {
|
|
scrabble_en: {
|
|
size: 26,
|
|
rackSize: 7,
|
|
bingo: 50,
|
|
blanks: 2,
|
|
// a b c d e f g h i j k l m n o p q r s t u v w x y z
|
|
values: [1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10],
|
|
counts: [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1],
|
|
},
|
|
scrabble_ru: {
|
|
size: 33,
|
|
rackSize: 7,
|
|
bingo: 50,
|
|
blanks: 2,
|
|
// а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я
|
|
values: [1, 3, 1, 3, 2, 1, 3, 5, 5, 1, 4, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 10, 5, 5, 5, 8, 10, 10, 4, 3, 8, 8, 3],
|
|
counts: [8, 2, 4, 2, 4, 8, 1, 1, 2, 5, 1, 4, 4, 3, 5, 10, 4, 5, 5, 5, 4, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2],
|
|
},
|
|
erudit_ru: {
|
|
size: 33,
|
|
rackSize: 7,
|
|
bingo: 15,
|
|
blanks: 3,
|
|
// а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я
|
|
values: [1, 3, 2, 3, 2, 1, 0, 5, 5, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 3, 10, 5, 10, 5, 10, 10, 10, 5, 5, 10, 10, 3],
|
|
counts: [10, 3, 5, 3, 5, 9, 0, 2, 2, 8, 4, 6, 4, 5, 8, 10, 6, 6, 6, 5, 3, 1, 2, 1, 2, 1, 1, 1, 2, 2, 1, 1, 3],
|
|
},
|
|
};
|