A Sudoku puzzle solver and generator using Mixed Integer Programming (MIP)
Project description
Sudoku MIP Solver
A Sudoku puzzle solver and generator using Mixed Integer Programming (MIP).
Table of Contents
- Features
- Installation
- Requirements
- Quick Start
- Command Line Interface
- SudokuMIPSolver API
- Examples
- License
Features
This package provides tools to:
- Solve Sudoku puzzles of any size using MIP optimization techniques
- Generate random Sudoku puzzles with varying difficulty levels
- Find all possible solutions for a given puzzle
- Support non-standard Sudoku grid dimensions (e.g., 12x12 with 4x3 sub-grids)
Installation
pip install sudoku-mip-solver
Requirements
- Python 3.9+
- PuLP (for the MIP solver)
Quick Start
from sudoku_mip_solver import SudokuMIPSolver
# Create a solver from a string representation of a 9x9 puzzle
puzzle_string = "530070000600195000098000060800060003400803001700020006060000280000419005000080079"
solver = SudokuMIPSolver.from_string(puzzle_string)
# Solve the puzzle
if solver.solve():
solver.pretty_print(solver.get_solution())
else:
print("No solution found!")
# Generate a random puzzle
new_solver, difficulty = SudokuMIPSolver.generate_random_puzzle(
sub_grid_width=3,
sub_grid_height=3,
target_difficulty=0.75
)
print(f"Generated puzzle with difficulty {difficulty:.2f}:")
new_solver.pretty_print(new_solver.board)
Command Line Interface
The package includes a command-line interface for solving and generating puzzles.
Basic Usage
# Display the version of the package
sudoku-mip-solver --version
# Solve a puzzle provided as a string
sudoku-mip-solver -s "530070000600195000098000060800060003400803001700020006060000280000419005000080079"
# Read a puzzle from a file
sudoku-mip-solver -f puzzle.txt
# Generate a random puzzle with medium difficulty
sudoku-mip-solver
# Generate but don't solve a puzzle
sudoku-mip-solver --generate-only
# Find all solutions to a puzzle
sudoku-mip-solver -f puzzle.txt -m -1
# Solve a non-standard 6x6 puzzle (2x3 sub-grids)
sudoku-mip-solver -s "530070600195098000" -w 2 -H 3
Command Line Options
Input Options
| Option | Description |
|---|---|
-s, --string |
Input puzzle as a string |
-f, --file |
Path to a file containing the puzzle |
--generate-only |
Generate a random puzzle without solving it |
Grid Dimensions
| Option | Description |
|---|---|
-w, --width |
Width of each sub-grid (default: 3) |
-H, --height |
Height of each sub-grid (default: same as width) |
Random Puzzle Options
| Option | Description |
|---|---|
-d, --difficulty |
Controls number of clues (0.0=easiest, 1.0=hardest, default: 0.75) |
--non-unique |
Skip uniqueness check, allowing multiple solutions |
Solver Options
| Option | Description |
|---|---|
-m, --max-solutions |
Maximum solutions to find (default: 1, use -1 for all) |
Output Options
| Option | Description |
|---|---|
-o, --output |
Save the solution or generated puzzle to a file |
-v, --verbose |
Show detailed solver information |
-q, --quiet |
Suppress all output except error messages |
--version |
Display the version number of the package |
SudokuMIPSolver API
Creating a Solver
# From a 2D array
board = [[5,3,0,0,7,0,0,0,0],
[6,0,0,1,9,5,0,0,0],
# ... (additional rows)
]
solver = SudokuMIPSolver(board, sub_grid_width=3, sub_grid_height=3)
# From a string representation
solver = SudokuMIPSolver.from_string("530070000...", sub_grid_width=3)
# Generate a random puzzle
solver, difficulty = SudokuMIPSolver.generate_random_puzzle(
sub_grid_width=3,
sub_grid_height=3,
target_difficulty=0.75,
unique_solution=True
)
Core Methods
| Method | Description |
|---|---|
build_model() |
Build the MIP model with all Sudoku constraints |
solve(show_output=False) |
Solve the puzzle and return True if solution found |
find_all_solutions(max_solutions=None) |
Find all solutions (or up to max_solutions) |
get_solution() |
Get the current solution |
reset_model() |
Remove all solution cuts, restoring original constraints |
Utility Methods
| Method | Description |
|---|---|
to_string(board=None, delimiter=None) |
Convert board to string representation |
get_pretty_string(board=None) |
Get the board as a formatted string with grid lines showing sub-grids |
pretty_print(board=None) |
Print the board with grid lines showing sub-grids |
extract_solution() |
Extract solution from model variables |
print_model() |
Print the model in a readable format |
Class Methods
| Method | Description |
|---|---|
from_string(sudoku_string, sub_grid_width=3, sub_grid_height=None, delimiter=None) |
Create solver from string representation |
generate_random_puzzle(sub_grid_width=3, sub_grid_height=None, target_difficulty=0.75, unique_solution=True, max_attempts=100, random_seed=None) |
Generate a random puzzle with specified parameters |
Algorithm Details
The solver uses Mixed Integer Programming (MIP) to model and solve Sudoku puzzles:
- Decision Variables: Binary variables x[i,j,k] representing whether cell (i,j) contains value k
- Constraints:
- Each cell must contain exactly one value
- Each row must contain all values exactly once
- Each column must contain all values exactly once
- Each sub-grid must contain all values exactly once
- Initial clues are fixed to their given values
- Solution Finding:
- The MIP solver (provided by PuLP) finds a feasible solution satisfying all constraints
- For multiple solutions, solution cuts are added to exclude previously found solutions
The random puzzle generator works by:
- Creating a complete, solved Sudoku grid
- Systematically removing values while ensuring the puzzle maintains a unique solution
- Continuing removal until the target difficulty level is reached
Examples
Solving a Puzzle
# Standard 9x9 puzzle
puzzle = "530070000600195000098000060800060003400803001700020006060000280000419005000080079"
solver = SudokuMIPSolver.from_string(puzzle)
solver.solve()
solver.pretty_print(solver.get_solution())
Getting Formatted Output as String
# Get the formatted puzzle as a string instead of printing directly
puzzle = "530070000600195000098000060800060003400803001700020006060000280000419005000080079"
solver = SudokuMIPSolver.from_string(puzzle)
solver.solve()
# Get the formatted string
formatted_solution = solver.get_pretty_string(solver.get_solution())
print("Solution:")
print(formatted_solution)
# You can also save it to a file or process it further
with open("solution.txt", "w") as f:
f.write(formatted_solution)
Finding Multiple Solutions
# Find all solutions to an under-constrained puzzle
puzzle = "123456789000000000000000000000000000000000000000000000000000000000000000000000000"
solver = SudokuMIPSolver.from_string(puzzle)
solutions = solver.find_all_solutions(max_solutions=5) # Limit to 5 solutions
print(f"Found {len(solutions)} solutions")
Working with Non-Standard Grids
# Create a 6x6 puzzle with 2x3 sub-grids
puzzle = "123456000000000000000000000000000000"
solver = SudokuMIPSolver.from_string(puzzle, sub_grid_width=2, sub_grid_height=3)
solver.solve()
solver.pretty_print(solver.get_solution())
Generating Puzzles with Different Difficulties
The target_difficulty parameter controls the number of clues in the generated puzzle:
- 0.0 = maximum clues (easiest puzzles)
- 1.0 = minimum clues (hardest puzzles)
For standard 9×9 Sudoku puzzles, a difficulty of 1.0 targets the theoretical minimum of 17 clues (mathematically proven lower bound). For puzzles of other sizes, the minimum is (wrongly) estimated as N clues for an N×N grid.
# Very easy puzzle (more clues)
easy_solver, difficulty = SudokuMIPSolver.generate_random_puzzle(target_difficulty=0.3)
print(f"Easy puzzle (difficulty: {difficulty:.2f})")
easy_solver.pretty_print(easy_solver.board)
# Harder puzzle (fewer clues)
hard_solver, difficulty = SudokuMIPSolver.generate_random_puzzle(target_difficulty=0.9)
print(f"Hard puzzle (difficulty: {difficulty:.2f})")
hard_solver.pretty_print(hard_solver.board)
License
This project is licensed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sudoku_mip_solver-0.2.0.tar.gz.
File metadata
- Download URL: sudoku_mip_solver-0.2.0.tar.gz
- Upload date:
- Size: 28.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
676ae768c49c8c1883cd4de2f40be05fdebedbfc0de443b3f3473e12a82025d6
|
|
| MD5 |
830f9f5a833233c5c3db3e4784bf3213
|
|
| BLAKE2b-256 |
b0b7c965c1bebcd855bad8d6417a7229ad4f58e982d0fd40280135d10465a4fd
|
File details
Details for the file sudoku_mip_solver-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sudoku_mip_solver-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5da6f38eb3db10dca93f8baece135f3a5f0b7d9a6fb8e1bf683ba90316ae973
|
|
| MD5 |
738e1854c51434847b23d924d8e9de46
|
|
| BLAKE2b-256 |
557d07f2a83c5c9b3a2a55ec3c6e1e9f045b8e0e885d21393666ce550e1a817c
|