The pack
letterGrid
function letterGrid(): Pack<{ letterGrid: LetterGridApi }>
The letter-grid pack: letter grid word hunt rules over the english-words lists. Pure logic, so it runs headless.
Helpers
createLetterGrid
function createLetterGrid(options: LetterGridOptions): LetterGrid
Roll a board and start a round. Throws a RangeError for options out of range: a board of no tiles, a set board with uneven rows or non-letters, an adjacency other than 4 or 8, a negative time.
scoreShared
function scoreShared(lists: ReadonlyArray<ReadonlyArray<string>>, scoring: Scoring = 'classic'): { readonly words: string[]; readonly score: number }[]
Scores for several players' word lists from one board, with the rule that a word two or more players found scores for nobody. Returns each player's words that scored and their total, in the order given.
scoreWord
function scoreWord(word: string, scoring: Scoring = 'classic'): number
Points for word under scoring. Throws a RangeError for a table or function that gives a negative or non-finite number.
Types
FoundWord
interface FoundWord
A word found, and where.
readonly word: string— The word, lower case.readonly points: number— Points it scored.readonly path: ReadonlyArray<number>— The tiles it was traced through, as indicesy * width + x.
LetterGrid
interface LetterGrid
A round of letter grid word hunting. Make one with createLetterGrid.
observation(): LetterGridView— The round as it stands.spell(path: ReadonlyArray<number>): string | null— The letters a path spells, lower case; null when the path is not a chain of touching tiles used once each.canExtend(path: ReadonlyArray<number>, tile: number): boolean— Can a trace atpathgo on totile? It must touch the last tile and not be in the path already. True for any tile when the path is empty.isPrefix(path: ReadonlyArray<number>): boolean— Does any word that counts start with whatpathspells? What a game shows while a player is still tracing.check(path: ReadonlyArray<number>): { readonly ok: true; readonly word: string; readonly points: number } | Refusal— Would this trace score now?{ ok: true, word, points }, or the refusal. Changes nothing.trace(path: ReadonlyArray<number>): { readonly ok: true; readonly result: FoundWord } | Refusal— Submit a traced path. Refused when it is not a chain, too short, not a word, found already, or the round is over.type(word: string): { readonly ok: true; readonly result: FoundWord } | Refusal— Submit a typed word, accepted when the board can spell it: the pack finds the path.advance(seconds: number): void— Letsecondsof game time pass; the round ends when time runs out. Consecutive advances are saved as one.end(): void— End the round now.undo(): boolean— Take back the last word (time is not given back).hint(): FoundWord | null— A word not yet found, shortest first, with its path; null when every word is found.solve(): ReadonlyArray<FoundWord>— Every word on the board with a path for each, longest first, then alphabetical.snapshot(): HistorySave<LetterGridMove>— The round as a save: the seed and the moves. Restore it into a round made with the same options.restore(save: HistorySave<LetterGridMove> | string): void— Replace this round's moves with a save. Throws, changing nothing, when it does not replay.
LetterGridApi
type LetterGridApi = typeof rules
game.letterGrid: every function and type this pack carries, as one namespace.
LetterGridMove
type LetterGridMove = | { readonly kind: 'trace'; readonly path: ReadonlyArray<number> } | { readonly kind: 'type'; readonly word: string } | { readonly kind: 'advance'; readonly seconds: number } | { readonly kind: 'end' }
A move: a traced path, a typed word, time passing, or the end of the round.
LetterGridOptions
interface LetterGridOptions
How a board and a round are set up. Only seed is required.
seed: number— Rolls the board, as an unsigned 32-bit integer.width?: number— Tiles across. Default 4, or the board's width.height?: number— Tiles down. Default 4, or the board's height.words?: WordList— The words that count. Default the English lexicon.minLength?: number— Shortest word that counts, in letters. Default 3.dice?: ReadonlyArray<string | ReadonlyArray<string>>— Letter dice: each die a string of single-letter faces ('aaeegn') or a list of faces, where a face may be more than one letter (['qu', 'a', 'b', 'c', 'd', 'e']). The dice are shuffled into the tiles and each shows a face; with fewer dice than tiles they are used again. Default: no dice, letters drawn by the word list's letter frequency.board?: ReadonlyArray<string>— A set board instead of a rolled one: one string per row, one letter a tile (qis aqutile whenquis true).qu?: boolean— Show Q as a QU tile, as most letter grids do, since English Q is nearly always followed by U. Default true.minVowels?: number— Fewest vowels (a, e, i, o, u) on a board drawn by frequency. Default a fifth of the tiles, rounded up.minWords?: number— Roll again until the board holds at least this many words, keeping the richest afterattempts. Default 0.attempts?: number— Boards tried forminWords. Default 40.adjacency?: 4 | 8— Which tiles touch: 8 (sides and corners, the default) or 4 (sides only).wrap?: boolean— Do the edges wrap round, so the left column touches the right and the top row the bottom? Default false.scoring?: Scoring— Points per word. Default'classic'.time?: number— Seconds in a round; Infinity for no limit. Default 180.
LetterGridView
interface LetterGridView
The round as it stands. Never lists a word the player has not found until the round is over.
readonly phase: 'playing' | 'over'—'playing'or'over'(time ran out, orendwas called).readonly width: number— Tiles across.readonly height: number— Tiles down.readonly tiles: ReadonlyArray<string>— Each tile's letters, lower case, row by row: indexy * width + x.readonly found: ReadonlyArray<FoundWord>— Words found, in the order found.readonly score: number— Points scored.readonly possible: number— Words on the board in all.readonly maxScore: number— Points on the board in all.readonly timeLeft: number— Seconds left; Infinity for no limit.readonly elapsed: number— Seconds played.readonly byLength: ReadonlyArray<{ readonly length: number; readonly total: number; readonly found: number }>— For each length on the board: how many words of it there are and how many are found.readonly missed: ReadonlyArray<FoundWord>— Once the round is over, every word not found, longest first, each with a path; empty while playing.
Scoring
type Scoring = 'classic' | 'letters' | ReadonlyArray<number> | ((word: string) => number)
Points for a word: 'classic' 1, 1, 2, 3, 5 and 11 for 3, 4, 5, 6, 7 and 8 or more letters; 'letters' a point a letter; a table; or your own function.


