Phaser AE

Letter grid word hunt

Pro packv3.0.0-0WordNew

A board of letter tiles and a clock: find as many words as you can by tracing through tiles that touch. The pack rolls the board, judges every word, keeps the score and the time, and shows every word you missed.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 21, 2026

Letter grid word hunt: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

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 indices y * 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 at path go on to tile? 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 what path spells? 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 — Let seconds of 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 (q is a qu tile when qu is 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 after attempts. Default 0.
  • attempts?: number — Boards tried for minWords. 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, or end was called).
  • readonly width: number — Tiles across.
  • readonly height: number — Tiles down.
  • readonly tiles: ReadonlyArray<string> — Each tile's letters, lower case, row by row: index y * 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.