Check a puzzle before publishing
Besides grading, pencilmark can look for the problems an editor would catch: a rule nobody needs, a puzzle that is
mostly given, a single hard step in an easy solve. Ask for the issues and reject fields:
$ pencilmark rate mini.json --json --fields band,issues,reject
{"line": 1, "format": "sudoku-grade", "format_version": 1, "grader": {"name": "pencilmark", "version": "1.1.0"}, "band": "Medium", "issues": [{"code": "spike", "severity": "warning", "text": "98% of the effort is in the 3 hardest steps"}], "reject": false}
This is the 6×6 puzzle from Write a variant puzzle file. Each issue has a code, a
severity and a sentence. reject is true when any issue has severity reject: a puzzle an
editor would send back. A warning is worth a look but may be intended.
What the checks find
- In the rules: a rule family nobody needs (
decorative,redundant) or that the solve hardly uses (barely_used). - In the givens: too many of them (
trivial). - In the solve: most of the effort in a few steps (
spike), a long routine solve rated easy (tedious), steps that rely on uniqueness or on trial and error (uniqueness,trial), or a difficulty unusual for the band (unusual).
The warning above is a spike: almost all the work is in the three hardest steps, and the rest are singles.
Grade card lists every check with its default limit.
A rule that is not needed
Add a thermometer to a classic, along three cells whose digits happen to increase (1, 5, 7 in the solution):
import pencilmark
doc = pencilmark.to_export("....4.8.3.6...8..29.86.5..1235.........7..2.........96..2......8.12..7...9.......")
doc["rules"].append({"type": "line", "kind": "thermo", "cells": ["r1c1", "r1c2", "r1c3"], "id": "extra-thermo"})
pencilmark.rate(doc, fields=["issues", "reject"])["issues"]
[{"code": "redundant", "severity": "warning", "text": "thermo line (rule 1): used in 1 step, but the puzzle is unique without it", "rules": [1], "ids": ["extra-thermo"]}]
The puzzle is unique without the thermometer, so it is reported. When no step uses such a rule the code is
decorative (a reject); when the solve does use it, as here, the code is redundant (a warning). rules gives the
rule's position in the file and ids its id, so a tool can point at it.
The rule checks look at whole families: all the thermometers together, all the white dots together. One extra dot among dots the puzzle needs is not reported.
Too many givens
A classic with two thirds of its cells given is rejected:
g = ".57.42.6346.17.95.9.86.54.1.35.69.8761.78.24.7.45.13.6.72.94.1884.25.73.3.68.75.4"
pencilmark.rate(g, fields=["band", "issues", "reject"])
# band 'Very Easy', issues [{'code': 'trivial', 'severity': 'reject', 'text': '67% of the cells are given'}], reject True
Tune the checks
Every check takes a severity (reject, warning, or off) and, where it has one, a limit:
card = pencilmark.rate(puzzle, fields=["issues", "reject"],
quality={"trivial": {"limit": 0.7}, "spike": {"severity": "off"}})
With trivial raised to 70%, the puzzle above passes (issues is empty, reject false).
On the command line the same settings are --quality trivial.limit=0.7,spike.severity=off.