npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reetesh/sudoku-engine

v2.0.0

Published

Sudoku puzzle generator, solver, and game helpers for TypeScript — validation, hints, difficulty levels, batch generation

Downloads

475

Readme

sudoku-engine

Sudoku generator and solver for TypeScript. You pass a 9×9 grid (null for blanks); it generates puzzles, checks moves, solves, and has helpers for hints and imports. No UI included.

Built by Reetesh Kumar · Play Sudoku on iamreetesh.com · Engine deep-dive

npm: @reetesh/sudoku-engine · GitHub: rishureetesh/sudoku-engine

Docs: Overview · Architecture · API · Migration · Roadmap

Runs on Node 18+, in the browser, or in a bundler. No runtime dependencies.

Install

npm install @reetesh/sudoku-engine
import { generatePuzzle, solve } from "@reetesh/sudoku-engine";

// Or only the sudoku module:
import { generatePuzzle } from "@reetesh/sudoku-engine/sudoku";

Quick start

import { generatePuzzle, solve, isSolvedCorrectly } from "@reetesh/sudoku-engine";

const { puzzle, solution, difficulty, clueCount } = generatePuzzle("medium");
const playerBoard = puzzle.map((row) => [...row]);

const { solved, board: solvedBoard } = solve(puzzle);
console.log(solved, difficulty, clueCount);

isSolvedCorrectly(solvedBoard, solution);
  • puzzle — what you show at the start (givens + blanks)
  • solution — full answer; keep server-side if you care about cheating
  • playerBoard — copy of puzzle that the player edits

Every generated puzzle has one solution.

Variants (2.0)

Classic 9×9 APIs are unchanged. For other rules, use createEngine:

import { createEngine } from "@reetesh/sudoku-engine";

const classic = createEngine({ variant: "classic" });
const mini = createEngine({ variant: "6x6" });       // 6×6, 2×3 boxes, digits 1–6
const sudokuX = createEngine({ variant: "diagonal" }); // Sudoku X: unique diagonals
const hyper = createEngine({ variant: "hyper" });       // Hyper: four extra 3×3 regions

mini.generatePuzzle("easy");
sudokuX.validateBoard(board);
hyper.generatePuzzle("hard");

Batch generation with a variant:

import { SudokuEngine } from "@reetesh/sudoku-engine";

new SudokuEngine({ variant: "6x6" }).generateBatch({ count: 8 });

See docs/ARCHITECTURE.md and docs/MIGRATION.md.

Internals

The API is a Board: (1–9 | null)[][].

Inside, row/column/box masks track which digits are used (9 bits each). Candidates come from those masks. The solver uses MRV (pick the cell with fewest options) and backtracking. Generation builds a full grid, removes cells, and checks uniqueness with the same solver.

You only work with Board; conversion to bitmasks is internal.

Boards

type Board = (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | null)[][];
import { createEmptyBoard, boardToString, stringToBoard } from "@reetesh/sudoku-engine";

createEmptyBoard();
boardToString(board);
stringToBoard("530070000600195000...");

Difficulty

Givens per level:

| Level | Givens | |--------|--------| | easy | 40–45 | | medium | 32–39 | | hard | 26–31 | | expert | 22–25 |

generatePuzzle("expert");
generatePuzzle("medium", { symmetric: true });
generateOne({ difficulty: "hard", seed: 20260530 });
dailyPuzzle("2026-05-30", "hard");

By clue count: rateDifficulty(board)

By how far naked singles get you: rateDifficultyByTechniques(board), analyzeTechniques(board)

Validate and solve

import {
  validateBoard,
  isValidMove,
  getCandidates,
  solve,
  countSolutions,
  hasUniqueSolution,
} from "@reetesh/sudoku-engine";

validateBoard(board);
isValidMove(board, row, column, value);
getCandidates(board, row, column);

const { solved, board } = solve(board);
countSolutions(board);
hasUniqueSolution(board);

isValidMove only checks row/column/box conflicts, not whether the puzzle is still solvable.

Play

import { applyMove, isBoardComplete, isSolvedCorrectly } from "@reetesh/sudoku-engine";

const result = applyMove(board, row, column, 5, puzzle);
if (result.success) {
  board = result.board;
}

isBoardComplete(board);
isSolvedCorrectly(board, solution);

Pass puzzle into applyMove so givens stay fixed.

Hints

isGiven(puzzle, row, column);
getGivenCells(puzzle);
getCellDisplayState(puzzle, board, solution, row, column);

revealCell(board, solution, row, column, puzzle);
revealNext(board, solution, puzzle);
revealRandom(board, solution, puzzle);

getCellDisplayState: "given" | "empty" | "player" | "incorrect"

Import

import { puzzleFromString, validateImportedPuzzle } from "@reetesh/sudoku-engine";

const board = puzzleFromString("530070000600195000...");
const result = validateImportedPuzzle(board);

if (result.valid) {
  result.puzzle;
  result.solution;
}

Coordinates

Rows and columns are 08. Bad indices throw SudokuEngineError.

isInBounds(row, column);
assertInBounds(row, column);

Batch

Up to 1000 per call. Default: split evenly across easy/medium/hard/expert.

generateBatch({ count: 100 });

generateBatch({
  count: 50,
  distribution: { easy: 20, medium: 15, hard: 10, expert: 5 },
});

Example

cd examples/react
npm install
npm run dev

Small React app: new game, daily puzzle, hints, cell states.

API

API reference. Types in dist/index.d.ts after npm run build.

Size

Roughly 13 KB minified (ESM entry), tree-shakeable.

Dev

npm install
npm test
npm run typecheck
npm run lint
npm run build

Tests: src/games/sudoku/engine/tests/

Changelog

CHANGELOG.md2.0.0

License

MIT © Reetesh Kumar · iamreetesh.com