API reference

Top level

dedoku.solve(puzzle, *, method='logic', assume_unique=True)[source]

Solve a puzzle string in one call.

Convenience wrapper around Grid and SudokuSolver: parses the 81-character puzzle, solves it with the chosen method, and returns the outcome with the final board in result.grid.

Parameters:
  • puzzle (str) – The puzzle description accepted by Grid.from_string().

  • method (str) – How to solve. "logic" (default) uses the explainable techniques only and may stall on the hardest puzzles; "hybrid" runs the techniques first and completes any remainder by brute force; "backtracking" skips the techniques and brute-forces directly. Non-default methods record their brute-force part as an explicit "Backtracking" step (see result.used_backtracking).

  • assume_unique (bool) – When False, uniqueness-based techniques are excluded, keeping every deduction sound even if the puzzle might have multiple solutions. Recommended together with "hybrid" when the puzzle is not guaranteed to have one solution.

Returns:

The session outcome; result.grid holds the final board and result.steps the full explainable solving path.

Return type:

SolveResult

Raises:
  • ValueError – If method is not one of the three modes.

  • InvalidGridError – If the puzzle description is malformed or its givens contradict each other.

  • ContradictionError – If the puzzle has no solution (detected by logic, or proven exhaustively by the non-default methods).

The board

class dedoku.Grid[source]

A 9x9 Sudoku board made of cells, rows, columns, and subgrids.

A freshly constructed grid is empty: every cell is unsolved with all nine candidates. Use from_string() to load a puzzle.

classmethod from_string(text)[source]

Build a grid from an 81-character puzzle description.

Cells are read left to right, top to bottom. Digits 1-9 are givens, while 0 and . mark empty cells. Whitespace and the decoration characters |, +, - are ignored, so the output of __str__() can be parsed back.

Parameters:

text (str) – The puzzle description.

Returns:

A grid with all givens placed and candidates propagated.

Return type:

Grid

Raises:

InvalidGridError – If the description does not contain exactly 81 cells, uses unexpected characters, or its givens contradict each other.

static subgrid_index(row_index, column_index)[source]

Return the subgrid index covering the given board position.

Parameters:
  • row_index (int) – Zero-based row index (0-8).

  • column_index (int) – Zero-based column index (0-8).

Returns:

The zero-based subgrid index (0-8).

Return type:

int

cell(row_index, column_index)[source]

Return the cell at the given board position.

Parameters:
  • row_index (int) – Zero-based row index (0-8).

  • column_index (int) – Zero-based column index (0-8).

Returns:

The requested cell.

Return type:

Cell

Raises:

ValueError – If either index is outside the 0-8 range.

property cells: tuple[Cell, ...]

tuple[Cell, …]: All 81 cells, left to right, top to bottom.

property rows: tuple[Row, ...]

tuple[Row, …]: The nine rows, top to bottom.

property columns: tuple[Column, ...]

tuple[Column, …]: The nine columns, left to right.

property subgrids: tuple[Subgrid, ...]

tuple[Subgrid, …]: The nine subgrids, left to right, top to bottom.

property units: tuple[Unit, ...]

tuple[Unit, …]: All 27 houses: rows, then columns, then subgrids.

is_solved()[source]

Check whether every cell holds a value and the board is valid.

Returns:

True if the puzzle is completely and correctly solved.

Return type:

bool

is_valid()[source]

Check that no house contains a duplicated solved digit.

Returns:

True if every row, column, and subgrid is valid.

Return type:

bool

to_string(empty='.')[source]

Serialise the board to an 81-character single-line string.

Parameters:

empty (str) – The character used for unsolved cells.

Returns:

The board, left to right, top to bottom.

Return type:

str

class dedoku.Cell(row_index, column_index)[source]

A single cell of a 9x9 Sudoku grid.

A cell starts unsolved with all nine digits as candidates. Placing a value with set_value() automatically removes that digit from the candidates of every peer (the cells sharing a row, column, or subgrid).

Parameters:
  • row_index (int) – Zero-based index of the row the cell lies in (0-8).

  • column_index (int) – Zero-based index of the column the cell lies in (0-8).

Raises:

ValueError – If either index is outside the 0-8 range.

attach(row, column, subgrid)[source]

Wire the cell to its three houses and register it with each.

This is called once by Grid while the board is being built and must not be called again afterwards.

Parameters:
  • row (Row) – The row the cell belongs to.

  • column (Column) – The column the cell belongs to.

  • subgrid (Subgrid) – The 3x3 subgrid the cell belongs to.

