Implement search
- Remove __setitem__; it was getting in the way... - deepcopy the board for each trial in search()
This commit is contained in:
parent
cc5e35ff81
commit
6cc3fed4b3
1 changed files with 21 additions and 19 deletions
40
sudoku.py
40
sudoku.py
|
@ -5,6 +5,7 @@
|
||||||
A Sudoku solver.
|
A Sudoku solver.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class Board(dict):
|
||||||
if initial_value not in self.possible_values:
|
if initial_value not in self.possible_values:
|
||||||
raise ValueError('Invalid initial value for square ({}, {}): {}'.format(x, y, initial_value))
|
raise ValueError('Invalid initial value for square ({}, {}): {}'.format(x, y, initial_value))
|
||||||
LOG.debug('Setting initial value of {} to {}'.format(square, initial_value))
|
LOG.debug('Setting initial value of {} to {}'.format(square, initial_value))
|
||||||
self[square] = [initial_value]
|
self.assign(square, [initial_value])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def xy_key(self, x, y):
|
def xy_key(self, x, y):
|
||||||
|
@ -119,29 +120,36 @@ class Board(dict):
|
||||||
del peers[Board.xy_key(x, y)]
|
del peers[Board.xy_key(x, y)]
|
||||||
return peers
|
return peers
|
||||||
|
|
||||||
def clear(self):
|
def clear(self, squares=None):
|
||||||
'''
|
'''
|
||||||
Clear the board. Resets each square's possible value list to the full range of possible values for this board.
|
Clear the board. Resets each square's possible value list to the full range of possible values for this board.
|
||||||
'''
|
'''
|
||||||
for square in self:
|
for square in (self if squares is None else squares):
|
||||||
self[square] = list(self.possible_values)
|
self.assign(square, list(self.possible_values))
|
||||||
|
|
||||||
def solve(self):
|
def solve(self):
|
||||||
for square, values in self.items():
|
return self.search()
|
||||||
if len(values) == 1:
|
|
||||||
try:
|
|
||||||
self.assign(square, values)
|
|
||||||
except ValueError:
|
|
||||||
raise
|
|
||||||
return self.solved
|
|
||||||
|
|
||||||
def search(self):
|
def search(self):
|
||||||
|
'''
|
||||||
|
Search for a solution, depth-first. Return the solved Board as a new instance of this class.
|
||||||
|
'''
|
||||||
if self.solved:
|
if self.solved:
|
||||||
return True
|
return self
|
||||||
|
|
||||||
# Chose the square with the fewest possible values.
|
# Chose the square with the fewest possible values.
|
||||||
_, smallest = min((len(self[sq]), sq) for sq in self if len(self[sq]) > 1)
|
_, smallest = min((len(self[sq]), sq) for sq in self if len(self[sq]) > 1)
|
||||||
# Deepcopy the board.
|
# Deepcopy the board.
|
||||||
|
for v in self[smallest]:
|
||||||
|
trial_board = copy.deepcopy(self)
|
||||||
|
try:
|
||||||
|
trial_board.assign(smallest, [v])
|
||||||
|
if trial_board.search():
|
||||||
|
return trial_board
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
raise ValueError('No possible solution found.')
|
||||||
|
|
||||||
|
|
||||||
def assign(self, square, value):
|
def assign(self, square, value):
|
||||||
'''
|
'''
|
||||||
|
@ -196,19 +204,13 @@ class Board(dict):
|
||||||
raise ValueError('No place for value {} to go in unit {}; board is now invalid'.format(value, unit))
|
raise ValueError('No place for value {} to go in unit {}; board is now invalid'.format(value, unit))
|
||||||
elif len(places) == 1:
|
elif len(places) == 1:
|
||||||
LOG.debug('One place for value {} to be in unit {}; setting'.format(value, unit))
|
LOG.debug('One place for value {} to be in unit {}; setting'.format(value, unit))
|
||||||
self[places[0]] = [value]
|
self.assign(places[0], [value])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
# Don't allow deleting keys from self.
|
# Don't allow deleting keys from self.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if key not in self:
|
|
||||||
# Don't allow adding new keys, only changes to existing ones.
|
|
||||||
raise KeyError('Key {} is not a valid coordinate pair.'.format(key))
|
|
||||||
self.assign(key, value)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
lines = []
|
lines = []
|
||||||
box_lines = []
|
box_lines = []
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue