Brute force sudoku cracker
Anton Vredegoor
anton.vredegoor at gmail.com
Sun Sep 18 08:41:50 EDT 2005
Diez B. Roggisch wrote:
> As everyone posts his, I'll do the same :) It uses some constraint based
> solving techniques - but not too complicated ones. When stuck, it
> backtracks. So far it never failed me, but I haven't tested it too
> thouroughly.
Thanks to all for sharing. I like to program sudoku and review such
code. So below here is my current file, I think it uses some techniques
not yet posted here. It also has a difficult 16x16 puzzle which I know
to be solvable (barring typing mistakes) but which my file can't solve
before I get tired of waiting, this might draw some heavyweights in the
ring :-)
I have also read the sudoku wiki page:
http://en.wikipedia.org/wiki/Sudoku
which was very helpfull and interesting (especially the link to Knuths
paper about dancing links was nice, even though I think (but maybe
wrongly so) that we don't need dancing links now that we've got sets,
but it's still a very very interesting paper)
I think the first important step is to realize that some variations
have fewer values so that it is possible to reduce the search space
early. Currently I'm exploring ideas about contigengies as explained in
the wiki above which seems promising, but I haven't found a nice way to
implement them yet. Maybe an easy optimization would be to find values
that don't come up often and choose variations containing those. And
maybe I should switch to an approach that has possibility values inside
the cells instead of computing them on the fly each time, that could
make contigency elimination easier.
Anton
from __future__ import generators
from sets import Set as set
problem1 = ['063000700','000690008','900007002',
'002010080','050806090','090070200',
'600100003','700045000','009000140']
problem2 = ['030009007','010080000','000100090',
'004905006','020000010','500607400',
'050001000','000040020','700500030']
problem3 = ['030500810','000760090','400000000',
'043905006','010000070','600801930',
'000000009','090086000','061002080']
problem4 = ['004530080','060009000','900000005',
'000800350','000000000','027006000',
'800000007','000300040','090072600']
X =[' 1 0 0 0 0 12 0 10 11 7 6 0 0 4 0 0',
' 0 7 0 0 15 13 0 0 0 0 2 0 0 8 0 0',
' 3 0 0 0 4 0 0 0 0 5 0 12 0 16 0 0',
' 0 0 14 2 0 9 0 0 0 0 1 0 0 0 0 0',
'10 15 0 1 0 0 0 2 0 16 0 0 3 0 0 0',
'12 0 0 3 0 0 15 0 0 13 0 4 0 1 9 5',
' 5 0 11 0 7 0 8 0 0 0 0 0 0 15 0 0',
' 7 13 0 16 0 0 0 6 0 0 0 14 0 10 0 0',
' 0 0 13 0 11 0 0 0 10 0 0 0 1 0 12 0',
' 0 0 7 0 0 0 0 0 0 3 0 16 0 14 0 13',
'16 8 0 0 14 0 5 0 0 15 0 0 4 0 0 6',
' 0 0 0 9 0 0 4 0 1 0 0 0 2 0 0 7',
' 0 0 0 0 0 16 0 0 0 0 8 0 10 5 0 0',
' 0 0 4 0 12 0 6 0 0 0 16 7 0 0 0 14',
' 0 0 6 0 0 1 0 0 0 0 12 13 0 0 11 0',
' 0 0 15 0 0 8 11 3 2 0 9 0 0 0 0 1']
problem5 = [row.split() for row in X]
class State:
def __init__(self,solved,unsolved):
self.solved = solved
self.unsolved = unsolved
self.size = int((len(solved)+len(unsolved))**.25)
def choiceset(self,x,y):
"the set of possible choices for an empty cell"
sz = self.size
res = set(range(1,sz*sz+1))
r,c = x/sz*sz,y/sz*sz
for (i,j),v in self.solved.iteritems():
if x == i or y == j or (r<=i<r+sz and c<=j<c+sz):
res.discard(v)
return res
def celldict(self):
"""dictionary of (cellcoords,choiceset) for each empty cell
if *any* empty cell cannot be filled return an empty dict
to signal an illegal position
"""
res = {}
for x,y in self.unsolved:
c = self.choiceset(x,y)
if not c:
return {}
res[x,y] = c
return res
class Node:
def __init__(self,state):
self.state = state
self.issolved = not bool(state.unsolved)
def children(self):
state = self.state
cd = state.celldict()
if cd: #position is still legal
ml = min([len(x) for x in cd.itervalues()])
#select empty cells which have the minimum number of
choices
items = [(k,v) for k,v in cd.iteritems() if len(v) == ml]
(x,y),S = min(items) #arbitrary choice of cell here
for v in S:
solved,unsolved =
dict(state.solved),dict(state.unsolved)
solved[x,y] = v
del unsolved[x,y]
yield Node(State(solved,unsolved))
def __repr__(self):
R = range(self.state.size**2)
res = [["__" for i in R] for j in R]+['']
for (i,j),v in self.state.solved.iteritems():
res[i][j] = "%02i" %v
return '\n'.join(map(' '.join,res))
def solutions(N):
if N.issolved:
yield N
else:
for child in N.children():
for g in solutions(child):
yield g
def todicts(P):
M = [map(int,row) for row in P]
solved = {}
unsolved = {}
for i,row in enumerate(M):
for j,x in enumerate(row):
if x:
solved[i,j] = x
else:
unsolved[i,j] = x
return solved,unsolved
def test():
solved,unsolved = todicts(problem4)
N = Node(State(solved,unsolved))
print N
for S in solutions(N):
print S
if __name__=='__main__':
test()
More information about the Python-list
mailing list