Skip to main content

Grade card

The sudoku-grade format, version 1: what a grader reports about one puzzle. The card is separate from the puzzle format: it refers to a puzzle but does not contain it. The key words MUST, MUST NOT, SHOULD and MAY are used as in RFC 2119.

What rate() returns and pencilmark rate --json prints: one JSON object per puzzle. JSON Schema: sudoku-grade.schema.json.

{"format": "sudoku-grade", "format_version": 1,
"grader": {"name": "pencilmark", "version": "1.1.0"},
"difficulty": 5.9, "time": 10.7, "band": "Hard",
"contains": [{"code": "HSI", "chip": "HSI", "name": "Hidden Single", "band": 0},
{"code": "NSI", "chip": "NSI", "name": "Naked Single", "band": 1},
{"code": "NPR", "chip": "NPR/HPR", "name": "Naked Pair", "band": 2, "alternatives": ["HPR"], "uses": 1},
{"code": "XWG", "chip": "XWG/KTE/SCL/MCH", "name": "X-Wing", "band": 3, "alternatives": ["KTE", "SCL", "MCH"], "uses": 1}]}

A card exists only for a puzzle with exactly one solution. For any other puzzle, rate() raises NotUnique, NoSolution or Undecided; rate_many() and the command line give {"error": "…"} in its place (the command line adds "line", the input line number, to every object).

1. Keys​

KeyFieldTypeMeaning
format, format_versionalways"sudoku-grade", 1
graderalways{"name", "version"}The engine that made the card. A card is only meaningful with that engine and version.
difficultydefaultnumber or nullHow hard the puzzle feels; continuous and positive (a nearly finished grid is about 0.2), no ceiling. Hardness, not amount: independent of board size and length, which belong to time. Independent of band: variants often sit above the classic range for their band. Null when the band is Unknown. Exact definition: Philosophy.
timedefaultnumber or nullPredicted median solving time in minutes over the people who solve puzzles like this one, without long breaks. Null when the band is Unknown.
time_mean, time_pNNoptionalnumber or nullSpread of solve times over today's players: the mean, or the NN-th percentile (time_p80 = 80% of players finish within it; NN from 1 to 99). Solves without long pauses; a log-normal fitted on two sources' per-player times.
time_distoptionalobject or nullThe time distribution's parameters: {"type": "lognormal", "sigma_players": …, "sigma_model": …}, around the median time. sigma_players is the spread of log solve time over today's players (0.79 for classics and multi-grid layouts, 0.62 for puzzles with any other rule; measured on grandgames and sudoku.coach), which time_mean and time_pNN use; sigma_model is the typical error of the predicted median itself (0.33 / 0.48).
banddefaultstring"Very Easy", "Easy", "Medium", "Hard", "Very Hard", "Expert", "Extreme"; "Unknown" when the band could not be determined: guessing was allowed and used, or max_seconds passed in the difficulty route. The band orders the knowledge a puzzle needs, not its effort: a Hard puzzle can have a lower difficulty than a Medium one (Philosophy).
band_indexoptionalinteger or null0 (Very Easy) … 6 (Extreme); null for Unknown. Handy for sorting, or for mapping the bands to other names.
containsdefaultlistThe techniques met on the easiest route, easiest first (section 3).
stopped_at_stepwith contains, when human logic ran outintegerThe first step human logic could not make (band Unknown with guessing allowed); the contains list ends there.
completedwith max_secondsbooleanTrue when grading finished within max_seconds; absent without a limit (grading then always finishes). False: the limit passed. If it passed in the difficulty route, band is "Unknown" and difficulty / time are null, with the techniques found so far in contains; if it passed only in the search for the shortest list, the grade stands but the list may not be minimal or may miss alternatives. max_seconds bounds the whole call (uniqueness proof, card, technique list); a puzzle whose uniqueness is not proven within it raises Undecided. Band "Unknown" with completed true means human logic ran out (allow_guessing).
elapsedoptionalnumberWall-clock seconds the grader took for this card (for rate(): the uniqueness proof plus the card). Machine-dependent; not part of the grade.
issuesoptionallistPublishing checks (section 4).
rejectoptionalbooleanTrue when any issue has severity reject.
warningoptionalbooleanTrue when any issue has severity warning.
seoptional{"er", "ep", "ed", "scale"} or nullSudoku Explainer-style ratings of the route on the classic SE scale (scale is "classic"). Null for anything but classic 9×9 sudoku (standard rows, columns and boxes, no other rule), where SE's scale is not defined.
stepsoptionallistThe step-by-step route (section 5).
describeoptionalobjectThe describe() output (How variants are named).
symmetryoptionallistSymmetries of the givens pattern, entries as in the generator spec ({"kind", "axis"?, "of": "givens"}).

