Guides
Variants Guide
Killer, Thermo, Kropki meta constraints, Samurai playable cells, and preset listing.
Always use the same engine instance for a session
const engine = createEngine({ variant: "windoku" });
const game = engine.generatePuzzle("medium")!;
// play / solve / validate with `engine` — not top-level classic helpersKiller / Thermo / Kropki (meta variants)
These look like classic 9×9 houses, but each generated puzzle carries extra rules.
import { createEngine } from "@reetesh/sudoku-engine";
import type { MetaConstraint } from "@reetesh/sudoku-engine";
const engine = createEngine({ variant: "killer" }); // or "thermo" | "kropki"
const game = engine.generatePuzzle("easy");
if (!game?.metaConstraints) throw new Error("expected meta");
// Persist both puzzle AND metaConstraints in your save file / API
function saveGame(game: {
puzzle: typeof game.puzzle;
solution: typeof game.solution;
meta: readonly MetaConstraint[];
}) {
/* your storage */
}
const play = engine.withMeta(game.metaConstraints);
play.validateBoard(game.puzzle); // includes cage / thermo / kropki rules
play.getCandidates(game.puzzle, 0, 0);
play.solve(game.puzzle);
play.applyMove(/* … */); // houses + meta on the scoped enginePersist metaConstraints with the puzzle. Reloading only the digit grid and solving with a bare killer engine ignores cages.
Meta shapes (discriminated by kind):
kind | Fields | Rule |
|---|---|---|
"cage-sum" | cells, sum, uniqueDigits: true | Digits unique in cage; filled cage sums to sum |
"thermometer" | cells (bulb → tip) | Strictly increasing along the path |
"kropki" | a, b, dot: "black" | "white" | Ratio 2 or consecutive |
Samurai
const samurai = createEngine({ variant: "samurai" });
const game = samurai.generatePuzzle("easy")!;
game.puzzle.length; // 21
// Many cells are permanently null (inactive). Do not force-fill them.
samurai.isBoardComplete(game.solution); // true — only playable cells matter
samurai.allCellCoordinates(); // full 21×21 canvas; filter with isPlayableCell if needed
import { isPlayableCell } from "@reetesh/sudoku-engine";
isPlayableCell(samurai.variant, row, col);UI tip: dim or omit inactive cells; never treat them as errors when empty.
Listing presets
import { listVariants, getVariant } from "@reetesh/sudoku-engine";
listVariants().map((v) => v.id);
getVariant("hyper").clueRanges;