Skip to main content

Usage

pencilmark grades, solves, checks, converts and generates sudoku puzzles, from the command line or from Python. Every command and function accepts the same inputs: a classic string, a puzzle file in the puzzle format, a site link or another app's format (Other formats).

Command line​

CommandPrints, per puzzle
pencilmark rate FILEband, difficulty, minutes and the hardest technique
pencilmark solve FILEthe solution, or not unique / no solution / undecided
pencilmark count FILEthe number of solutions (>=1000 when it stops counting; --limit N to change)
pencilmark describe FILEthe variant's name and its rules in words
pencilmark export FILEthe puzzle as a canonical sudoku-puzzle file (--pretty for one rule per line)
pencilmark json FILEthe puzzle in pencilmark's internal JSON
pencilmark fingerprint FILEa content fingerprint, sha256:...
pencilmark generate SPECa new puzzle for a generator spec

FILE holds one puzzle per line (a classic string, a JSON object, a link, or the path of a .json file); - reads standard input. A .json or .ipuz file that holds one puzzle, or a list of puzzles, is read whole. A line that cannot be read prints error, its line number and the message, and the next line goes on. python -m pencilmark works the same way.

$ echo '....4.8.3.6...8..29.86.5..1235.........7..2.........96..2......8.12..7...9.......' | pencilmark rate - --report
#1 Hard - difficulty 5.9 - time 10.7 min
contains: Naked Pair (or Hidden Pair), X-Wing (or 2-String Kite / Simple Colouring / Short chain in the head)

Options of rate​

OptionEffect
--reportreadable text; -v adds the steps in solvers' notation, -vv plain sentences, -vvv short lessons
--jsonthe full card as one JSON line; --steps adds the steps, --explain notation,short,teach their explanations
--fields difficulty,band,...only these card fields (Grade card lists them)
--format '%b|%d|%m'your own columns (below)
-j Ngrade N puzzles at a time; the output keeps the input order
--max-seconds Nstop each puzzle after N seconds (the card says completed: false)
--computerallow computer-style search (forcing chains, trial) as well as human techniques
--allow-guessingguess where no technique applies (band Unknown), instead of proving by full search (Extreme)
--no-alternativeskeep contains but skip the search for interchangeable techniques (faster)
--quality ...tune the publishing checks (Check a puzzle before publishing)
--format fieldMeaning
%b %d %mband, difficulty, minutes
%pminutes within which 80% of players finish
%hhardest technique met
%ctechnique chips above Very Easy (naked singles included), with uses
%nissue codes
%s %u %istatus, unique?, line number

Options of generate​

--seed N and --time S override the spec's limits. The puzzle is printed as a sudoku-puzzle file and its band, difficulty and time go to standard error; --result prints {"puzzle", "card", "info"} as one JSON line instead.

Python​

Grade​

import pencilmark

card = pencilmark.rate("....4.8.3.6...8..29.86.5..1235.........7..2.........96..2......8.12..7...9.......")
card["band"], card["difficulty"], card["time"], card["contains"]

rate takes the same options as the command line: fields=[...], max_seconds=60, explain="short", mode="computer" (the default is "human"), allow_guessing=True, se=True for a Sudoku Explainer-style rating, quality={...}. rate_many(puzzles, jobs=4) grades a list in parallel and keeps its order; a puzzle that fails gives {"error": ...} in its place.

Solve and count​

solution = pencilmark.solve(puzzle)
n = pencilmark.count_solutions(puzzle)

solve returns the unique solution as a list of digits, or raises NotUnique or NoSolution. count_solutions returns {"count": 1, "exact": True} for a proper puzzle. count_solutions(puzzle, limit=1000) counts up to limit; exact is false when it stopped there. Both use the engine's exhaustive search; when that search hits its node limit, OR-Tools CP-SAT takes over if installed (pip install "pencilmark[verify]"), otherwise solve raises Undecided.

Convert and check​

exp = pencilmark.to_export(puzzle, title="Escargot", author="Arto Inkala")
text = pencilmark.dumps(exp)
pencilmark.validate(exp)

to_export gives the sudoku-puzzle dict, with its solution and with meta.type and meta.rules_text filled in from describe when they are empty; dumps writes it in the canonical layout, one rule per line; validate raises FormatError with the exact problem. describe(puzzle) names the variant and its rules, fingerprint(puzzle) gives a fingerprint of the rules and grid (not the metadata), and load(puzzle) the engine's own object. from_string, to_string, from_ipuz and to_ipuz convert to and from other formats.

Generate​

result = pencilmark.generate(spec)
result["puzzle"], result["card"], result["info"]

spec is a generator spec (a dict, JSON text or a path). The result is a sudoku-puzzle with its solution, its grade card and how the run went; GenerationFailed says why when no puzzle met the spec in time. Example specs are in docs/examples/specs/.

Python API lists every function and argument.

The card​

What each field means, and how the techniques in contains are listed: Grade card. Where other sites' difficulty labels fall on pencilmark's scale: Benchmark.

Speed​

Most puzzles take under a second (median 0.49 s on the benchmark, classics 0.04 s); a few very hard variants take minutes while the technique list is made minimal (Benchmark). max_seconds caps any call. Without the cap the result is the same on every machine.