Raises:

RuntimeError – If the cell is already attached.

Return type:

None

property row_index: int

int: Zero-based index of the cell’s row.

property column_index: int

int: Zero-based index of the cell’s column.

property position: tuple[int, int]

tuple[int, int]: The (row_index, column_index) pair.

property label: str

str: Human-readable, one-based position label such as R4C7.

property row: Row

Row: The row the cell belongs to.

Raises:

RuntimeError – If the cell has not been attached to a grid.

property column: Column

Column: The column the cell belongs to.

Raises:

RuntimeError – If the cell has not been attached to a grid.

property subgrid: Subgrid

Subgrid: The 3x3 subgrid the cell belongs to.

Raises:

RuntimeError – If the cell has not been attached to a grid.

property value: int | None

int | None: The solved digit, or None while unsolved.

property candidates: frozenset[int]

frozenset[int]: Immutable view of the remaining candidates.

A solved cell exposes an empty set. The view is cached between mutations, so repeated reads are cheap.

property is_solved: bool

bool: Whether the cell holds a definitive value.

property is_given: bool

bool: Whether the value was part of the original puzzle.

Uniqueness-based techniques (such as avoidable rectangles) must distinguish clues supplied by the puzzle from values deduced during solving.

property is_bivalue: bool

bool: Whether the cell is unsolved with exactly two candidates.

Bivalue cells are the anchors of several advanced techniques (remote pairs, W-Wing, Y-Wing, BUG, …).

property peers: tuple[Cell, ...]

tuple[Cell, …]: The 20 distinct cells sharing a house with this one.

Peers are returned sorted by position for deterministic iteration and cached after the first access, since the wiring never changes.

Raises:

RuntimeError – If the cell has not been attached to a grid.

sees(other)[source]

Report whether other shares a house with this cell.

A cell never sees itself. Visibility is purely positional, so this works whether or not the cells are attached to a grid.

Parameters:

other (Cell) – The cell to test against.

Returns:

True if the two cells share a row, column, or subgrid.

Return type:

bool

set_value(digit)[source]

Solve the cell with digit and propagate to all peers.

The digit is removed from the candidates of every peer, which may in turn reveal a contradiction elsewhere on the board.

Parameters:

digit (int) – The digit to place, between 1 and 9.

Raises:
  • ValueError – If digit is not a digit between 1 and 9, or the cell is already solved.

  • ContradictionError – If digit is not among the cell’s candidates, or placing it strips the last candidate from an unsolved peer.

Return type:

None

mark_as_given()[source]

Flag the cell’s value as an original clue of the puzzle.

This is called by dedoku.grid.Grid.from_string() right after each given is placed.

Raises:

ValueError – If the cell has no value yet.

Return type:

None

remove_candidate(digit)[source]

Eliminate digit from the cell’s candidates.

Removing a candidate from a solved cell, or a digit that is already absent, is a harmless no-op.

Parameters:

digit (int) – The digit to eliminate, between 1 and 9.

Returns:

True if the candidate was present and got removed.

Return type:

bool

Raises:
  • ValueError – If digit is not a digit between 1 and 9.

  • ContradictionError – If the removal leaves an unsolved cell with no candidates at all.

dedoku.DIGITS = frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9})

frozenset() -> empty frozenset object frozenset(iterable) -> frozenset object

Build an immutable unordered collection of unique elements.

Houses

class dedoku.Unit(index)[source]

Base class for any house of nine cells.

Parameters:

index (int) – Zero-based index of the unit within its kind (0-8).

Raises:

ValueError – If index is outside the 0-8 range.

Variables:

kind – Lower-case human-readable name of the unit kind ("row", "column", or "subgrid").

register(cell)[source]

Add cell to the unit while the grid is being built.

This is called by dedoku.cell.Cell.attach() and must not be used afterwards.

Parameters:

cell (Cell) – The cell to register.

Raises:

RuntimeError – If the unit already holds nine cells.

Return type:

None

property index: int

int: Zero-based index of the unit within its kind.

property name: str

str: Human-readable, one-based name such as row 3.

property cells: tuple[Cell, ...]

tuple[Cell, …]: The nine cells of the unit, in board order.

The tuple is cached, since the wiring never changes once built.

solved_values()[source]

Return the digits already placed inside the unit.

Returns:

The set of solved digits.

Return type:

frozenset[int]

missing_values()[source]

