Skip to main content

Formats

Reading, checking and writing puzzles and generator requests; the rule families and the variant names. Generated from the docstrings by tools/gen_reference_md.py; public names only.

pencilmark.inputs​

src/pencilmark/inputs.py

Reading a puzzle: every input form pencilmark accepts, in the order they are tried. load(), to_export() and the command line all read through here.

read(x) -> a puzzle dict: puzzle JSON, a sudoku-puzzle document, an ipuz file or a sample-pack line

to_puzzle_json(obj) -> puzzle JSON (pencilmark.puzzle_json), whatever read() returned
NameWhat it does
def linkpuzzle JSON for a site link or payload (pencilmark.sources: SudokuPad, f-puzzles, Penpa, Logic Masters,…
def readany accepted input -> a puzzle dict (see the module docstring); TypeError for an input of no known form
def to_puzzle_jsonaccept puzzle JSON, the sudoku-puzzle exchange format, an ipuz sudoku, or a sample-pack line {"puzzle": ...,…

source

puzzle JSON for a site link or payload (pencilmark.sources: SudokuPad, f-puzzles, Penpa, Logic Masters, short links; some fetch one page), or None when s is not one

def read(x)​

source

any accepted input -> a puzzle dict (see the module docstring); TypeError for an input of no known form

def to_puzzle_json(obj)​

source

accept puzzle JSON, the sudoku-puzzle exchange format, an ipuz sudoku, or a sample-pack line {"puzzle": ..., "grade": ...}.

pencilmark.exchange​

src/pencilmark/exchange/init.py

The "sudoku-puzzle" exchange format, format_version 1 (Puzzle format): engine Puzzle <-> exchange dict.

write(p, meta=None, solution=None) -> dict engine Puzzle -> canonical sudoku-puzzle dict

to_internal(doc) -> dict sudoku-puzzle dict -> puzzle JSON (pencilmark.puzzle_json)
read(doc) -> Puzzle sudoku-puzzle dict -> engine Puzzle (not finalized)
validate(doc) strict reading checks (docs/formats/puzzle-format.md section 4); raises FormatError
dumps(doc, pretty=True) -> str canonical JSON text (docs/formats/puzzle-format.md 8.1)
fingerprint(doc) -> "sha256:..." content fingerprint (docs/formats/puzzle-format.md 8.2)

The format is a transport layer: read(write(p)) builds the same engine structure as p (groups in order, relations and constraints in order, domains, givens, coordinates), except for the normalisations listed in engine-mapping (each mechanic gets ONE engine expansion). The writer therefore works from the engine structure directly and keeps the order of relations and constraints (the order changes cards).

pencilmark.exchange.common​

src/pencilmark/exchange/common.py

The format's constants, errors, cell names and operator parameters.

NameWhat it does
class FormatErrorthe file breaks the format (unknown key / rule type, bad cell, failed structural check).
class NotSupportedErrorthe file is valid, but uses something the pencilmark engine cannot build (or an engine puzzle the format…
def cell_name0-based (row, col) -> 'r1c1'
def parse_cell'r1c1' -> 0-based (row, col)
def op_paramsan operator's named parameter in a rule or an any_of entry -> the positional params list
def op_namedpositional params -> the named parameter key(s) of a rule or any_of entry

class FormatError(ValueError)​

source

the file breaks the format (unknown key / rule type, bad cell, failed structural check).

class NotSupportedError(ValueError)​

source

the file is valid, but uses something the pencilmark engine cannot build (or an engine puzzle the format cannot express).

def cell_name(r, c)​

source

0-based (row, col) -> 'r1c1'

def parse_cell(s)​

source

'r1c1' -> 0-based (row, col)

def op_params(r)​

source

an operator's named parameter in a rule or an any_of entry -> the positional params list

def op_named(op, params)​

source

positional params -> the named parameter key(s) of a rule or any_of entry

pencilmark.exchange.board​

src/pencilmark/exchange/board.py

The board: cells, houses, areas, and the digit tables some rules expand to.

NameWhat it does
class Boardcoordinates of the existing cells (0-based) and helpers shared by the reader and the writer.
def subgrid_unitsrows, columns, boxes of an n x n sub-grid given as n*n engine cells in its reading order (puzzle_json's…
def windowindices of the n x n cells with top-left corner (r0, c0); None if one is missing
def grid_unitshouses of a grid at (r0, c0): its D rows (top to bottom), D columns (left to right) and, for a box_grid with…
def grid_areaset of the cell indices in the D x D square with top-left corner (r0, c0)
def read_housesthe house groups a rule list states, in engine order -> (houses [(kind, cells, extra)], areas [cell sets])…
def battenburg_tableallowed 2x2 digit tuples (TL, TR, BL, BR) of a battenburg window, marked or unmarked
def vudoku_tableallowed (x, y, z) triples: x is y + z or |y - z|
def product_pairsdigit pairs [u, v] in 1..D with u * v == p

class Board​

source

coordinates of the existing cells (0-based) and helpers shared by the reader and the writer.

def name(self, i)​

source

cell index i -> its name (r1c1)

def idx(self, name)​

source

cell name -> index; FormatError if it is not a cell of the board

def adjacent(self, a, b)​

source

True if cells a and b share an edge

def touching(self, a, b)​

source

True if cells a and b touch by an edge or a corner

def pairs(self, steps, cells=None)​

source

forward neighbour pairs in the engine's order (puzzle_json._pairs): by cell, then by step.

def windows2x2(self)​

source

2x2 blocks of existing cells (TL, TR, BL, BR) in puzzle_json's global-entropy order (sorted by top-left).

def is_block(self, cells)​

source

True if the four cells are a 2x2 block written TL, TR, BL, BR

def outside_side(self, cells)​

source

side of an outside clue whose ordered cells start next to it, or None

def outside_ok(self, side, cells, diagonal_ok=True)​

source

True if check_outside accepts cells on that side

def check_outside(self, side, cells, diagonal_ok=True, what='outside clue')​

source

raise FormatError unless cells start just outside the grid on side and run away from it (diagonally too when diagonal_ok)

def subgrid_units(grid, n, box)​

source

rows, columns, boxes of an n x n sub-grid given as n*n engine cells in its reading order (puzzle_json's _layout_regions / standard order).

def window(B, r0, c0, n)​

source

indices of the n x n cells with top-left corner (r0, c0); None if one is missing

def grid_units(B, r0, c0, box=None)​

source

houses of a grid at (r0, c0): its D rows (top to bottom), D columns (left to right) and, for a box_grid with box [bh, bw], its D boxes (reading order of their top-left cells). None if a cell of the D x D area is missing.

def grid_area(B, r0, c0)​

source

set of the cell indices in the D x D square with top-left corner (r0, c0)

def read_houses(rules, B)​

source

the house groups a rule list states, in engine order -> (houses [(kind, cells, extra)], areas [cell sets]) (houses-and-regions, 6.1): grid / box_grid / box / row / column rules in file order, a house equal to an earlier one skipped; areas = the grid / box_grid areas.

def battenburg_table(D, marked)​

source

allowed 2x2 digit tuples (TL, TR, BL, BR) of a battenburg window, marked or unmarked

def vudoku_table(D)​

source

allowed (x, y, z) triples: x is y + z or |y - z|

def product_pairs(D, p)​

source

digit pairs [u, v] in 1..D with u * v == p

pencilmark.exchange.validate​

src/pencilmark/exchange/validate.py

Strict reading checks (Puzzle format section 4): validate(doc) raises FormatError.

NameWhat it does
def validatestrict reading (Puzzle format section 4) of a format_version 1 dict; raises FormatError.
def area_cellsan area (Puzzle format 5): the origin of a grid / box_grid rule -> that grid's D x D cells…
def resolve_areasrules with every "area" replaced by its cell list ("cells"), as the engine expansion reads them

def validate(doc)​

source

strict reading (Puzzle format section 4) of a format_version 1 dict; raises FormatError. Returns the Board.

def area_cells(B, a)​

source

an area (Puzzle format 5): the origin of a grid / box_grid rule -> that grid's D x D cells in reading order, or a list of cell names -> those cells; as engine cell ids

def resolve_areas(B, rules)​

source

rules with every "area" replaced by its cell list ("cells"), as the engine expansion reads them

pencilmark.exchange.read​

src/pencilmark/exchange/read.py

Reading: a sudoku-puzzle dict -> puzzle JSON / engine Puzzle (to_internal, read).

NameWhat it does
def to_internalsudoku-puzzle (format_version 1) -> puzzle JSON (pencilmark.puzzle_json) that builds the engine Puzzle.
def readsudoku-puzzle dict -> engine Puzzle (not finalized)

def to_internal(doc)​

source

sudoku-puzzle (format_version 1) -> puzzle JSON (pencilmark.puzzle_json) that builds the engine Puzzle.

def read(doc)​

source

sudoku-puzzle dict -> engine Puzzle (not finalized)

pencilmark.exchange.write​

src/pencilmark/exchange/write.py

Writing: an engine Puzzle -> a canonical sudoku-puzzle dict (write).

NameWhat it does
def writeengine Puzzle -> sudoku-puzzle dict (format_version 1).

def write(p, meta=None, solution=None, provenance=False)​

source

engine Puzzle -> sudoku-puzzle dict (format_version 1). Raises NotSupportedError for engine structures the format cannot express. provenance=True returns (doc, prov): prov[i] = the engine objects rule i was written from ({"groups", "relations", "constraints", "clues"}: indices into p.groups / p.relations / p.constraints / p.cell_clues).

pencilmark.exchange.canonical​

src/pencilmark/exchange/canonical.py

The canonical text and the content fingerprint (Puzzle format 8).

NameWhat it does
def canonicalkey order of Puzzle format 8.1 (content unchanged)
def dumpscanonical JSON text: fixed key order; pretty: givens / solution one grid row per line, one rule per line.
def jcsRFC 8785 JSON canonicalization for the values the fingerprint covers (integers, strings, lists, objects with…
def semantic_subsetthe fingerprinted content: board, digits, givens, candidates and rules, with the houses replaced by the…
def fingerprint_of_docSHA-256 of the JCS form of the semantic subset of an already canonical sudoku-puzzle dict

def canonical(doc)​

source

key order of Puzzle format 8.1 (content unchanged)

def dumps(doc, pretty=True)​

source

canonical JSON text: fixed key order; pretty: givens / solution one grid row per line, one rule per line.

def jcs(x)​

source

RFC 8785 JSON canonicalization for the values the fingerprint covers (integers, strings, lists, objects with ASCII keys, booleans, null): sorted keys, no whitespace, UTF-8.

def semantic_subset(doc)​

source

the fingerprinted content: board, digits, givens, candidates and rules, with the houses replaced by the houses they state (grid, box_grid, box / row / column rules), so every spelling of the same houses gives the same result, and set-like cell lists sorted

def fingerprint_of_doc(doc)​

source

SHA-256 of the JCS form of the semantic subset of an already canonical sudoku-puzzle dict

pencilmark.rules​

src/pencilmark/rules/init.py

Rule families: what the public format knows about each family, in one place (step 5 of the restructure, docs/research/rule_families_plan.md). Readers, writers, words, drawing and the generator consult these tables instead of their own lists; the C++ engine keeps its own table (csrc/nary.hpp), checked against the engine kinds in model.py.

line the line kinds (rules/line.py)
edge the edge kinds (rules/edge.py)
cell the cell kinds (rules/cell.py)

pencilmark.rules.line​

src/pencilmark/rules/line.py

The line rule family (lines-and-arrows): every public kind with its parameters, whether it can be closed, and its words (the variant name and the one-sentence rule describe() writes). Parameters are REQUIRED when digits != 9 (on 9 x 9 each has a default the reader fills in).

NameWhat it does
class LineKind
def line_to_enginea public line rule -> the engine's rules (exchange's reader): a line constraint, or for double_arrow an…
def line_from_enginean engine line constraint -> public rules (exchange's writer); a kropki chain becomes its kropki edges

class LineKind​

source

No docstring.

def line_to_engine(r, D, B, pairs_rule)​

source

a public line rule -> the engine's rules (exchange's reader): a line constraint, or for double_arrow an equal sum

def line_from_engine(c, B, D)​

source

an engine line constraint -> public rules (exchange's writer); a kropki chain becomes its kropki edges

pencilmark.rules.edge​

src/pencilmark/rules/edge.py

The edge rule family (edges-and-corners): every public kind with its one parameter, its words (the variant name, the one-sentence rule describe() writes, the noun a card note uses) and how it is drawn; and the translation between an edge clue and the engine's pair relation, both ways, with the "all clues shown" negatives.

NameWhat it does
class EdgeKind
def edge_value_oka numbered edge clue (difference / sum / product / ratio) some pair of different digits 1..D satisfies
def edge_relation
def edge_family(family, value) a pair relation is written as when it is an edge clue, or None
def edge_rule
def negative_relation-> list of (kind, args) for an uncovered pair, in expansion order

class EdgeKind​

source

No docstring.

def edge_value_ok(kind, value, D)​

source

a numbered edge clue (difference / sum / product / ratio) some pair of different digits 1..D satisfies

def edge_relation(rule, D)​

source

No docstring.

def edge_family(kind, args, D)​

source

(family, value) a pair relation is written as when it is an edge clue, or None

def edge_rule(family, value, a, b, B)​

source

No docstring.

def negative_relation(of, sel, D)​

source

-> list of (kind, args) for an uncovered pair, in expansion order

pencilmark.rules.cell​

src/pencilmark/rules/cell.py

The cell rule family (cell-clues): every public kind with its parameter, the variant it belongs to (odd and even make one variant, low and high another), whether the "every cell of the kind is marked" negative is defined, and how it is drawn.

NameWhat it does
class CellKind

class CellKind​

source

No docstring.

pencilmark.puzzle_json​

src/pencilmark/puzzle_json.py

Puzzle <-> JSON: the storage / exchange format for any sudoku the engine supports.

Minimal (classic, any standard size; boxes implied): {"numbers": "..3.2.6..9..3.5..1..18.64...."} # '.' or '0' blank, 1-9 then A-Z above 9 Full form (every field optional except what the puzzle needs): { "digits": 9, # D: digits 1..D "shape": ["#########.....", ...], # cells on the bounding grid: '#' cell, '.' void # (omit for a full D x D board) "box": "3x3", # implied rows / columns / boxes of a D x D board "layout": "samurai", # implied regions of a named multi-grid layout "numbers": "...", # givens, row-major over the bounding grid ('#' = void) "solution": "...", "regions": [{"kind": "row|col|box|region|diag|window|cage|extra", "cells": ["r1c1", ...], "sum": 15, "unique": true}, ...], # explicit no-repeat groups (when not implied) "rules": [{"type": "kropki", "color": "white", "cells": ["r1c1", "r1c2"]}, {"type": "line", "kind": "thermo", "cells": [...]}, # any engine line kind, bulb first {"type": "antiknight"}, {"type": "antiking"}, {"type": "nonconsecutive"}, {"type": "frame", "side": "top", "index": 3, "sum": 12}, # first 3 cells from that edge {"type": "rossini", "arrows": [{"side": "left", "index": 2, "towards": "in"}], "all_given": true}, {"type": "vudoku", "cells": ["r2c2", "r1c2", "r2c3"]}, # vertex = sum or difference of the arms {"type": "lowhigh", "high": ["r1c1", ...], "split": 5}, # marked 6..9, the rest 1..5 {"type": "parity", "parity": "odd", "cells": [...]}, # shaded odd / even cells (graphical) {"type": "digit_class", "digits": "123", "cells": [...]}, # cells marked as holding one of these digits {"type": "exclude", "digit": 5, "cells": [...]}, # a circle: its digit is in none of these cells {"type": "increasing", "cells": [...]}, (Bold X / Snowflake / Argyle / Asterisk ...: their lines or cells as extra "regions") {"type": "pairs", "rel": "notdiff", "params": [1], "cells": [["r1c1", "r1c2"], ...]}, {"type": "pairs", "rel": "notsum", "params": [10], "where": "orthogonal", "except": [["r1c1", "r1c2"]]}, # whole-grid pair rule: every orthogonal / diagonal / king / knight # neighbour pair except those listed (e.g. "no X where not marked") {"type": "<engine constraint>", ...}, ...], # arrow, sum, little_killer, sandwich ... "candidates": {"r1c1": "137"}, # given pencil marks (Sukaku); shaded clues are rules above "meta": {"title": "...", "author": "...", "rules_text": "...", "source": "...", "id": "..."}} # optional metadata, never used for solving (top level) Cells are 'r{row}c{col}', 1-based on the bounding grid. Regions are the no-repeat groups (for each sub-grid its rows, columns and boxes); boxes are what a renderer draws with thick lines. Rows / columns / boxes of a D x D board (or a named layout) are implied unless "regions" overrides them. from_json(obj or str) -> Puzzle (not finalized) to_json(Puzzle) -> dict canonical_hash(obj) -> md5

NameWhat it does
def from_jsonpuzzle JSON (dict or str) -> Puzzle, not finalized; ValueError for unknown keys or an unknown layout
def entropy_table2x2 window (TL, TR, BL, BR) with a low, a middle and a high digit, classes = thirds of 1..D (sudoku.coach's…
def quadruple_tableevery n-digit tuple over 1..D that holds each digit of need at least as often as need does
def add_namedapply a named rule (cells as engine indices or "r1c1" names) to an engine Puzzle and remember it (for…
def to_jsonPuzzle -> puzzle JSON dict; solution and meta are written when given
def canonical_hashmd5 of the canonical puzzle definition (sorted keys, no metadata) -- the same puzzle from two sources dedupes.

def from_json(obj) -> M.Puzzle​

source

puzzle JSON (dict or str) -> Puzzle, not finalized; ValueError for unknown keys or an unknown layout

def entropy_table(D)​

source

2x2 window (TL, TR, BL, BR) with a low, a middle and a high digit, classes = thirds of 1..D (sudoku.coach's rule).

def quadruple_table(D, n, need)​

source

every n-digit tuple over 1..D that holds each digit of need at least as often as need does

def add_named(p, rule)​

source

apply a named rule (cells as engine indices or "r1c1" names) to an engine Puzzle and remember it (for importers that build Puzzles directly).

def to_json(p: M.Puzzle, solution=None, meta=None) -> dict​

source

Puzzle -> puzzle JSON dict; solution and meta are written when given

def canonical_hash(obj) -> str​

source

md5 of the canonical puzzle definition (sorted keys, no metadata) -- the same puzzle from two sources dedupes.

pencilmark.interop​

src/pencilmark/interop.py

Interop strings and ipuz export (Other formats).

from_string(s) -> sudoku-puzzle dict plain classic strings (also as text grids with | - + separators, or with a

trailing comment), pencil-mark grids, 729-character candidate strings, HoDoKu
library lines, and
sudokuwiki.org packed strings (S9B, S6B, X9B, W9B, C9B, J9B, L9B, M9B, L6C)
to_string(puzzle, "plain" | "grid" | "sudokuwiki") the reverse (refuses what the target cannot express)
to_ipuz(puzzle) -> dict ipuz 2 for classic / Latin square / diagonal / windoku / jigsaw / killer
from_ipuz(ipuz) -> sudoku-puzzle dict the reverse (boxes, zones, cages); a to_ipuz file returns its embedded puzzle

The sudokuwiki encodings are checked against sudokuwiki.org's solver; the decoder reads exactly what the encoder writes and refuses anything else (placed digits in plain forms, candidate masks, 6x6 killer operators other than '+').

NameWhat it does
def from_stringplain classic string, text grid, pencil-mark grid, 729-character candidate string or sudokuwiki packed…
def to_stringengine Puzzle / any accepted input -> plain classic string or sudokuwiki packed string
def is_classic9a classic 9 x 9 sudoku: standard rows, columns and boxes, nothing else (what Sudoku Explainer rates)
def analyse-> (kind or None, info) for the sudokuwiki forms
def to_ipuzsudoku-puzzle (or any accepted input) -> ipuz 2 dict, for classic / Latin square / diagonal / windoku /…
def from_ipuzipuz sudoku (dict or JSON text) -> sudoku-puzzle dict.

def from_string(s, symbols=None)​

source

plain classic string, text grid, pencil-mark grid, 729-character candidate string or sudokuwiki packed string -> sudoku-puzzle dict (Other formats)

def to_string(puzzle, form='plain')​

source

engine Puzzle / any accepted input -> plain classic string or sudokuwiki packed string

def is_classic9(p)​

source

a classic 9 x 9 sudoku: standard rows, columns and boxes, nothing else (what Sudoku Explainer rates)

def analyse(p)​

source

-> (kind or None, info) for the sudokuwiki forms

def to_ipuz(puzzle)​

source

sudoku-puzzle (or any accepted input) -> ipuz 2 dict, for classic / Latin square / diagonal / windoku / jigsaw / killer and their combinations; refuses everything else with NotSupportedError.

def from_ipuz(ipuz)​

source

ipuz sudoku (dict or JSON text) -> sudoku-puzzle dict. Reads puzzle, solution, boxes, zones (jigsaw boxes, diagonals, windows, other regions) and cages; a file written by to_ipuz gives back its embedded puzzle unless the grid was edited. Refuses kinds other than sudoku and cells it cannot read.

pencilmark.spec_format​

src/pencilmark/spec_format.py

The "sudoku-spec" generator request (Generator spec): a puzzle file with blanks.

validate_spec(doc) strict reading of the spec language (unknown keys refused)

spec_to_gen(doc) -> dict the part pencilmark.gen can run, as a Spec dict; anything else -> NotSupportedError
spec_from_gen(spec) -> dict pencilmark.gen Spec (or dict) -> sudoku-spec
gen_support(doc) -> (bool, reason) can pencilmark.gen run it today?

What pencilmark.gen runs today: gap-free D x D boards (4, 6, 8, 9, 10, 12, 16) with the standard box_grid, or a named layout of 9 x 9 grids (one box_grid per grid); locked rules of different digits; templates for cages, thermo / whisper / renban / between lines, arrows, kropki / XV, parity cells, with counts (incl. "all", "min", "tile", "max") and shape limits (size, sum, length); givens count and per grid; one symmetry of the givens; where: band, difficulty range or "max" / "min", contains (codes), elements max; limits: time, seed. Everything else in a spec is refused with the reason, never ignored.

NameWhat it does
def validate_specstrict reading of a sudoku-spec (Generator spec); locked rules are checked like puzzle rules
def spec_to_gensudoku-spec -> pencilmark.gen Spec dict; NotSupportedError naming the first part the generator cannot run
def gen_support(True, "") when pencilmark.gen can run the spec today, else (False, reason)
def spec_from_genpencilmark.gen Spec (or its dict) -> sudoku-spec dict
def spec_fingerprint

def validate_spec(doc)​

source

strict reading of a sudoku-spec (Generator spec); locked rules are checked like puzzle rules

def spec_to_gen(doc)​

source

sudoku-spec -> pencilmark.gen Spec dict; NotSupportedError naming the first part the generator cannot run

def gen_support(doc)​

source

(True, "") when pencilmark.gen can run the spec today, else (False, reason)

def spec_from_gen(spec)​

source

pencilmark.gen Spec (or its dict) -> sudoku-spec dict

def spec_fingerprint(doc)​

source

No docstring.

pencilmark.variants​

src/pencilmark/variants.py

Variant registry: names, rule text and recognition (variant-naming). Not part of the file format: variant names appear only in meta.type and in describe() output.

describe(puzzle) -> {"type": "Killer Sudoku", "rules": {"Sudoku": "...", "Killer": "...", ...}}

rules_text(described) -> "Sudoku: ...\nKiller: ..."

Two kinds of entries: @variant(name, aliases=..., text=...) a PLACEMENT variant: fn(g) -> the rules it adds to ONE grid area g, or None where the variant is not defined for that area (size / box shape). Works for any size: windoku(g) on a 16x16 grid places its 9 windows. @family(name, match=..., text=...) a RULE-FAMILY variant (killer, thermo, kropki, ...): recognised by rule type / kind; text(rules, ctx) writes its sentence. Recognition: for each grid area, every placement variant's rules are compared with the puzzle's rules as sets of (type, kind, cell set) -- region kinds are drawing hints and are ignored; if all are present the variant applies and those rules are explained. Remaining rules get their family names; anything unknown falls back to a generic name. Generator presets: the placement functions are also the presets a generator can use (preset_rules(name, size)); the current generator (pencilmark.gen) places only its own element kinds, so preset generation of placement variants is a next step.

NameWhat it does
class Gridone grid area (a grid / box_grid, or the whole board): n x n cells, box bh x bw (None for a jigsaw / Latin…
class Varianta registered variant: name, aliases and rule text, with a placement function or a family matcher
def variantdecorator: register fn as the placement variant name
def familyregister a family variant (no placement function) with its matcher, rule text and aliases
def regionregion rule dict of the given kind over cells
def x_sudokuthe two main diagonals as diagonal regions
def windokuthe windoku windows as window regions; None without boxes
def argylethe eight argyle diagonals as regions; 9x9 only, else None
def asteriskthe nine asterisk cells as one region; 9x9 only, else None
def centre_dotthe centre cell of every box as one region; None unless the boxes have odd sides
def girandolathe nine girandola cells as one region; 9x9 only, else None
def disjointone region per position inside a box (the cells at that position in every box); None without boxes
def layout_namename of a multi-grid layout from its subgrid origins (0-based), or None
def size_wordtitle word for a grid size: Tiny (4), Mini (6), nothing (9), else DxD
def describevariant name and rules (an ordered key -> sentence map, like Logic Masters rule headers) from the rules alone.
def grid_namesposition names of the grids of a multi-grid board ("top-left", "centre", …), or "grid at r7c7" where…
def rules_text'Key: sentence' lines (used only to fill an empty meta.rules_text)
def preset_rulesthe rules a placement variant adds to a size x size board with standard boxes (a generator preset); None…

class Grid​

source

one grid area (a grid / box_grid, or the whole board): n x n cells, box bh x bw (None for a jigsaw / Latin square). Local coordinates are 0-based.

def cell(self, r, c)​

source

name of the cell at local (r, c)

def block(self, r, c, h, w)​

source

names of the h x w cells with top-left corner (r, c), row by row

def cells_set(self)​

source

names of all n x n cells, row by row

def diagonal(self)​

source

names of the cells on the main diagonal

def antidiagonal(self)​

source

names of the cells on the diagonal from top right to bottom left

def boxes(self)​

source

top-left (r, c) of every box; empty for a jigsaw or Latin square

def window_starts(self)​

source

top-left corners of the windoku windows: box-sized windows one cell in from each box border, spaced one cell apart (9x9: r2c2, r2c6, r6c2, r6c6)

def line(self, r, c, dr, dc, length)​

source

names of length cells from (r, c) stepping (dr, dc)

class Variant​

source

a registered variant: name, aliases and rule text, with a placement function or a family matcher

def variant(name, aliases=(), text='')​

source

decorator: register fn as the placement variant name

def family(name, match, text, aliases=())​

source

register a family variant (no placement function) with its matcher, rule text and aliases

def region(cells, kind='extra')​

source

region rule dict of the given kind over cells

def x_sudoku(g)​

source

the two main diagonals as diagonal regions

def windoku(g)​

source

the windoku windows as window regions; None without boxes

def argyle(g)​

source

the eight argyle diagonals as regions; 9x9 only, else None

def asterisk(g)​

source

the nine asterisk cells as one region; 9x9 only, else None

def centre_dot(g)​

source

the centre cell of every box as one region; None unless the boxes have odd sides

def girandola(g)​

source

the nine girandola cells as one region; 9x9 only, else None

def disjoint(g)​

source

one region per position inside a box (the cells at that position in every box); None without boxes

def layout_name(subgrids, extra=0)​

source

name of a multi-grid layout from its subgrid origins (0-based), or None

def size_word(D)​

source

title word for a grid size: Tiny (4), Mini (6), nothing (9), else DxD

def describe(puzzle)​

source

variant name and rules (an ordered key -> sentence map, like Logic Masters rule headers) from the rules alone.

def grid_names(grids)​

source

position names of the grids of a multi-grid board ("top-left", "centre", …), or "grid at r7c7" where positions would be ambiguous

def rules_text(described)​

source

'Key: sentence' lines (used only to fill an empty meta.rules_text)

def preset_rules(name, size=9, box=None)​

source

the rules a placement variant adds to a size x size board with standard boxes (a generator preset); None where the variant is not defined for that size