Numbers are rounded as emitted: difficulty, time, time_mean and time_pNN to 1 decimal, the sigmas of time_dist to 2. Two puzzles whose rounded values are equal are not distinguished by the card.

2. Choosing fields​

rate(puzzle, fields=[...]) and --fields a,b,c take a list of the keys above; only those are computed. A field name is always the key it produces (contains also brings stopped_at_step). Without fields, the default fields are returned. explain= adds explanations to steps, at one or more levels (CLI --explain notation,short,teach; section 5). Shorthands: steps=True = the steps field with every explanation level; se=True = the se field.

3. contains​

Each entry is one technique on the route:

KeyMeaning
codeThree-letter technique code (Techniques lists them).
chipcode plus its alternatives, /-separated: any one of them gets the solver through at that point.
nameTechnique name.
bandThe technique's band index.
usesTimes used on the route; omitted for singles (full house, hidden and naked singles, a rule's last cell), whose count depends on the route.
alternativesOther codes that also work there; omitted when there are none; null when the producer did not search (pencilmark: alternatives=False, CLI --no-alternatives; band, difficulty and time are the same).

A full house (FHS) is always both a naked and a hidden single, so it is listed under the hidden single (or the naked single when there is no hidden one) rather than as a chip of its own; FHS appears only when no other single is on the route. Its steps keep the code FHS.

4. issues​

The checks run only when the card is asked for issues (or reject); a default card does not run them.

{"code", "severity", "text", "rules"?, "ids"?}. severity is "reject" (would not be published) or "warning". rules, when present, lists the offending rules as indices into the rules array of the puzzle file that was graded (0-based; for other inputs, of the puzzle as to_export writes it); ids lists those rules' ids where they have one.

Each check can be adjusted with a quality object keyed by code. Every entry is an object with limit (checks that have one) and / or severity ("reject", "warning", or "off" to turn the check off):

pencilmark.rate(puzzle, fields=["issues", "reject", "warning"],
quality={"trivial": {"limit": 0.5}, "trial": {"severity": "reject"}, "tedious": {"severity": "off"}})

CLI: --quality trivial.limit=0.5,trial.severity=reject,tedious.severity=off. The generator spec reserves a quality block of the same shape for a later version (Generator spec, section 7).

CodeSeverity when asked forMeaningLimit (default)
spikewarningA Medium or harder puzzle whose 3 hardest steps carry more than limit of its effort (effort per step as in difficulty).share (0.6)
decorativerejectA rule family (for example all the thermometers, or all the white dots) that is neither needed for uniqueness nor used by any step.–
barely_usedreject when never used, else warningA rule family used in fewer than limit steps.steps (3)
redundantwarningA rule family the puzzle is unique without, although the solve uses it.–
trivialrejectMore than limit of the cells are given (0.6 = 60%).share (0.6)
tediouswarningVery Easy or Easy, but over limit minutes of routine work.minutes (60)
uniquenesswarningNeeds a uniqueness-based technique (relies on assuming one solution).–
trialwarningNeeds trial and error or forcing chains.–
unusualrejectA difficulty outside the middle 80% of published puzzles of the same band (classic 9×9 and variants apart; Very Easy has no floor, Extreme no ceiling; Generate a puzzle has the ranges).–

The three rule checks look at whole families: one extra dot among needed dots is not reported.

Example: {"code": "barely_used", "severity": "reject", "text": "thermo lines (rules 3, 4, 5, 6): used in only 0 steps of the solve", "rules": [3, 4, 5, 6]}.

5. steps​

Which route the steps walk depends on whether the card asks for the techniques. When it asks for contains (or for issues, reject or warning, which read the technique list), the steps walk the route of the shortest technique list, so they use exactly the techniques contains lists. Without them, the steps walk the full first route: easy to hard, the simplest technique first, as the band is found. Band, difficulty and time are the same either way.

Each step: n (from 1), code, name, band (the same keys as in contains), then placement ("r2c8=3") and / or eliminations (["r4c5#7", …]: candidate 7 removed from r4c5), and, when requested, explanation with the requested levels:

LevelExample
notationX-Wing: 3 r58 c68 => r679c6<>3: the notation solvers and forums share (HoDoKu's form: technique, digits, pattern, => result; <> = "is not")
shortr6c6, r7c6, r9c6 ≠ 3: X-Wing on 3 (row 5 and row 8 / column 6 and column 8): one plain sentence, result first
teacha few sentences: where to look, why it works, what it achieves

notation is always present for a step; short and teach are left out of a step the explainer could not reconstruct.

6. Implementing a subset​

Only format, format_version and grader are required; every other key is present only when it was asked for (section 2). A producer MAY therefore emit just the headline numbers, for example difficulty, time and band, and a consumer that needs only those MAY ignore every other key. A consumer MUST NOT compare cards from different grader names or versions as if they were on one scale.