|
|
|
#!/usr/bin/python
|
|
|
|
# vim:set fileencoding=utf-8:
|
|
|
|
|
|
|
|
"""
|
|
|
|
A backtracking Sudoku solver.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from __future__ import division, print_function
|
|
|
|
import numpy as np
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
puzzle = [3, 7, 0, 0, 0, 4, 9, 5, 1,
|
|
|
|
0, 0, 2, 5, 0, 0, 0, 0, 8,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 7,
|
|
|
|
0, 0, 0, 0, 0, 3, 0, 2, 0,
|
|
|
|
0, 1, 0, 0, 2, 0, 0, 4, 0,
|
|
|
|
0, 5, 0, 9, 0, 0, 0, 0, 0,
|
|
|
|
8, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
7, 0, 0, 0, 0, 5, 3, 0, 0,
|
|
|
|
5, 9, 4, 6, 0, 0, 0, 7, 2]
|
|
|
|
puzzle = np.array(puzzle).reshape(9, 9)
|
|
|
|
|
|
|
|
visual = True
|
|
|
|
|
|
|
|
|
|
|
|
def valid():
|
|
|
|
rows = np.vsplit(puzzle, 9)
|
|
|
|
cols = np.hsplit(puzzle, 9)
|
|
|
|
grids = [grid for h in np.hsplit(puzzle, 3) for grid in np.vsplit(h, 3)]
|
|
|
|
|
|
|
|
units = rows + cols + grids
|
|
|
|
return all(np.max(np.bincount(unit[unit != 0])) == 1 for unit in units)
|
|
|
|
|
|
|
|
|
|
|
|
def print_puzzle(clear=True):
|
|
|
|
ANSI_CPL = "\033[%dF" # CPL = Cursor Previous Line
|
|
|
|
|
|
|
|
if clear:
|
|
|
|
print(ANSI_CPL % 10)
|
|
|
|
|
|
|
|
for row in np.vsplit(puzzle, 9):
|
|
|
|
for element in row[0]:
|
|
|
|
if element != 0:
|
|
|
|
print(element, end=' ')
|
|
|
|
else:
|
|
|
|
print('‧', end=' ')
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
def set_next_number():
|
|
|
|
position = find_first(puzzle == 0)
|
|
|
|
for number in range(1, 10):
|
|
|
|
puzzle[position] = number
|
|
|
|
if valid():
|
|
|
|
if visual:
|
|
|
|
print_puzzle()
|
|
|
|
time.sleep(0.005)
|
|
|
|
if 0 not in puzzle:
|
|
|
|
# Solved
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
if set_next_number():
|
|
|
|
return True
|
|
|
|
|
|
|
|
# dead end ⇒ backtrack
|
|
|
|
puzzle[position] = 0
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def find_first(condition):
|
|
|
|
return tuple(p[0] for p in np.where(condition))
|
|
|
|
|
|
|
|
|
|
|
|
print(__doc__)
|
|
|
|
print_puzzle(clear=False)
|
|
|
|
|
|
|
|
if set_next_number():
|
|
|
|
print_puzzle()
|