Return the digits still to be placed inside the unit.

Returns:

The complement of solved_values().

Return type:

frozenset[int]

unsolved_cells()[source]

Return the cells of the unit that have no value yet.

Returns:

The unsolved cells, in board order.

Return type:

tuple[Cell, …]

cells_with_candidate(digit)[source]

Return the unsolved cells that still allow digit.

Parameters:

digit (int) – The digit to look for, between 1 and 9.

Returns:

The matching cells, in board order.

Return type:

tuple[Cell, …]

is_valid()[source]

Check that no digit appears twice among the solved cells.

Returns:

True if every solved digit is unique in the unit.

Return type:

bool

class dedoku.Row(index)[source]

A horizontal line of nine cells.

Parameters:

index (int)

class dedoku.Column(index)[source]

A vertical line of nine cells.

Parameters:

index (int)

class dedoku.Subgrid(index)[source]

A 3x3 box of nine cells.

Subgrids are indexed left to right, top to bottom: subgrid 0 covers rows 0-2 and columns 0-2, subgrid 8 covers rows 6-8 and columns 6-8.

Parameters:

index (int)

The solver

class dedoku.SudokuSolver(techniques=None, *, assume_unique=True, backtracking_fallback=False)[source]

Logic-only Sudoku solver (no backtracking, no guessing).

The solver owns an ordered pipeline of techniques. On every iteration it applies the first technique that produces a deduction, then starts over from the top of the pipeline, so harder strategies only run when the simpler ones are exhausted.

Parameters:
  • techniques (Sequence[Technique] | None) – Custom technique pipeline, tried in the given order. When omitted, default_techniques() is used.

  • assume_unique (bool) – When False, uniqueness-based techniques (unique rectangles, BUG, avoidable rectangles) are excluded from the default pipeline, so solving stays sound on puzzles that may have multiple solutions. Ignored when techniques is given.

  • backtracking_fallback (bool) – When True, a puzzle the logical pipeline cannot finish is completed by brute-force search over the remaining candidates, recorded as an explicit "Backtracking" step. Off by default: logic only.

static default_techniques(*, assume_unique=True)[source]

Build the default pipeline, ordered from simplest to hardest.

Parameters:

assume_unique (bool) – When False, techniques flagged with requires_unique_solution are left out.

Returns:

Fresh instances of the selected techniques.

Return type:

tuple[Technique, …]

property techniques: tuple[Technique, ...]

tuple[Technique, …]: The pipeline used by this solver.

solve(grid)[source]

Solve grid in place as far as pure logic allows.

The grid is mutated: values are placed and candidates eliminated. When the pipeline runs dry before the board is complete, the result reports solved=False and the grid holds the partial progress — unless the solver was built with backtracking_fallback=True, in which case the remainder is completed by brute force and recorded as a "Backtracking" step.

Parameters:

grid (Grid) – The board to solve.

Returns:

The session outcome with the full list of steps.

Return type:

SolveResult

Raises:

dedoku.exceptions.ContradictionError – If the board reaches an impossible state, meaning the puzzle has no solution.

class dedoku.SolveResult(solved, steps=(), grid=None)[source]

Outcome of a solving session.

Variables:
  • solved (bool) – Whether the puzzle was completely solved.

  • steps (tuple[Step, ...]) – Every deduction performed, in order of application.

  • grid (Grid | None) – The board the session worked on, in its final state — solved, or holding the partial progress when the pipeline stalled.

Parameters:
  • solved (bool)

  • steps (tuple[Step, ...])

  • grid (Grid | None)

property used_backtracking: bool

bool: Whether brute force contributed to this result.

True only when the solver ran with the explicit backtracking fallback enabled and the logical techniques were not enough.

property techniques_used: tuple[str, ...]

tuple[str, …]: Distinct technique names, in first-use order.

class dedoku.Step(technique, description, placements=(), eliminations=())[source]

Immutable record of one successful technique application.

Coordinates are zero-based; the human-readable description uses one-based RxCy labels. Any bare (row, column, digit) tuples passed to the constructor are normalised to Placement / Elimination instances.

Variables:
  • technique (str) – Name of the technique that produced the step.

  • description (str) – Human-readable explanation of the deduction.

  • placements (tuple[Placement, ...]) – Values placed on the board by this step.

  • eliminations (tuple[Elimination, ...]) – Candidates removed from cells by this step.

Parameters:
  • technique (str)

  • description (str)

  • placements (tuple[Placement, ...])

  • eliminations (tuple[Elimination, ...])

