Created
September 29, 2016 19:08
-
-
Save valdergallo/bd33ed2ae26e736831216267ebf30e94 to your computer and use it in GitHub Desktop.
Sudoku AI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
n = 3 # Size of inner region | |
n2, n3, n4 = n**2, n**3, n**4 | |
def show(flatline): | |
'Display grid from a string (values in row major order with blanks for unknowns)' | |
fmt = '|'.join(['%s' * n] * n) | |
sep = '+'.join(['-' * n] * n) | |
for i in range(n): | |
for j in range(n): | |
offset = (i*n+j)*n2 | |
print fmt % tuple(flatline[offset:offset+n2]) | |
if i != n-1: | |
print sep | |
def _find_friends(cell): | |
'Return tuple of cells in same row, column, or subgroup' | |
friends = set() | |
row, col = cell // n2, cell % n2 | |
friends.update(row * n2 + i for i in range(n2)) | |
friends.update(i * n2 + col for i in range(n2)) | |
nw_corner = row // n * n3 + col // n * n | |
friends.update(nw_corner + i + j for i in range(n) for j in range(0, n3, n2)) | |
friends.remove(cell) | |
return tuple(friends) | |
friend_cells = map(_find_friends, range(n4)) | |
def select_an_unsolved_cell(possibles, heuristic=min): | |
# Default heuristic: select cell with fewest possibilities | |
# Other possible heuristics include: random.choice() and max() | |
return heuristic((len(p), cell) for cell, p in enumerate(possibles) if len(p)>1)[1] | |
def solve(possibles, pending_marks): | |
# Apply pending_marks (list of cell,value pairs) to possibles (list of str). | |
# Mutates both inputs. Return solution as a flat string (values in row-major order) | |
# or return None for dead-ends where all possibilites have been eliminated. | |
for cell, v in pending_marks: | |
possibles[cell] = v | |
for f in friend_cells[cell]: | |
p = possibles[f] | |
if v in p: | |
p = possibles[f] = p.replace(v, '') # exclude value v from friend f | |
if not p: | |
return None # Dead-end: all possibilities eliminated | |
if len(p) == 1: | |
pending_marks.append((f, p[0])) | |
# Check to see if the puzzle is fully solved (each cell has only one possible value) | |
if max(map(len, possibles)) == 1: | |
return ''.join(possibles) | |
# If it gets here, there are still unsolved cells | |
cell = select_an_unsolved_cell(possibles) | |
for v in possibles[cell]: # try all possible values for that cell | |
ans = solve(possibles[:], [(cell, v)]) | |
if ans is not None: | |
return ans | |
# ----- Examples ----- | |
for given in [ | |
'53 7 6 195 98 6 8 6 34 8 3 17 2 6 6 28 419 5 8 79', | |
' 75 4 5 8 17 6 36 2 7 1 5 1 1 5 8 96 1 82 3 4 9 48 ', | |
' 9 7 4 1 6 2 8 1 43 6 59 1 3 97 8 52 7 6 8 4 7 5 8 2 ', | |
'67 38 921 85 736 1 8 4 7 5 1 8 4 2 6 8 5 175 24 321 61 84', | |
'27 15 8 3 7 4 7 5 1 7 9 2 6 2 5 8 6 5 4 8 59 41', | |
]: | |
show(given) | |
pending_marks = [(i, v) for i, v in enumerate(given) if v != ' '] | |
possibles = ['123456789'] * len(given) | |
result = solve(possibles, pending_marks) | |
show(result) | |
print '=-' * 20 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment