API reference¶
Top level¶
- dedoku.solve(puzzle, *, method='logic', assume_unique=True)[source]¶
Solve a puzzle string in one call.
Convenience wrapper around
GridandSudokuSolver: parses the 81-character puzzle, solves it with the chosen method, and returns the outcome with the final board inresult.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 (seeresult.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.gridholds the final board andresult.stepsthe full explainable solving path.- Return type:
- Raises:
ValueError – If
methodis 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-9are givens, while0and.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:
- 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:
- Raises:
ValueError – If either index is outside the 0-8 range.
- property subgrids: tuple[Subgrid, ...]¶
tuple[Subgrid, …]: The nine subgrids, left to right, top to bottom.
- is_solved()[source]¶
Check whether every cell holds a value and the board is valid.
- Returns:
Trueif the puzzle is completely and correctly solved.- Return type:
bool
- 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
Gridwhile the board is being built and must not be called again afterwards.
- 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
Nonewhile 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
othershares 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:
Trueif the two cells share a row, column, or subgrid.- Return type:
bool
- set_value(digit)[source]¶
Solve the cell with
digitand 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
digitis not a digit between 1 and 9, or the cell is already solved.ContradictionError – If
digitis 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
digitfrom 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:
Trueif the candidate was present and got removed.- Return type:
bool
- Raises:
ValueError – If
digitis 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
indexis outside the 0-8 range.- Variables:
kind – Lower-case human-readable name of the unit kind (
"row","column", or"subgrid").
- register(cell)[source]¶
Add
cellto 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, …]
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 whentechniquesis 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 withrequires_unique_solutionare left out.- Returns:
Fresh instances of the selected techniques.
- Return type:
tuple[Technique, …]
- solve(grid)[source]¶
Solve
gridin 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=Falseand the grid holds the partial progress — unless the solver was built withbacktracking_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:
- 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:
- Parameters:
- property used_backtracking: bool¶
bool: Whether brute force contributed to this result.
Trueonly 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
descriptionuses one-basedRxCylabels. Any bare(row, column, digit)tuples passed to the constructor are normalised toPlacement/Eliminationinstances.- 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_solution –
Truefor uniqueness-based techniques, whose deductions are only sound when the puzzle has exactly one solution. The default pipeline can exclude them viaSudokuSolver(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
Noneif 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
gridby 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:
Truewhen a completion was found and applied,Falsewhen 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.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.