Reference
API Reference
Function signatures, types, PuzzleEngine methods, and house builders.
API reference
Canonical types ship in dist/index.d.ts after npm run build. This page is the product-facing map of every public surface.
Related: Overview · Guides · Troubleshooting · Migration
Imports
| Path | Notes |
|---|---|
@reetesh/sudoku-engine | Main entry (recommended) |
@reetesh/sudoku-engine/sudoku | Same exports (alias path) |
Empty cells are always null (never 0). Solver/generator use bitmasks internally; you only see Board.
Variant system
import {
createEngine,
listVariants,
getVariant,
CLASSIC_VARIANT,
SIX_BY_SIX_VARIANT,
DIAGONAL_VARIANT,
HYPER_VARIANT,
WINDOKU_VARIANT,
JIGSAW_VARIANT,
KILLER_VARIANT,
THERMO_VARIANT,
KROPKI_VARIANT,
SAMURAI_VARIANT,
} from "@reetesh/sudoku-engine";
type VariantId =
| "classic"
| "6x6"
| "diagonal"
| "hyper"
| "jigsaw"
| "windoku"
| "killer"
| "thermo"
| "kropki"
| "samurai";
createEngine({ variant: "classic" /* default */ });
listVariants(); // readonly SudokuVariant[]
getVariant("samurai"); // throws SudokuEngineError if unknownTop-level solve, generatePuzzle, validateBoard, … always target classic 9×9. For every other preset, use createEngine.
createEngine(options?)
Creates a variant-scoped PuzzleEngine.
| Parameter | Type | Default | Description |
|---|---|---|---|
options.variant | VariantId | "classic" | Named preset (ignored when definition is set) |
options.definition | SudokuVariant | — | Full custom ruleset (defineVariant + house builders) |
Returns: PuzzleEngine
const mini = createEngine({ variant: "6x6" });
import {
defineVariant,
composeHouses,
CLASSIC_GRID,
buildRowHouses,
buildColumnHouses,
buildIrregularRegionHouses,
getVariant,
} from "@reetesh/sudoku-engine";
const custom = createEngine({
definition: defineVariant({
...getVariant("jigsaw"),
id: "my-jigsaw",
houses: composeHouses(CLASSIC_GRID, [
...buildRowHouses(CLASSIC_GRID),
...buildColumnHouses(CLASSIC_GRID),
...buildIrregularRegionHouses(CLASSIC_GRID, myRegionMap),
]),
}),
});PuzzleEngine
All methods use the engine’s variant (grid size, digits, houses, meta, playable mask).
Properties
| Property | Type | Description |
|---|---|---|
variant | SudokuVariant | Full ruleset object |
variantId | string | variant.id (preset id or custom) |
Meta constraints (Killer / Thermo / Kropki)
| Method | Parameters | Returns | Description |
|---|---|---|---|
withMeta(meta) | readonly MetaConstraint[] | PuzzleEngine | New engine scoped with those per-puzzle rules |
generatePuzzle for killer / thermo / kropki returns metaConstraints on GeneratedPuzzle. Attach them before solve / validate / candidates:
const engine = createEngine({ variant: "killer" });
const game = engine.generatePuzzle("easy")!;
const scoped = engine.withMeta(game.metaConstraints!);
scoped.solve(game.puzzle);Also exported: variantWithMeta, validateMetaConstraints, isPlayableCell.
Board helpers
| Method | Parameters | Returns | Errors |
|---|---|---|---|
createEmptyBoard() | — | Board | — |
cloneBoard(board) | Board | Board | — |
isValidBoardShape(board) | unknown | board is Board | — |
countClues(board) | Board | number | — |
boardsEqual(a, b) | Board, Board | boolean | — |
allCellCoordinates() | — | { row, column }[] | Full canvas (incl. Samurai inactive) |
boardToString(board) | Board | string | Digits + . for empty |
stringToBoard(serialized) | string | Board | null | null if length ≠ size² |
isInBounds(row, column) | number, number | boolean | — |
assertInBounds(row, column) | number, number | void | SudokuEngineError if OOB |
Validation
| Method | Parameters | Returns | Notes |
|---|---|---|---|
validateBoard(board) | Board | boolean | Shape + houses + meta (when attached) |
isValidMove(board, row, column, value) | Board, coords, digit | boolean | Peer uniqueness; allows replacing same digit in-cell |
Candidates
| Method | Parameters | Returns |
|---|---|---|
getCandidates(board, row, column) | Board, coords | number[] |
Solver
| Method | Parameters | Returns |
|---|---|---|
solve(board) | Board | SolverResult { solved, board } |
countSolutions(board, limit?) | Board, number? | number |
hasUniqueSolution(board) | Board | boolean |
findEmptyCell(board) | Board | { row, column } | null |
Generator
| Method | Parameters | Returns | Notes |
|---|---|---|---|
generateSolvedBoard() | — | Board | Full valid grid |
generatePuzzle(difficulty, options?) | Difficulty, GeneratePuzzleOptions? | GeneratedPuzzle | null | null if retries exhausted |
removeCells(solution, targetClues, maxAttempts) | Board, number, number | Board | null | Carve while unique |
removeCellsSymmetric(...) | same | Board | null | 180° symmetric removal |
GeneratePuzzleOptions: { maxAttempts?: number; symmetric?: boolean }
GeneratedPuzzle
{
difficulty: Difficulty;
puzzle: Board;
solution: Board;
clueCount: number;
metaConstraints?: readonly MetaConstraint[]; // killer / thermo / kropki
}Difficulty
| Method | Parameters | Returns |
|---|---|---|
rateDifficulty(board) | Board | Difficulty (clue-count bands) |
getTargetClueCount(difficulty) | Difficulty | number |
isClueCountInRange(difficulty, clueCount) | Difficulty, number | boolean |
rateDifficultyByTechniques(board) | Board | Difficulty |
analyzeTechniques(board) | Board | TechniqueAnalysis |
Play
| Method | Parameters | Returns |
|---|---|---|
applyMove(board, row, column, value, puzzle?) | Board, coords, digit|null, optional givens | ApplyMoveResult |
isBoardComplete(board) | Board | boolean |
isSolvedCorrectly(board, solution) | Board, Board | boolean |
ApplyMoveResult: { success, board, reason? }
reason (when success === false) | When |
|---|---|
"That cell is a given." | puzzle passed and cell is a clue |
"Value must be … or null." | Digit outside variant digit set |
"Digit conflicts with a house…" | House uniqueness fails |
OOB → throws SudokuEngineError.
Hints
| Method | Parameters | Returns |
|---|---|---|
isGiven(puzzle, row, column) | Board, coords | boolean |
getGivenCells(puzzle) | Board | { row, column }[] |
revealCell(board, solution, row, column, puzzle?) | … | RevealResult | null |
revealNext(board, solution, puzzle?) | … | RevealResult | null |
revealRandom(board, solution, puzzle?) | … | RevealResult | null |
getCellDisplayState(puzzle, board, solution, row, column) | … | CellDisplayState |
CellDisplayState: "given" | "empty" | "player" | "incorrect"
RevealResult: { board, row, column, value }
Import
| Method | Parameters | Returns |
|---|---|---|
puzzleFromString(serialized) | string | Board | null |
validateImportedPuzzle(board) | Board | ImportValidationResult |
ImportValidationResult: { valid, unique, solvable, error?, puzzle? }
ImportedPuzzle (when valid): { puzzle, solution, clueCount }
Classic top-level functions
Thin facades over a lazy classic engine. Prefer createEngine for variants.
| Area | Exports |
|---|---|
| Generate | generatePuzzle, generateSolvedBoard, generateOne, generateBatch, BatchEngine |
| Solve | solve, countSolutions, hasUniqueSolution, findEmptyCell |
| Validate | validateBoard, isValidMove |
| Candidates | getCandidates |
| Play | applyMove, isBoardComplete, isSolvedCorrectly |
| Hints | isGiven, getGivenCells, revealCell, revealNext, revealRandom, getCellDisplayState |
| Import | puzzleFromString, validateImportedPuzzle |
| Daily | dailyPuzzle, dateToSeed |
| Difficulty | rateDifficulty, rateDifficultyByTechniques, analyzeTechniques, getTargetClueCount, isClueCountInRange |
| Board | createEmptyBoard, cloneBoard, boardsEqual, countClues, boardToString, stringToBoard, isValidBoardShape, allCellCoordinates |
| Coordinates | isInBounds, assertInBounds |
| Constants | GRID_SIZE (9), BOX_SIZE, CELL_COUNT, DIGITS, CLUE_RANGES, DIFFICULTIES, MAX_BATCH_SIZE (1000), MIN_CLUES (17) |
| Seeding | setGlobalSeed, clearGlobalSeed |
| Errors | SudokuEngineError |
| Meta helpers | variantWithMeta, validateMetaConstraints, isPlayableCell |
| House builders | CLASSIC_GRID, SIX_BY_SIX_GRID, SAMURAI_GRID, DEFAULT_JIGSAW_REGION_MAP, SAMURAI_BLOCK_ANCHORS, buildRowHouses, buildColumnHouses, buildRegionHouses, buildIrregularRegionHouses, buildWindokuRegionHouses, buildHyperRegionHouses, buildSamuraiPlayableCells, jigsawSudokuHouses, windokuSudokuHouses, hyperSudokuHouses, samuraiSudokuHouses, composeHouses |
| Define | defineVariant |
dailyPuzzle(date, difficulty?)
| Parameter | Type | Default |
|---|---|---|
date | string (YYYY-MM-DD) | — |
difficulty | Difficulty | "medium" |
Returns: GeneratedPuzzle (classic, deterministic for that date).
BatchEngine
new BatchEngine({ seed?: number; variant?: VariantId });
engine.generateOne({ difficulty, seed? }): GeneratedPuzzle; // throws on failure
engine.generateBatch({ count, distribution? }): GeneratedPuzzle[];| Option | Notes |
|---|---|
seed | Base seed; batch items use seed + index |
variant | Any VariantId (default "classic") |
count | 1 … MAX_BATCH_SIZE |
distribution | Partial counts per difficulty; must sum to count if provided |
Also: generateOne / generateBatch / resolveDistribution as free functions (classic unless you construct BatchEngine with a variant).
Errors: SudokuEngineError for invalid batch size, bad distribution, or exhausted generation retries.
Meta constraint types
type MetaConstraint =
| CageSumConstraint
| ThermometerConstraint
| KropkiConstraint;
interface CageSumConstraint {
id: string;
kind: "cage-sum";
cells: readonly CellRef[];
sum: number;
uniqueDigits: true;
}
interface ThermometerConstraint {
id: string;
kind: "thermometer";
cells: readonly CellRef[]; // bulb → tip
}
interface KropkiConstraint {
id: string;
kind: "kropki";
a: CellRef;
b: CellRef;
dot: "black" | "white"; // ratio 2 | consecutive
}
interface CellRef {
row: number;
column: number;
}Shared types (summary)
| Type | Description |
|---|---|
Board | (Digit | null)[][] |
Digit / CellValue | Number digit or null |
Difficulty | "easy" | "medium" | "hard" | "expert" |
VariantId | Named presets (see above) |
SudokuVariant | Full variant metadata (id: string) |
CreateEngineOptions | { variant?, definition? } |
PuzzleEngine | Variant-scoped interactive API |
BatchEngine / BatchEngineOptions | Seeded batch generation |
SolverResult | { solved, board } |
GeneratedPuzzle | Puzzle + solution + metadata |
GeneratePuzzleOptions | { symmetric?, maxAttempts? } |
GenerateBatchOptions / GenerateOneOptions | Batch helpers |
RevealResult / CellDisplayState | Hints / UI |
ApplyMoveResult | Move outcome |
ImportValidationResult / ImportedPuzzle | Import pipeline |
TechniqueAnalysis | Technique counts for difficulty |
GridSpec, House, HouseKind, HouseSet | Extension layer |
ClueRange, ClueRanges, GenerationProfile | Variant tuning |
DifficultyDistribution | Counts per difficulty |
Error handling
| Situation | Behavior |
|---|---|
| Out-of-bounds coords | SudokuEngineError from assertInBounds / applyMove |
| Unknown variant id | SudokuEngineError from getVariant |
| Invalid board shape in BitGrid | SudokuEngineError |
| Soft generation failure | generatePuzzle → null |
| Hard batch failure | BatchEngine → throws SudokuEngineError |
| Bad import string | puzzleFromString → null |
| Invalid player move | applyMove → { success: false, reason } |
| Bad batch distribution / size | SudokuEngineError |
SudokuEngineError extends Error — catch by instanceof when you need structured handling.
End-to-end examples
// Classic
generatePuzzle("medium");
// Diagonal
const x = createEngine({ variant: "diagonal" });
x.generatePuzzle("hard");
// Killer (meta required)
const killer = createEngine({ variant: "killer" });
const g = killer.generatePuzzle("easy")!;
killer.withMeta(g.metaConstraints!).solve(g.puzzle);
// Samurai
createEngine({ variant: "samurai" }).generatePuzzle("easy");
// Batch
new BatchEngine({ variant: "windoku", seed: 1 }).generateBatch({ count: 8 });