dedoku 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dedoku-0.3.0/LICENSE +21 -0
- dedoku-0.3.0/PKG-INFO +230 -0
- dedoku-0.3.0/README.md +206 -0
- dedoku-0.3.0/dedoku/__init__.py +38 -0
- dedoku-0.3.0/dedoku/cell.py +306 -0
- dedoku-0.3.0/dedoku/exceptions.py +31 -0
- dedoku-0.3.0/dedoku/grid.py +229 -0
- dedoku-0.3.0/dedoku/solver.py +169 -0
- dedoku-0.3.0/dedoku/techniques/__init__.py +63 -0
- dedoku-0.3.0/dedoku/techniques/aic.py +215 -0
- dedoku-0.3.0/dedoku/techniques/als.py +148 -0
- dedoku-0.3.0/dedoku/techniques/base.py +78 -0
- dedoku-0.3.0/dedoku/techniques/bug.py +73 -0
- dedoku-0.3.0/dedoku/techniques/chains.py +309 -0
- dedoku-0.3.0/dedoku/techniques/chute.py +169 -0
- dedoku-0.3.0/dedoku/techniques/colouring.py +198 -0
- dedoku-0.3.0/dedoku/techniques/fish.py +256 -0
- dedoku-0.3.0/dedoku/techniques/hidden.py +142 -0
- dedoku-0.3.0/dedoku/techniques/intersections.py +111 -0
- dedoku-0.3.0/dedoku/techniques/medusa.py +212 -0
- dedoku-0.3.0/dedoku/techniques/naked.py +128 -0
- dedoku-0.3.0/dedoku/techniques/rectangles.py +232 -0
- dedoku-0.3.0/dedoku/techniques/wings.py +135 -0
- dedoku-0.3.0/dedoku/techniques/wwing.py +81 -0
- dedoku-0.3.0/dedoku/units.py +186 -0
- dedoku-0.3.0/dedoku.egg-info/PKG-INFO +230 -0
- dedoku-0.3.0/dedoku.egg-info/SOURCES.txt +46 -0
- dedoku-0.3.0/dedoku.egg-info/dependency_links.txt +1 -0
- dedoku-0.3.0/dedoku.egg-info/top_level.txt +1 -0
- dedoku-0.3.0/pyproject.toml +35 -0
- dedoku-0.3.0/setup.cfg +4 -0
- dedoku-0.3.0/tests/test_aic.py +47 -0
- dedoku-0.3.0/tests/test_als.py +49 -0
- dedoku-0.3.0/tests/test_avoidable.py +43 -0
- dedoku-0.3.0/tests/test_bug.py +93 -0
- dedoku-0.3.0/tests/test_cell.py +118 -0
- dedoku-0.3.0/tests/test_chains.py +91 -0
- dedoku-0.3.0/tests/test_chute.py +52 -0
- dedoku-0.3.0/tests/test_colouring.py +106 -0
- dedoku-0.3.0/tests/test_fish.py +126 -0
- dedoku-0.3.0/tests/test_grid.py +112 -0
- dedoku-0.3.0/tests/test_intersections.py +60 -0
- dedoku-0.3.0/tests/test_medusa.py +52 -0
- dedoku-0.3.0/tests/test_rectangles.py +98 -0
- dedoku-0.3.0/tests/test_solver.py +98 -0
- dedoku-0.3.0/tests/test_techniques.py +105 -0
- dedoku-0.3.0/tests/test_wings.py +95 -0
- dedoku-0.3.0/tests/test_wwing.py +51 -0
dedoku-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 n36l3c7
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dedoku-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dedoku
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A pure-Python Sudoku solver that uses only human-style logical deduction techniques (no backtracking).
|
|
5
|
+
Author-email: n36l3c7 <gervasio.samu@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/n36l3c7/dedoku
|
|
8
|
+
Keywords: sudoku,solver,puzzle,logic,deduction
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Games/Entertainment :: Puzzle Games
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# Dedoku
|
|
26
|
+
|
|
27
|
+
[](https://www.python.org/)
|
|
28
|
+
[](https://github.com/n36l3c7/dedoku/releases)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
[](pyproject.toml)
|
|
31
|
+
[](tests/)
|
|
32
|
+
[](#solving-philosophy)
|
|
33
|
+
|
|
34
|
+
A **pure-Python** Sudoku solving library that relies exclusively on
|
|
35
|
+
**human-style logical deduction** — 20 named technique families, from naked
|
|
36
|
+
singles to alternating inference chains, and not a single guess.
|
|
37
|
+
|
|
38
|
+
## About the project
|
|
39
|
+
|
|
40
|
+
Most Sudoku solvers brute-force the board: try a digit, propagate, undo on
|
|
41
|
+
contradiction. This library takes the opposite stance. Every digit placed and
|
|
42
|
+
every candidate eliminated is the conclusion of a **named, explainable
|
|
43
|
+
technique**, applied exactly the way a strong human solver would reason. The
|
|
44
|
+
full solving path is recorded step by step, so any solution can be replayed
|
|
45
|
+
and audited.
|
|
46
|
+
|
|
47
|
+
- **Zero external dependencies** — Python 3.10+ standard library only, tests
|
|
48
|
+
included (`unittest`).
|
|
49
|
+
- **Fully typed and documented** — modern type hints and Sphinx-style
|
|
50
|
+
reStructuredText docstrings on every module, class, and method.
|
|
51
|
+
- **Explainable solving** — each `Step` reports the technique, a
|
|
52
|
+
human-readable description, the placements, and the eliminations.
|
|
53
|
+
- **Honestly benchmarked** — a reproducible 500-puzzle benchmark against a
|
|
54
|
+
reference backtracking solver ships with the repo ([below](#benchmark)).
|
|
55
|
+
|
|
56
|
+
### Solving philosophy
|
|
57
|
+
|
|
58
|
+
**No backtracking. No guessing. Ever.** If the pipeline of logical techniques
|
|
59
|
+
cannot finish a puzzle, the solver stops and says so — it never falls back to
|
|
60
|
+
trial and error. On the benchmark's hardest tier this happens on 11 puzzles
|
|
61
|
+
out of 100; everything below that tier is solved outright.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git clone https://github.com/n36l3c7/dedoku.git
|
|
67
|
+
cd dedoku
|
|
68
|
+
pip install -e .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
No dependencies are installed — the package is the code you cloned.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from dedoku import Grid, SudokuSolver
|
|
77
|
+
|
|
78
|
+
puzzle = (
|
|
79
|
+
"530070000"
|
|
80
|
+
"600195000"
|
|
81
|
+
"098000060"
|
|
82
|
+
"800060003"
|
|
83
|
+
"400803001"
|
|
84
|
+
"700020006"
|
|
85
|
+
"060000280"
|
|
86
|
+
"000419005"
|
|
87
|
+
"000080079"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
grid = Grid.from_string(puzzle)
|
|
91
|
+
result = SudokuSolver().solve(grid)
|
|
92
|
+
|
|
93
|
+
print(result.solved) # True
|
|
94
|
+
print(grid) # pretty-printed solved board
|
|
95
|
+
print(grid.to_string()) # 81-character string
|
|
96
|
+
|
|
97
|
+
for step in result.steps: # the full, explainable solving path
|
|
98
|
+
print(f"[{step.technique}] {step.description}")
|
|
99
|
+
print(result.techniques_used) # distinct techniques, in first-use order
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Puzzle strings are 81 characters, read left to right, top to bottom: digits
|
|
103
|
+
`1`–`9` are givens, `0` or `.` mark empty cells; whitespace and the decoration
|
|
104
|
+
characters `|`, `-`, `+` are ignored, so pretty-printed boards parse back.
|
|
105
|
+
|
|
106
|
+
Need a custom pipeline? Pass any sequence of techniques, tried in order:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from dedoku import SudokuSolver
|
|
110
|
+
from dedoku.techniques import HiddenSingle, NakedSingle, XYChain
|
|
111
|
+
|
|
112
|
+
solver = SudokuSolver(techniques=[NakedSingle(), HiddenSingle(), XYChain()])
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The board model is fully navigable if you want to build your own techniques:
|
|
116
|
+
each `Cell` knows its `row`, `column`, `subgrid`, `candidates`, and `peers`;
|
|
117
|
+
the `Grid` exposes all 27 houses via `rows`, `columns`, `subgrids`, `units`.
|
|
118
|
+
|
|
119
|
+
## Implemented techniques
|
|
120
|
+
|
|
121
|
+
The default pipeline applies 28 technique instances from 20 families, always
|
|
122
|
+
restarting from the simplest after every deduction.
|
|
123
|
+
|
|
124
|
+
| # | Family | Classes | Module |
|
|
125
|
+
|---|--------|---------|--------|
|
|
126
|
+
| 1 | Naked Candidates | `NakedSingle`, `NakedPair`, `NakedTriple`, `NakedQuad` | [naked.py](dedoku/techniques/naked.py) |
|
|
127
|
+
| 2 | Hidden Candidates | `HiddenSingle`, `HiddenPair`, `HiddenTriple`, `HiddenQuad` | [hidden.py](dedoku/techniques/hidden.py) |
|
|
128
|
+
| 3 | Intersection Removal | `PointingCandidates`, `ClaimingCandidates` | [intersections.py](dedoku/techniques/intersections.py) |
|
|
129
|
+
| 4 | X-Wing | `XWing` | [fish.py](dedoku/techniques/fish.py) |
|
|
130
|
+
| 5 | Chute Remote Pairs | `ChuteRemotePairs` | [chute.py](dedoku/techniques/chute.py) |
|
|
131
|
+
| 6 | Simple Colouring | `SimpleColouring` | [colouring.py](dedoku/techniques/colouring.py) |
|
|
132
|
+
| 7 | W-Wing | `WWing` | [wwing.py](dedoku/techniques/wwing.py) |
|
|
133
|
+
| 8 | Y-Wing (XY-Wing) | `YWing` | [wings.py](dedoku/techniques/wings.py) |
|
|
134
|
+
| 9 | Unique Rectangles | `UniqueRectangle` (Type 1) | [rectangles.py](dedoku/techniques/rectangles.py) |
|
|
135
|
+
| 10 | Swordfish | `Swordfish` | [fish.py](dedoku/techniques/fish.py) |
|
|
136
|
+
| 11 | XYZ-Wing | `XYZWing` | [wings.py](dedoku/techniques/wings.py) |
|
|
137
|
+
| 12 | BUG (Bivalue Universal Grave) | `BivalueUniversalGrave` | [bug.py](dedoku/techniques/bug.py) |
|
|
138
|
+
| 13 | Avoidable Rectangles | `AvoidableRectangle` | [rectangles.py](dedoku/techniques/rectangles.py) |
|
|
139
|
+
| 14 | Unique Rectangles Type 2 | `UniqueRectangleType2` | [rectangles.py](dedoku/techniques/rectangles.py) |
|
|
140
|
+
| 15 | Finned Fish | `FinnedXWing`, `FinnedSwordfish` | [fish.py](dedoku/techniques/fish.py) |
|
|
141
|
+
| 16 | X-Chain (basic X-Cycles) | `XChain` | [chains.py](dedoku/techniques/chains.py) |
|
|
142
|
+
| 17 | XY-Chain | `XYChain` | [chains.py](dedoku/techniques/chains.py) |
|
|
143
|
+
| 18 | 3D Medusa | `Medusa3D` | [medusa.py](dedoku/techniques/medusa.py) |
|
|
144
|
+
| 19 | ALS-XZ (Almost Locked Sets) | `AlsXz` | [als.py](dedoku/techniques/als.py) |
|
|
145
|
+
| 20 | AIC (Alternating Inference Chains) | `AIC` | [aic.py](dedoku/techniques/aic.py) |
|
|
146
|
+
|
|
147
|
+
Uniqueness-based techniques (9, 12, 13, 14) assume the puzzle has exactly one
|
|
148
|
+
solution — the standard convention for published Sudokus.
|
|
149
|
+
|
|
150
|
+
## Benchmark
|
|
151
|
+
|
|
152
|
+
500 unique-solution puzzles (seed 42), 100 for each of five difficulty
|
|
153
|
+
levels, timed against the **lightest classic backtracking solver** (bitmask,
|
|
154
|
+
sequential cells, no heuristics). Same protocol for both solvers: puzzle
|
|
155
|
+
string in, board out, minimum of 3 runs, parsing included. Python 3.13,
|
|
156
|
+
Windows 11. Levels are graded by the hardest technique the original
|
|
157
|
+
13-family pipeline needs: singles → subsets → intersections → advanced →
|
|
158
|
+
*extreme* (beyond that base pipeline).
|
|
159
|
+
|
|
160
|
+
### Solve-time distribution
|
|
161
|
+
|
|
162
|
+
<picture>
|
|
163
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-distribution-dark.svg">
|
|
164
|
+
<img alt="Solve-time distribution by difficulty level: one dot per puzzle on a logarithmic ms scale, backtracking vs logic library, with medians marked" src="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-distribution-light.svg">
|
|
165
|
+
</picture>
|
|
166
|
+
|
|
167
|
+
| Level | Solved by library | BT median | BT p95 | BT max | Library median | Library p95 | Library max |
|
|
168
|
+
|---|---|---|---|---|---|---|---|
|
|
169
|
+
| 1 · Singles | 100/100 | 15.9 ms | 481 ms | 1,757 ms | **1.1 ms** | 1.6 ms | 1.8 ms |
|
|
170
|
+
| 2 · Subsets | 100/100 | 13.1 ms | 218 ms | 2,172 ms | **1.6 ms** | 2.4 ms | 2.9 ms |
|
|
171
|
+
| 3 · Intersections | 100/100 | 5.0 ms | 126 ms | 2,026 ms | **3.1 ms** | 7.8 ms | 12.1 ms |
|
|
172
|
+
| 4 · Advanced | 100/100 | 15.6 ms | 225 ms | 551 ms | **7.5 ms** | 17.7 ms | 31.2 ms |
|
|
173
|
+
| 5 · Extreme | 89/100 † | **16.7 ms** | 229 ms | 684 ms | 65.4 ms | 306 ms | 720 ms |
|
|
174
|
+
| **Overall** | **489/500** | 12.6 ms | 279 ms | 2,172 ms | **3.0 ms** | 158 ms | 720 ms |
|
|
175
|
+
|
|
176
|
+
† On the 11 extreme puzzles the library cannot crack, its reported time is
|
|
177
|
+
the time to exhaust every technique and stop — never a guess.
|
|
178
|
+
|
|
179
|
+
Key findings:
|
|
180
|
+
|
|
181
|
+
- **The logic library wins on the median at every human-graded level up to
|
|
182
|
+
Advanced** (3.0 ms vs 12.6 ms overall) and is far more predictable: naive
|
|
183
|
+
backtracking's worst case is 2.2 s when the cell order is unlucky, versus
|
|
184
|
+
0.72 s for the library's hardest chain solve.
|
|
185
|
+
- **Brute-force time is uncorrelated with human difficulty** — backtracking
|
|
186
|
+
is fastest on level 3 and slowest on level 1, while the library's times
|
|
187
|
+
grow monotonically with the level. The two solvers measure different
|
|
188
|
+
notions of "hard".
|
|
189
|
+
- **Level 5 is chain territory**: the advanced techniques added in v0.2.0
|
|
190
|
+
raised the library's extreme-tier solve rate from 0/100 to 89/100.
|
|
191
|
+
|
|
192
|
+
### Which techniques crack the extreme tier
|
|
193
|
+
|
|
194
|
+
<picture>
|
|
195
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-techniques-dark.svg">
|
|
196
|
+
<img alt="Number of solved extreme puzzles in which each advanced technique fired, XY-Chain leading with 55 of 89" src="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-techniques-light.svg">
|
|
197
|
+
</picture>
|
|
198
|
+
|
|
199
|
+
### Reproduce it
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
python benchmark/generate_puzzles.py # optional: regenerate the dataset (seeded)
|
|
203
|
+
python benchmark/run_benchmark.py # times both solvers, writes benchmark/results.csv
|
|
204
|
+
python benchmark/make_charts.py # renders the SVG charts into docs/
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Architecture
|
|
208
|
+
|
|
209
|
+
| Class | Responsibility |
|
|
210
|
+
|-------|----------------|
|
|
211
|
+
| `Cell` | A single cell: value, candidates, position, given flag, and references to its row, column, and subgrid. |
|
|
212
|
+
| `Unit` → `Row`, `Column`, `Subgrid` | The 27 houses of nine cells, sharing bookkeeping logic. |
|
|
213
|
+
| `Grid` | The full 9×9 board: wiring, parsing, serialisation, validity. |
|
|
214
|
+
| `Technique` / `Step` | One logical strategy and the immutable record of one applied deduction. |
|
|
215
|
+
| `SudokuSolver` / `SolveResult` | The engine that chains techniques and the outcome of a session. |
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
python -m unittest discover -s tests -v
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
78 tests cover the board model, every technique (positive and negative
|
|
224
|
+
cases), and end-to-end solving. Chain and ALS implementations were
|
|
225
|
+
additionally validated by checking every benchmark solution against the
|
|
226
|
+
backtracking reference — zero mismatches.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
Released under the [MIT License](LICENSE).
|
dedoku-0.3.0/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Dedoku
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](https://github.com/n36l3c7/dedoku/releases)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](pyproject.toml)
|
|
7
|
+
[](tests/)
|
|
8
|
+
[](#solving-philosophy)
|
|
9
|
+
|
|
10
|
+
A **pure-Python** Sudoku solving library that relies exclusively on
|
|
11
|
+
**human-style logical deduction** — 20 named technique families, from naked
|
|
12
|
+
singles to alternating inference chains, and not a single guess.
|
|
13
|
+
|
|
14
|
+
## About the project
|
|
15
|
+
|
|
16
|
+
Most Sudoku solvers brute-force the board: try a digit, propagate, undo on
|
|
17
|
+
contradiction. This library takes the opposite stance. Every digit placed and
|
|
18
|
+
every candidate eliminated is the conclusion of a **named, explainable
|
|
19
|
+
technique**, applied exactly the way a strong human solver would reason. The
|
|
20
|
+
full solving path is recorded step by step, so any solution can be replayed
|
|
21
|
+
and audited.
|
|
22
|
+
|
|
23
|
+
- **Zero external dependencies** — Python 3.10+ standard library only, tests
|
|
24
|
+
included (`unittest`).
|
|
25
|
+
- **Fully typed and documented** — modern type hints and Sphinx-style
|
|
26
|
+
reStructuredText docstrings on every module, class, and method.
|
|
27
|
+
- **Explainable solving** — each `Step` reports the technique, a
|
|
28
|
+
human-readable description, the placements, and the eliminations.
|
|
29
|
+
- **Honestly benchmarked** — a reproducible 500-puzzle benchmark against a
|
|
30
|
+
reference backtracking solver ships with the repo ([below](#benchmark)).
|
|
31
|
+
|
|
32
|
+
### Solving philosophy
|
|
33
|
+
|
|
34
|
+
**No backtracking. No guessing. Ever.** If the pipeline of logical techniques
|
|
35
|
+
cannot finish a puzzle, the solver stops and says so — it never falls back to
|
|
36
|
+
trial and error. On the benchmark's hardest tier this happens on 11 puzzles
|
|
37
|
+
out of 100; everything below that tier is solved outright.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/n36l3c7/dedoku.git
|
|
43
|
+
cd dedoku
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
No dependencies are installed — the package is the code you cloned.
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from dedoku import Grid, SudokuSolver
|
|
53
|
+
|
|
54
|
+
puzzle = (
|
|
55
|
+
"530070000"
|
|
56
|
+
"600195000"
|
|
57
|
+
"098000060"
|
|
58
|
+
"800060003"
|
|
59
|
+
"400803001"
|
|
60
|
+
"700020006"
|
|
61
|
+
"060000280"
|
|
62
|
+
"000419005"
|
|
63
|
+
"000080079"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
grid = Grid.from_string(puzzle)
|
|
67
|
+
result = SudokuSolver().solve(grid)
|
|
68
|
+
|
|
69
|
+
print(result.solved) # True
|
|
70
|
+
print(grid) # pretty-printed solved board
|
|
71
|
+
print(grid.to_string()) # 81-character string
|
|
72
|
+
|
|
73
|
+
for step in result.steps: # the full, explainable solving path
|
|
74
|
+
print(f"[{step.technique}] {step.description}")
|
|
75
|
+
print(result.techniques_used) # distinct techniques, in first-use order
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Puzzle strings are 81 characters, read left to right, top to bottom: digits
|
|
79
|
+
`1`–`9` are givens, `0` or `.` mark empty cells; whitespace and the decoration
|
|
80
|
+
characters `|`, `-`, `+` are ignored, so pretty-printed boards parse back.
|
|
81
|
+
|
|
82
|
+
Need a custom pipeline? Pass any sequence of techniques, tried in order:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from dedoku import SudokuSolver
|
|
86
|
+
from dedoku.techniques import HiddenSingle, NakedSingle, XYChain
|
|
87
|
+
|
|
88
|
+
solver = SudokuSolver(techniques=[NakedSingle(), HiddenSingle(), XYChain()])
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The board model is fully navigable if you want to build your own techniques:
|
|
92
|
+
each `Cell` knows its `row`, `column`, `subgrid`, `candidates`, and `peers`;
|
|
93
|
+
the `Grid` exposes all 27 houses via `rows`, `columns`, `subgrids`, `units`.
|
|
94
|
+
|
|
95
|
+
## Implemented techniques
|
|
96
|
+
|
|
97
|
+
The default pipeline applies 28 technique instances from 20 families, always
|
|
98
|
+
restarting from the simplest after every deduction.
|
|
99
|
+
|
|
100
|
+
| # | Family | Classes | Module |
|
|
101
|
+
|---|--------|---------|--------|
|
|
102
|
+
| 1 | Naked Candidates | `NakedSingle`, `NakedPair`, `NakedTriple`, `NakedQuad` | [naked.py](dedoku/techniques/naked.py) |
|
|
103
|
+
| 2 | Hidden Candidates | `HiddenSingle`, `HiddenPair`, `HiddenTriple`, `HiddenQuad` | [hidden.py](dedoku/techniques/hidden.py) |
|
|
104
|
+
| 3 | Intersection Removal | `PointingCandidates`, `ClaimingCandidates` | [intersections.py](dedoku/techniques/intersections.py) |
|
|
105
|
+
| 4 | X-Wing | `XWing` | [fish.py](dedoku/techniques/fish.py) |
|
|
106
|
+
| 5 | Chute Remote Pairs | `ChuteRemotePairs` | [chute.py](dedoku/techniques/chute.py) |
|
|
107
|
+
| 6 | Simple Colouring | `SimpleColouring` | [colouring.py](dedoku/techniques/colouring.py) |
|
|
108
|
+
| 7 | W-Wing | `WWing` | [wwing.py](dedoku/techniques/wwing.py) |
|
|
109
|
+
| 8 | Y-Wing (XY-Wing) | `YWing` | [wings.py](dedoku/techniques/wings.py) |
|
|
110
|
+
| 9 | Unique Rectangles | `UniqueRectangle` (Type 1) | [rectangles.py](dedoku/techniques/rectangles.py) |
|
|
111
|
+
| 10 | Swordfish | `Swordfish` | [fish.py](dedoku/techniques/fish.py) |
|
|
112
|
+
| 11 | XYZ-Wing | `XYZWing` | [wings.py](dedoku/techniques/wings.py) |
|
|
113
|
+
| 12 | BUG (Bivalue Universal Grave) | `BivalueUniversalGrave` | [bug.py](dedoku/techniques/bug.py) |
|
|
114
|
+
| 13 | Avoidable Rectangles | `AvoidableRectangle` | [rectangles.py](dedoku/techniques/rectangles.py) |
|
|
115
|
+
| 14 | Unique Rectangles Type 2 | `UniqueRectangleType2` | [rectangles.py](dedoku/techniques/rectangles.py) |
|
|
116
|
+
| 15 | Finned Fish | `FinnedXWing`, `FinnedSwordfish` | [fish.py](dedoku/techniques/fish.py) |
|
|
117
|
+
| 16 | X-Chain (basic X-Cycles) | `XChain` | [chains.py](dedoku/techniques/chains.py) |
|
|
118
|
+
| 17 | XY-Chain | `XYChain` | [chains.py](dedoku/techniques/chains.py) |
|
|
119
|
+
| 18 | 3D Medusa | `Medusa3D` | [medusa.py](dedoku/techniques/medusa.py) |
|
|
120
|
+
| 19 | ALS-XZ (Almost Locked Sets) | `AlsXz` | [als.py](dedoku/techniques/als.py) |
|
|
121
|
+
| 20 | AIC (Alternating Inference Chains) | `AIC` | [aic.py](dedoku/techniques/aic.py) |
|
|
122
|
+
|
|
123
|
+
Uniqueness-based techniques (9, 12, 13, 14) assume the puzzle has exactly one
|
|
124
|
+
solution — the standard convention for published Sudokus.
|
|
125
|
+
|
|
126
|
+
## Benchmark
|
|
127
|
+
|
|
128
|
+
500 unique-solution puzzles (seed 42), 100 for each of five difficulty
|
|
129
|
+
levels, timed against the **lightest classic backtracking solver** (bitmask,
|
|
130
|
+
sequential cells, no heuristics). Same protocol for both solvers: puzzle
|
|
131
|
+
string in, board out, minimum of 3 runs, parsing included. Python 3.13,
|
|
132
|
+
Windows 11. Levels are graded by the hardest technique the original
|
|
133
|
+
13-family pipeline needs: singles → subsets → intersections → advanced →
|
|
134
|
+
*extreme* (beyond that base pipeline).
|
|
135
|
+
|
|
136
|
+
### Solve-time distribution
|
|
137
|
+
|
|
138
|
+
<picture>
|
|
139
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-distribution-dark.svg">
|
|
140
|
+
<img alt="Solve-time distribution by difficulty level: one dot per puzzle on a logarithmic ms scale, backtracking vs logic library, with medians marked" src="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-distribution-light.svg">
|
|
141
|
+
</picture>
|
|
142
|
+
|
|
143
|
+
| Level | Solved by library | BT median | BT p95 | BT max | Library median | Library p95 | Library max |
|
|
144
|
+
|---|---|---|---|---|---|---|---|
|
|
145
|
+
| 1 · Singles | 100/100 | 15.9 ms | 481 ms | 1,757 ms | **1.1 ms** | 1.6 ms | 1.8 ms |
|
|
146
|
+
| 2 · Subsets | 100/100 | 13.1 ms | 218 ms | 2,172 ms | **1.6 ms** | 2.4 ms | 2.9 ms |
|
|
147
|
+
| 3 · Intersections | 100/100 | 5.0 ms | 126 ms | 2,026 ms | **3.1 ms** | 7.8 ms | 12.1 ms |
|
|
148
|
+
| 4 · Advanced | 100/100 | 15.6 ms | 225 ms | 551 ms | **7.5 ms** | 17.7 ms | 31.2 ms |
|
|
149
|
+
| 5 · Extreme | 89/100 † | **16.7 ms** | 229 ms | 684 ms | 65.4 ms | 306 ms | 720 ms |
|
|
150
|
+
| **Overall** | **489/500** | 12.6 ms | 279 ms | 2,172 ms | **3.0 ms** | 158 ms | 720 ms |
|
|
151
|
+
|
|
152
|
+
† On the 11 extreme puzzles the library cannot crack, its reported time is
|
|
153
|
+
the time to exhaust every technique and stop — never a guess.
|
|
154
|
+
|
|
155
|
+
Key findings:
|
|
156
|
+
|
|
157
|
+
- **The logic library wins on the median at every human-graded level up to
|
|
158
|
+
Advanced** (3.0 ms vs 12.6 ms overall) and is far more predictable: naive
|
|
159
|
+
backtracking's worst case is 2.2 s when the cell order is unlucky, versus
|
|
160
|
+
0.72 s for the library's hardest chain solve.
|
|
161
|
+
- **Brute-force time is uncorrelated with human difficulty** — backtracking
|
|
162
|
+
is fastest on level 3 and slowest on level 1, while the library's times
|
|
163
|
+
grow monotonically with the level. The two solvers measure different
|
|
164
|
+
notions of "hard".
|
|
165
|
+
- **Level 5 is chain territory**: the advanced techniques added in v0.2.0
|
|
166
|
+
raised the library's extreme-tier solve rate from 0/100 to 89/100.
|
|
167
|
+
|
|
168
|
+
### Which techniques crack the extreme tier
|
|
169
|
+
|
|
170
|
+
<picture>
|
|
171
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-techniques-dark.svg">
|
|
172
|
+
<img alt="Number of solved extreme puzzles in which each advanced technique fired, XY-Chain leading with 55 of 89" src="https://raw.githubusercontent.com/n36l3c7/dedoku/main/docs/benchmark-techniques-light.svg">
|
|
173
|
+
</picture>
|
|
174
|
+
|
|
175
|
+
### Reproduce it
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
python benchmark/generate_puzzles.py # optional: regenerate the dataset (seeded)
|
|
179
|
+
python benchmark/run_benchmark.py # times both solvers, writes benchmark/results.csv
|
|
180
|
+
python benchmark/make_charts.py # renders the SVG charts into docs/
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Architecture
|
|
184
|
+
|
|
185
|
+
| Class | Responsibility |
|
|
186
|
+
|-------|----------------|
|
|
187
|
+
| `Cell` | A single cell: value, candidates, position, given flag, and references to its row, column, and subgrid. |
|
|
188
|
+
| `Unit` → `Row`, `Column`, `Subgrid` | The 27 houses of nine cells, sharing bookkeeping logic. |
|
|
189
|
+
| `Grid` | The full 9×9 board: wiring, parsing, serialisation, validity. |
|
|
190
|
+
| `Technique` / `Step` | One logical strategy and the immutable record of one applied deduction. |
|
|
191
|
+
| `SudokuSolver` / `SolveResult` | The engine that chains techniques and the outcome of a session. |
|
|
192
|
+
|
|
193
|
+
## Development
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
python -m unittest discover -s tests -v
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
78 tests cover the board model, every technique (positive and negative
|
|
200
|
+
cases), and end-to-end solving. Chain and ALS implementations were
|
|
201
|
+
additionally validated by checking every benchmark solution against the
|
|
202
|
+
backtracking reference — zero mismatches.
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
Released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""A pure-Python Sudoku solver based on human-style logical deduction.
|
|
2
|
+
|
|
3
|
+
The package exposes an object-oriented board model (:class:`Cell`,
|
|
4
|
+
:class:`Row`, :class:`Column`, :class:`Subgrid`, :class:`Grid`) and a
|
|
5
|
+
logic-only solving engine (:class:`SudokuSolver`) that never resorts to
|
|
6
|
+
backtracking. It has no external dependencies.
|
|
7
|
+
|
|
8
|
+
Individual techniques live in :mod:`dedoku.techniques`.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from .cell import DIGITS, Cell
|
|
14
|
+
from .exceptions import ContradictionError, InvalidGridError, SudokuError
|
|
15
|
+
from .grid import Grid
|
|
16
|
+
from .solver import SolveResult, SudokuSolver
|
|
17
|
+
from .techniques import Step, Technique
|
|
18
|
+
from .units import Column, Row, Subgrid, Unit
|
|
19
|
+
|
|
20
|
+
__version__ = "0.3.0"
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"DIGITS",
|
|
24
|
+
"Cell",
|
|
25
|
+
"Column",
|
|
26
|
+
"ContradictionError",
|
|
27
|
+
"Grid",
|
|
28
|
+
"InvalidGridError",
|
|
29
|
+
"Row",
|
|
30
|
+
"SolveResult",
|
|
31
|
+
"Step",
|
|
32
|
+
"Subgrid",
|
|
33
|
+
"SudokuError",
|
|
34
|
+
"SudokuSolver",
|
|
35
|
+
"Technique",
|
|
36
|
+
"Unit",
|
|
37
|
+
"__version__",
|
|
38
|
+
]
|