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

hodoku-difficulty-rating-ts

v0.2.1

Published

TypeScript port of HoDoKu's logical Sudoku difficulty rating engine.

Readme

hodoku-difficulty-rating-ts

TypeScript port of HoDoKu's logical Sudoku solver, scoped to difficulty rating only.

Given an 81-character puzzle string it returns the difficulty score and band that HoDoKu's Java solver would produce.

Please consider that it is far slower than the original Java version. Use this version only if you need JS support.

Deprecation notice

Development has stopped for this partial port. However, a new version powered by TeaVM JS transpilation can be found here. It is much faster than this port and parity is the same, it includes generation, step finding, and additional QoL features.

Difficulty bands

These values are maximum scores, not exclusive score ranges. A puzzle can still be rated above the score-only ceiling if it uses a technique whose minimum difficulty is higher. For example, a MEDIUM puzzle can have an EASY score.

| Difficulty | Max score | |---|---:| | EASY | 800 | | MEDIUM | 1000 | | HARD | 1600 | | UNFAIR | 1800 | | EXTREME | none |

Current parity

Take this numbers as reference only, not guaranteed results. If you find a disparity, please report.

These numbers are generated using parity tool, which uses Java QQwing (generation of 5000 puzzles) and original Hodoku (solving). Then the sudokus are solved using current library, and scores are compared.

You can run it using npm run parity-tool

Note: the tool excludes known disparities. So also check that file.

| Difficulty | Difficulty parity | Score parity | |-----------------------|-----------------------|--------------------| | Easy | 100% | 100% | | Medium | 100% | 100% | | Hard | 100% | 100% | | Unfair | 100% | 99.7% | | Extreme | 99.74% | 97.91% |

Scope

Included:

  • Full logical solver engine (naked singles → forcing chains) — all techniques contribute to scoring
  • SudokuSolver.rate() and SudokuSolver.rateByScore() — public rating entry points
  • Difficulty bands: EASY, MEDIUM, HARD, UNFAIR, EXTREME
  • Scores that match the Java reference implementation

Not included:

  • Step-by-step API (getStep, doStep, solve)
  • Access to the internal grid state (Sudoku2, candidates, placed values)
  • Solution strings or candidate lists
  • Display metadata (fish base/cover sets, fin cells)
  • Puzzle generation

Usage

import { SudokuSolver } from "hodoku-difficulty-rating-ts";

const { solved, difficulty, score } = SudokuSolver.rate(
  "...253..87..1.......1....4...8.94...5.......71.95.8....1......2...785...3.4.2....",
);

console.log(solved);      // true
console.log(difficulty);  // "EASY"
console.log(score);       // 224

Optional difficulty cap

Stop early if the puzzle exceeds a given difficulty band:

const r = SudokuSolver.rate(puzzle, "HARD");
if (!r.solved) {
  console.log("Puzzle exceeds HARD");
}

Optional score cap

Stop early if the puzzle exceeds a given score:

const r = SudokuSolver.rateByScore(puzzle, 1600);
if (!r.solved) {
  console.log("Puzzle exceeds score 1600");
}

API

SudokuSolver.rate(puzzle, maxDifficulty?)

| Parameter | Type | Default | Description | |---|---|---|---| | puzzle | string | — | 81-character string; '0' or '.' for empty cells. | | maxDifficulty | DifficultyType | "EXTREME" | Stop and return solved: false when score exceeds this band's ceiling. |

SudokuSolver.rateByScore(puzzle, maxScore?)

| Parameter | Type | Default | Description | |---|---|---|---| | puzzle | string | — | 81-character string; '0' or '.' for empty cells. | | maxScore | number | Number.MAX_SAFE_INTEGER | Stop and return solved: false when cumulative score exceeds this threshold. |

Returns SolveRating:

interface SolveRating {
  solved:     boolean;        // true when all 81 cells were filled by logic
  score:      number;         // cumulative HoDoKu score
  difficulty: DifficultyType; // "EASY" | "MEDIUM" | "HARD" | "UNFAIR" | "EXTREME"
}

Difficulty ceilings (matching Java): EASY max 800, MEDIUM max 1000, HARD max 1600, UNFAIR max 1800, EXTREME has no score ceiling. Difficulty is also bounded below by the hardest technique used, so the final difficulty is not determined by score alone.

Building from source

npm install
npm run build   # outputs CJS + ESM + .d.ts to dist/
npm test        # runs Jest regression tests against test/test_data.csv

License

GPL-3.0-only — ported from HoDoKu, Copyright © 2008–2012 Bernhard Hobiger.