class dedoku.Placement(row, column, digit)[source]

A digit placed at a board position.

Behaves exactly like the plain (row, column, digit) tuple it replaces — unpacking, indexing, and equality with bare tuples all keep working — while adding named field access.

Variables:
  • row (int) – Zero-based row index (0-8).

  • column (int) – Zero-based column index (0-8).

  • digit (int) – The digit placed, between 1 and 9.

Parameters:
  • row (int)

  • column (int)

  • digit (int)

row: int

Alias for field number 0

column: int

Alias for field number 1

digit: int

Alias for field number 2

class dedoku.Elimination(row, column, digit)[source]

A candidate removed from a board position.

Behaves exactly like the plain (row, column, digit) tuple it replaces — unpacking, indexing, and equality with bare tuples all keep working — while adding named field access.

Variables:
  • row (int) – Zero-based row index (0-8).

  • column (int) – Zero-based column index (0-8).

  • digit (int) – The candidate removed, between 1 and 9.

Parameters:
  • row (int)

  • column (int)

  • digit (int)

row: int

Alias for field number 0

column: int

Alias for field number 1

digit: int

Alias for field number 2

class dedoku.Technique[source]

Abstract base class for a single logical solving strategy.

Concrete techniques must be stateless: all information lives in the grid, so the same instance can be reused across puzzles.

Variables:
  • name – Human-readable technique name shown in solving reports.

  • requires_unique_solutionTrue for uniqueness-based techniques, whose deductions are only sound when the puzzle has exactly one solution. The default pipeline can exclude them via SudokuSolver(assume_unique=False).

abstractmethod apply(grid)[source]

Search the grid and apply the first occurrence of the pattern.

Implementations must mutate the grid only when they return a step, and each call must perform at most one deduction so that the solver can always fall back to the simplest available technique.

Parameters:

grid (Grid) – The board to inspect and mutate.

Returns:

A record of the deduction, or None if the pattern does not occur anywhere on the board.

Return type:

Step | None

Raises:

dedoku.exceptions.ContradictionError – If applying the deduction reveals an inconsistent board state.

Brute force (opt-in)

Opt-in brute-force completion.

Backtracking is not a logical technique, and this module is deliberately kept outside dedoku.techniques: the default pipeline never touches it. It exists for the explicit hybrid and backtracking solving modes, where the caller chooses to trade explainability for a guaranteed answer.

The search runs over the grid’s current candidates, so every candidate already removed by sound logic narrows it — in hybrid mode the brute-force tail is typically tiny.

dedoku.backtracking.complete_with_backtracking(grid)[source]

Complete grid by depth-first search over its candidates.

Solved cells are taken as fixed; unsolved cells only try digits still among their candidates. On success the found values are placed on the grid (with normal propagation); on failure the grid is left untouched.

Parameters:

grid (Grid) – The board to complete in place.

Returns:

True when a completion was found and applied, False when no completion exists for the current board state.

Return type:

bool

Exceptions

Exception hierarchy for the dedoku package.

All exceptions raised by this library derive from SudokuError, so callers can catch a single base class to handle any library failure.

exception dedoku.exceptions.SudokuError[source]

Base class for every exception raised by dedoku.

exception dedoku.exceptions.InvalidGridError[source]

Raised when a puzzle description cannot be turned into a valid grid.

Typical causes are a wrong number of cells, characters outside the accepted alphabet, or givens that immediately contradict each other (for example two identical digits in the same row).

exception dedoku.exceptions.ContradictionError[source]

Raised when the board reaches a logically impossible state.

This happens when an unsolved cell loses its last candidate or a digit has no remaining home inside a unit. During solving it means the puzzle (or the technique that produced the state) is inconsistent.

Command-line module

Command-line interface.

Run dedoku (or python -m dedoku) with an 81-character puzzle to solve it with pure logic. With --explain every deduction is printed in order, showing exactly how each cell was filled and which candidates were eliminated along the way.

Exit codes: 0 solved, 1 not fully solvable by the logical pipeline, 2 invalid input.

dedoku.cli.build_parser()[source]

Build the argument parser for the dedoku command.

Returns:

The configured parser.

Return type:

argparse.ArgumentParser

dedoku.cli.main(argv=None)[source]

Run the command-line interface.

Parameters:

argv (Sequence[str] | None) – Argument list; defaults to sys.argv[1:].

Returns:

The process exit code.

Return type:

int