-
Notifications
You must be signed in to change notification settings - Fork 0
/
sudoku_solver.py
208 lines (178 loc) · 4.85 KB
/
sudoku_solver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
"""
Max Guo
November 21, 2011
Sudoku solver
"""
"""
TODO: - fix backtrack(), not recursive
"""
"""
USAGE: - python sudoku_solver.py
input files are text files without any extensions
solutions are output into the same directory
look at sample puzzles to see format of input files
solutions to sample puzzles also included
- more puzzles can be found at www.websudoku.com
"""
import copy
class sudoku_solver():
def __init__(self, file):
self.filename = file
f = open(file, "r")
#initialize domains of all variables
self.domain = {}
var_count = 0
for line in f:
for char in line:
if char == "*":
self.domain[var_count] = range(1, 10)
var_count += 1
else:
if not char == "\n":
self.domain[var_count] = [int(char)]
var_count += 1
self.constraints = []
#adding all row constraint arcs
for i in xrange(9):
for j in xrange(9*i, 9*i + 9):
for k in xrange(9*i, 9*i + 9):
if not j == k and not (j, k, "r") in self.constraints:
self.constraints.append((j, k, "r"))
#adding all column constraint arcs
for i in xrange(9):
for j in xrange(9):
for k in xrange(9*i + j, 81, 9):
for l in xrange(9*i + j, 81, 9):
if not k == l and not (k, l, "c") in self.constraints:
self.constraints.append((k, l, "c"))
#adding all block constraint arcs
const = [0, 3, 6, 27, 30, 33, 54, 57, 60]
for num in const:
for i in xrange(3):
for j in xrange(3):
for k in xrange(3):
for l in xrange(3):
if not i + 9*j == k + 9*l and not (i + 9*j + num, k + 9*l + num, "b") in self.constraints:
self.constraints.append((i + 9*j + num, k + 9*l + num, "b"))
f.close()
#ac-3 algorithm that eliminates values from the domains based on constraints, eventually solving the constraint satisfaction problem
def ac_3(self, domain):
queue = []
for constraint in self.constraints:
queue.append(constraint)
#the ac-3 algorithm
while queue:
(xi, xj, cons) = queue.pop(0)
if self.ac_3_helper(xi, xj, domain):
for (a, b, c) in self.constraints:
if b == xi and not a == xj:
queue.append((a, b, c))
if not domain[xi]:
return 0
for key in domain:
if not len(domain[key]) == 1:
return 1
return 2
#ac-3 helper method, checks the domains of two variables
def ac_3_helper(self, a, b, domain):
if len(domain[b]) == 1 and domain[b][0] in domain[a]:
domain[a].remove(domain[b][0])
return True
return False
#assign values using only logic
def logic_solve(self):
temp = copy.deepcopy(self.domain)
for i in xrange(81):
row = [i]
col = [i]
block = [i]
for (a, b, c) in self.constraints:
if a == i:
if c == "r":
row.append(b)
elif c == "c":
col.append(b)
else:
block.append(b)
d1 = {}
for var in row:
if len(temp[var]) > 1:
for val in temp[var]:
if not val in d1:
d1[val] = [var]
else:
d1[val].append(var)
for key in d1:
if len(d1[key]) == 1:
temp[d1[key][0]] = [key]
self.ac_3(temp)
d2 = {}
for var in col:
if len(temp[var]) > 1:
for val in temp[var]:
if not val in d2:
d2[val] = [var]
else:
d2[val].append(var)
for key in d2:
if len(d2[key]) == 1:
temp[d2[key][0]] = [key]
self.ac_3(temp)
d3 = {}
for var in block:
if len(temp[var]) > 1:
for val in temp[var]:
if not val in d3:
d3[val] = [var]
else:
d3[val].append(var)
for key in d3:
if len(d3[key]) == 1:
temp[d3[key][0]] = [key]
self.ac_3(temp)
self.domain = copy.deepcopy(temp)
#backtrack algorithm that calls ac-3 as a subroutine
def backtrack(self):
self.logic_solve()
if self.ac_3(self.domain) == 2:
return 2
temp = copy.deepcopy(self.domain)
temp2 = copy.deepcopy(self.domain)
for var in temp2:
if len(temp2[var]) > 1:
t = []
for val in temp2[var]:
t.append(val)
for val in t:
temp2[var] = [val]
result = self.ac_3(temp2)
if result == 2:
self.domain = copy.deepcopy(temp2)
return 2
else:
temp2 = copy.deepcopy(temp)
return 0
#output solution to file
def output_solution(self):
f = open(self.filename + "_solution.txt", "w")
f.write(self.filename + "\n")
if self.backtrack() == 2:
string = ""
count = 0
for key in self.domain:
string += str(self.domain[key][0])
count += 1
if count == 9:
count = 0
string += "\n"
f.write(string)
else:
f.write("No Solution. Domains of variables:\n")
for key in self.domain:
f.write(str(key) + ": " + str(self.domain[key]) + "\n")
f.close()
sudoku_solver("ac3solvable_example").output_solution()
sudoku_solver("gentle_sudoku").output_solution()
sudoku_solver("moderate_sudoku").output_solution()
sudoku_solver("diabolical_sudoku").output_solution()
sudoku_solver("guessing_puzzle").output_solution()