sudoku-solver.py
1 |
|
2 |
sudoku-solver.py - A simple program that can solve any (solvable) sudoku puzzle. |
3 |
Copyright 2016 Maarten 'Vngngdn' Vangeneugden |
4 |
|
5 |
Licensed under the Apache License, Version 2.0 (the "License"); |
6 |
you may not use this file except in compliance with the License. |
7 |
You may obtain a copy of the License at |
8 |
|
9 |
https://www.apache.org/licenses/LICENSE-2.0 |
10 |
|
11 |
Unless required by applicable law or agreed to in writing, software |
12 |
distributed under the License is distributed on an "AS IS" BASIS, |
13 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
See the License for the specific language governing permissions and |
15 |
limitations under the License. |
16 |
""" |
17 |
|
18 |
""" |
19 |
This sudoku solver takes a recursive approach to solve a given sudoku. |
20 |
Although there are many different variations and types of sudokus, this program |
21 |
only handles NxN sudokus (i.e. squares with root-subgrids, like the most common |
22 |
9x9). So if you want hexadecimal plays, you can totally do that. |
23 |
""" |
24 |
|
25 |
# Imports: |
26 |
from math import sqrt # For the roots of the sudoku length. |
27 |
|
28 |
# Constants: |
29 |
EMPTY = 0 |
30 |
|
31 |
# Prints the given sudoku to the terminal in a readable way: |
32 |
def print_sudoku(sudoku): |
33 |
# In order to print in a clean way, we first have to determine the length of |
34 |
# the biggest number: |
35 |
biggestNumber = 0 |
36 |
for row in sudoku: |
37 |
for number in row: |
38 |
if number > biggestNumber: |
39 |
biggestNumber = number |
40 |
|
41 |
rootNumber = 10 |
42 |
digits = 1 |
43 |
while rootNumber**digits < biggestNumber: |
44 |
digits += 1 |
45 |
# And now we've got the largest amount of digits. |
46 |
|
47 |
for row in sudoku: |
48 |
for number in row: |
49 |
# XXX: Even though the next line looks a bit dirty, it's a great way |
50 |
# to deduce the required amount of whitespace for readable output. |
51 |
# It takes the highest amount of digits, subtracted with the current |
52 |
# number's digits. so 10 in a sudoku with max. 3 digits: 3-2+1 = 2 |
53 |
# whitespaces. |
54 |
spaces = " " * (digits-len(str(number)) + 1) |
55 |
print(str(number) + spaces, end="") |
56 |
|
57 |
for i in range(0, digits): |
58 |
# And after each row, a seperation whitespace. |
59 |
print() |
60 |
|
61 |
|
62 |
|
63 |
|
64 |
# Given an empty sudoku, the solution is very simple. |
65 |
# Although this is mainly a small optimization, as it is only usable when the |
66 |
# root solution is possible. |
67 |
def solve_empty_sudoku(sudoku): |
68 |
n = sqrt(len(sudoku)) |
69 |
|
70 |
for i in range(0, n*n): |
71 |
for j in range(0, n*n): |
72 |
sudoku[i][j] = (i*n + i/n + j) % (n*n) + 1; |
73 |
return sudoku |
74 |
|
75 |
|
76 |
# Checks if the given sudoku qualifies as a root solution. |
77 |
# This function mainly serves as a silly optimization to check beforehand |
78 |
# whether we have to do the entire backtrack. |
79 |
def is_possible_root_solution(sudoku): |
80 |
root_solution = solve_empty_sudoku() |
81 |
for x in range(0, len(root_solutxon)): |
82 |
for y in range(0, len(root_solution[x])): |
83 |
if sudoku[x][y] != root_solution[x][y] and sudoku[x][y] != EMPTY: |
84 |
return False |
85 |
# when here, all the sudoku can be filled as a root solution. |
86 |
return True |
87 |
|
88 |
# Checks whether the given number already exists in the given array. |
89 |
def exists_in_array(x, y, value, sudoku): |
90 |
if value == EMPTY: |
91 |
return False # Just... of course. |
92 |
|
93 |
for i in range(0, len(sudoku)): |
94 |
if sudoku[i][y] == value: # No need to check for empty |
95 |
return True |
96 |
return False |
97 |
|
98 |
# Checks whether the number on the given location is unique in its column. |
99 |
def exists_in_column(x, y, value, sudoku): |
100 |
if value == EMPTY: |
101 |
return False |
102 |
|
103 |
for i in range(0, len(sudoku[x])): |
104 |
if sudoku[x][i] == value: |
105 |
return True |
106 |
return False |
107 |
|
108 |
# Checks whether the number on the given location is unique in its "root grid". |
109 |
def exists_in_grid(x, y, value, sudoku): |
110 |
if value == EMPTY: |
111 |
return False |
112 |
|
113 |
# We're going to find out now in which part of the grid the (x,y) is put. |
114 |
# The idea I'm going for : |
115 |
# I'll first try to find out in which segment of the sudoku the value is in. |
116 |
# When I've found it, I trim the data to that segment, after I'll be |
117 |
# checking on that segment only. |
118 |
n = int(sqrt(len(sudoku))) # Determining the square root of the sudoku's length. |
119 |
|
120 |
# The following algorithm is able to handle all square sudokus. |
121 |
A = x%n |
122 |
B = y%n |
123 |
for i in range(0, n): |
124 |
for j in range(0, n): |
125 |
C = x - A + i |
126 |
D = y - B + j |
127 |
if sudoku[C][D] == value and (C != x and D != y): |
128 |
return True |
129 |
return False |
130 |
|
131 |
# Checks whether the sudoku still contains empty grids. Returns false if not, |
132 |
# and vice versa. |
133 |
def is_filled_sudoku(sudoku): |
134 |
for x in range(0, len(sudoku)): |
135 |
for y in range(0, len(sudoku[x])): |
136 |
if sudoku[x][y] == EMPTY: |
137 |
return False |
138 |
return True |
139 |
|
140 |
# Looks from the upper left grid to the lower right grid of the sudoku to find |
141 |
# an empty grid. If it encounters an empty grid, the respective (x,y) |
142 |
# coordinates are returned. |
143 |
# If no empty grid is being found, both returned values will be -1. |
144 |
def find_first_empty_grid(sudoku): |
145 |
for x in range(0, len(sudoku)): |
146 |
for y in range(0, len(sudoku[x])): |
147 |
if sudoku[x][y] == EMPTY: |
148 |
return x, y |
149 |
# When we get to this point, there's no empty grid anymore in the sudoku. |
150 |
raise Exception(""" |
151 |
The given sudoku does not feature any empty grids. Assert that you've |
152 |
given the sudoku to the is_filled_sudoku() function. |
153 |
""") |
154 |
|
155 |
# Works identical to the other function, but looks for the first grid with the |
156 |
# smallest amount of possibilities. So grids with 2 possibilities are returned, |
157 |
# even if an empty grid was found already, having 5 possibilities. |
158 |
def find_first_empty_grid_optimized(sudoku, possibilities): |
159 |
i = 0 |
160 |
j = 0 |
161 |
minimum = len(sudoku)+1 # +1 guarantees 1 will always be chosen. |
162 |
for x in range(0, len(sudoku)): |
163 |
for y in range(0, len(sudoku[x])): |
164 |
if sudoku[x][y] == EMPTY: |
165 |
if len(possibilities[x][y]) < minimum: |
166 |
i, j = x, y |
167 |
minimum = len(possibilities[x][y]) |
168 |
return i, j |
169 |
|
170 |
|
171 |
|
172 |
# Checks whether assigning the given value to the given coordinate in the sudoku |
173 |
# still renders the sudoku valid. |
174 |
def is_valid_assignment(x, y, value, sudoku): |
175 |
if exists_in_array(x, y, value, sudoku): |
176 |
return False |
177 |
if exists_in_column(x, y, value, sudoku): |
178 |
return False |
179 |
if exists_in_grid(x, y, value, sudoku): |
180 |
return False |
181 |
return True |
182 |
|
183 |
# Collects all symbols that can be placed in the given place. |
184 |
def collect_possible_entries(sudoku, x, y): |
185 |
# The strategy is simple: Iterate over all possibilities, and check whether |
186 |
# it's possible or not. |
187 |
possible_values = set() |
188 |
for value in range(1, len(sudoku)+1): |
189 |
if is_valid_assignment(x, y, value, sudoku): |
190 |
possible_values.add(value) |
191 |
return possible_values |
192 |
|
193 |
# Updates the possibilities, in function of the given position. |
194 |
def remove_possibility(sudoku, possibilities, x, y): |
195 |
for i in range(len(possibilities)): |
196 |
for j in range(len(possibilities[i])): |
197 |
# i==x XOR j==y |
198 |
if (i==x and j!=y) or (i!=x and j==y): |
199 |
# The next if-test is necessary to avoid a KeyErrro. |
200 |
if sudoku[x][y] in possibilities[i][j]: |
201 |
#assert (i != x and j == y) or ( |
202 |
possibilities[i][j].remove(sudoku[x][y]) |
203 |
# Removal of possibilities in grid: |
204 |
n = int(sqrt(len(sudoku))) # Determining the square root of the sudoku's length. |
205 |
A = x%n |
206 |
B = y%n |
207 |
for i in range(0, n): |
208 |
for j in range(0, n): |
209 |
C = x - A + i |
210 |
D = y - B + j |
211 |
if C!=x and D!=y: |
212 |
if sudoku[x][y] in possibilities[C][D]: |
213 |
assert C != x and D != y |
214 |
possibilities[C][D].remove(sudoku[x][y]) |
215 |
|
216 |
def add_possibility(sudoku, possibilities, x, y, value): |
217 |
for i in range(len(possibilities)): |
218 |
for j in range(len(possibilities[i])): |
219 |
# i==x XOR j==y |
220 |
#if (i==x or j==y) and not (i==x and j==y): |
221 |
if (i==x and j!=y) or (i!=x and j==y): |
222 |
#assert i != x and j != y |
223 |
possibilities[i][j].add(value) |
224 |
# Addition of possibilities in grid: |
225 |
n = int(sqrt(len(sudoku))) # Determining the square root of the sudoku's length. |
226 |
A = x%n |
227 |
B = y%n |
228 |
for i in range(0, n): |
229 |
for j in range(0, n): |
230 |
C = x - A + i |
231 |
D = y - B + j |
232 |
if C!=x and D!=y: |
233 |
assert C != x and D != y |
234 |
possibilities[C][D].add(value) |
235 |
|
236 |
# TODO: For remove/add_possibility: Add √n*√n grid removal as well. |
237 |
|
238 |
|
239 |
# Applies a recursive backtrack algorithm to the given sudoku, in an attempt to |
240 |
# solve it. |
241 |
def recursive_solution(sudoku, possibilities): |
242 |
if is_filled_sudoku(sudoku): |
243 |
return True # The sudoku is solved. |
244 |
else: |
245 |
#x, y = find_first_empty_grid(sudoku) |
246 |
x, y = find_first_empty_grid_optimized(sudoku, possibilities) |
247 |
if len(possibilities[x][y]) == EMPTY: |
248 |
# With an empty grid, there must be a possible value to enter. If there |
249 |
# isn't, that means that somewhere earlier, a possibility was removed, |
250 |
# because the wrong value was added. Thus, this is an impossible |
251 |
# solution, and we must backtrack. |
252 |
return False |
253 |
|
254 |
for i in range(1, 1+len(sudoku)): |
255 |
if i in possibilities[x][y]: |
256 |
# We don't need to test if it's concerning a valid assignment, since |
257 |
# if it weren't a valid assignment, it wouldn't be listed as a |
258 |
# possibility in the first place. |
259 |
sudoku[x][y] = i |
260 |
remove_possibility(sudoku, possibilities, x, y) |
261 |
possibilities[x][y].remove(i) |
262 |
if recursive_solution(sudoku, possibilities) is False: |
263 |
sudoku[x][y] = EMPTY |
264 |
add_possibility(sudoku, possibilities, x, y, i) |
265 |
i = 0 # Reloop over all posibilities (which may have updated |
266 |
# Note how I don't add the tested possibility back, as I did |
267 |
# remove the possibility. That is, because it's clear that |
268 |
# this cannot be a solution if we want to solve the sudoku. |
269 |
# Thus, it shouldn't be added back. |
270 |
else: |
271 |
return True |
272 |
return False |
273 |
|
274 |
# Assertion function. Checks whether the sudoku is a valid, and solved sudoku. |
275 |
def test_solution(sudoku): |
276 |
discovered = [] # This list will be used to store discovered numbers. |
277 |
# Rows: |
278 |
for column in sudoku: |
279 |
for number in column: |
280 |
if number in discovered: |
281 |
return False |
282 |
else: |
283 |
discovered.append(number) |
284 |
discovered.clear() |
285 |
|
286 |
#Columns: |
287 |
for i in range(0, len(sudoku)): |
288 |
for y in range(0, len(sudoku[i])): |
289 |
if y in discovered: |
290 |
return False |
291 |
else: |
292 |
discovered.append(y) |
293 |
discovered.clear() |
294 |
|
295 |
#Grids: |
296 |
# Checking for grids requires us to collect the starting points of said |
297 |
# grids. |
298 |
n = int(sqrt(len(sudoku))) |
299 |
gridPoints = [] |
300 |
for i in range(0, n): |
301 |
gridPoints.append(i*n) |
302 |
|
303 |
for k in gridPoints: |
304 |
for x in range(0,n): |
305 |
for y in range(0,n): |
306 |
if sudoku[x+k][y+k] in discovered: |
307 |
return False |
308 |
else: |
309 |
discovered.append(sudoku[x+k][y+k]) |
310 |
discovered.clear() |
311 |
|
312 |
return True |
313 |
|
314 |
# Prints an introduction paragraph to the user, explaining the details of the |
315 |
# program, and how to operate it properly. |
316 |
def print_introduction(): |
317 |
introduction = """ |
318 |
Welcome to Vngngdn's sudoku solver! |
319 |
I'll explain briefly what you have to do to use this program: |
320 |
After this paragraph, insert the first array of the sudoku, with each |
321 |
grid seperated by 1 space. |
322 |
If there are grids that are empty, use a 0 as placeholder for the empty |
323 |
space. |
324 |
When the row is complete, hit RETURN. |
325 |
The program will then ask for other arrays, until a square sudoku is |
326 |
formed. |
327 |
So, for example, if you enter 9 numbers, and hit RETURN, the program |
328 |
will ask for 8 more arrays, to create a 9x9 sudoku. |
329 |
When the last array has been entered, the program will stop asking for |
330 |
input, and immediately try to solve the sudoku. |
331 |
When the sudoku has been solved, it will print the solution in a |
332 |
readable way. |
333 |
If the sudoku could not be solved, it will print "Failed!", instead of a |
334 |
solution. |
335 |
This indicates the sudoku is most likely invalid, and can't be solved. |
336 |
Off you go now! |
337 |
|
338 |
""" |
339 |
print(introduction) |
340 |
|
341 |
# Asks the user for input. |
342 |
def receive_input(): |
343 |
sudoku = [] # This will be returned in the end. |
344 |
#integers = [] # A list which will be added to the sudoku after input. |
345 |
length = 1 # We know there will be at least 1 number. |
346 |
i = 0 |
347 |
while i < length: |
348 |
integers = [] # A list which will be added to the sudoku after input. |
349 |
integers.clear() |
350 |
# The amount of given numbers implies the remaining amount of rows, |
351 |
# because only square sudokus are handled. |
352 |
line = input() |
353 |
numbers = line.split() # numbers now contains the given numbers. |
354 |
# XXX: Next addition might be redundant after the first one, but it's |
355 |
# still cleaner than having an entire redundant block. |
356 |
length = len(numbers) |
357 |
for number in numbers: |
358 |
integers.append(int(number)) |
359 |
sudoku.append(integers) |
360 |
|
361 |
i += 1 |
362 |
|
363 |
# Current state: The sudoku is completely filled in, including empty spots. |
364 |
# TODO: Add some defensive programming structure, to check for empty spots |
365 |
# in the sudoku. If so, ask the user where to insert the missing numbers. |
366 |
|
367 |
return sudoku |
368 |
|
369 |
|
370 |
|
371 |
# MAIN (sort of) |
372 |
|
373 |
print_introduction() |
374 |
sudoku = receive_input() |
375 |
""" |
376 |
sudoku = [ |
377 |
[0, 0, 0, 0, 9, 0, 4, 2, 0], |
378 |
[0, 0, 0, 0, 0, 0, 0, 0, 8], |
379 |
[9, 0, 0, 1, 0, 0, 3, 0, 0], |
380 |
[0, 3, 0, 0, 5, 8, 9, 1, 0], |
381 |
[0, 0, 0, 9, 0, 3, 0, 0, 0], |
382 |
[0, 1, 9, 4, 2, 0, 0, 5, 0], |
383 |
[0, 0, 5, 0, 0, 6, 0, 0, 4], |
384 |
[6, 0, 0, 0, 0, 0, 0, 0, 0], |
385 |
[0, 2, 7, 0, 8, 0, 0, 0, 0] |
386 |
] |
387 |
""" |
388 |
""" |
389 |
sudoku = [ |
390 |
[2, 0, 0, 0], |
391 |
[0, 0, 1, 0], |
392 |
[0, 3, 0, 0], |
393 |
[0, 0, 0, 4] |
394 |
] |
395 |
""" |
396 |
|
397 |
""" |
398 |
sudoku = [ |
399 |
[0, 0, 0, 0, 1, 5, 8, 0, 6, 0, 0, 0, 7, 0, 0, 2], |
400 |
[1, 0, 0, 0, 4, 3, 0, 6, 0, 0, 10, 15, 0, 0, 0, 16], |
401 |
[0, 4, 8, 11, 0, 0, 10, 0, 0, 9, 0, 7, 0, 1, 0, 3], |
402 |
[9, 0, 5, 16, 2, 0, 0, 15, 0, 0, 8, 13, 10, 0, 0, 0], |
403 |
[0, 15, 0, 0, 0, 2, 0, 0, 0, 10, 0, 1, 4, 14, 6, 12], |
404 |
[0, 0, 12, 0, 5, 1, 0, 11, 14, 0, 0, 0, 8, 0, 7, 0], |
405 |
[0, 0, 0, 10, 0, 0, 6, 14, 0, 12, 0, 0, 0, 3, 0, 11], |
406 |
[13, 0, 7, 0, 0, 9, 0, 0, 0, 15, 0, 3, 0, 0, 16, 5], |
407 |
[15, 3, 0, 0, 11, 0, 12, 0, 0, 0, 1, 0, 0, 8, 0, 7], |
408 |
[5, 0, 10, 0, 0, 0, 1, 0, 7, 4, 0, 0, 15, 0, 0, 0], |
409 |
[0, 8, 0, 12, 0, 0, 0, 7, 16, 0, 11, 10, 0, 5, 0, 0], |
410 |
[7, 13, 16, 1, 3, 0, 5, 0, 0, 0, 15, 0, 0, 0, 11, 0], |
411 |
[0, 0, 0, 5, 9, 16, 0, 0, 10, 0, 0, 6, 11, 7, 0, 15], |
412 |
[10, 0, 15, 0, 8, 0, 2, 0, 0, 7, 0, 0, 14, 16, 1, 0], |
413 |
[8, 0, 0, 0, 7, 12, 0, 0, 9, 0, 5, 14, 0, 0, 0, 13], |
414 |
[14, 0, 0, 3, 0, 0, 0, 1, 0, 16, 13, 2, 0, 0, 0, 0] |
415 |
] |
416 |
""" |
417 |
|
418 |
""" |
419 |
sudoku = [ |
420 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0], |
421 |
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0], |
422 |
[0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
423 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
424 |
[0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 18, 0, 0, 0, 0, 0, 0], |
425 |
[0, 0, 0, 7, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
426 |
[0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0], |
427 |
[0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0], |
428 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
429 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
430 |
[0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 12, 0, 0, 0], |
431 |
[0, 0, 12, 0, 15, 0, 0, 0, 0, 0, 14, 0, 0, 0, 5, 0, 0, 24, 3, 0, 0, 0, 0, 0, 0], |
432 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 13, 0, 0, 5, 0, 0, 24, 3, 0], |
433 |
[0, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
434 |
[0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
435 |
[0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
436 |
[0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0], |
437 |
[0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
438 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
439 |
[0, 5, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1], |
440 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], |
441 |
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
442 |
[0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
443 |
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], |
444 |
[0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
445 |
] |
446 |
""" |
447 |
print("You entered the following sudoku:") |
448 |
print_sudoku(sudoku) |
449 |
|
450 |
# OPTIMIZATION |
451 |
print("Collecting possibility table...") |
452 |
possibilities = [] |
453 |
for x in range(len(sudoku)): |
454 |
row = [] |
455 |
for y in range(len(sudoku[x])): |
456 |
entries = (collect_possible_entries(sudoku, x, y)) |
457 |
row.append(entries) |
458 |
assert len(entries) != 0 |
459 |
possibilities.append(row) |
460 |
print("Possibility table complete!") |
461 |
|
462 |
if recursive_solution(sudoku, possibilities): |
463 |
#if test_solution(sudoku): |
464 |
print("Sudoku solved!") |
465 |
print_sudoku(sudoku) |
466 |
else: |
467 |
print("Failed!") |
468 |
print_sudoku(sudoku) |
469 |
|
470 |