And there we go, took an example from a website and lo and behold, my sudoku solver freaking works. I'll get to some cleanup later today :D
- Author
- Vngngdn
- Date
- July 20, 2016, 12:58 p.m.
- Hash
- 306409c3d126fdff16a950a002086c145771267a
- Parent
- e5efc74698b5f3d9409cb8a5e64528ef649b0d1c
- Modified file
- sudoku-solver.py
sudoku-solver.py ¶
73 additions and 26 deletions.
View changes Hide changes
1 |
1 |
sudoku-solver.py - A simple program that can solve any (solvable) sudoku puzzle. |
2 |
2 |
Copyright 2016 Maarten 'Vngngdn' Vangeneugden |
3 |
3 |
|
4 |
4 |
Licensed under the Apache License, Version 2.0 (the "License"); |
5 |
5 |
you may not use this file except in compliance with the License. |
6 |
6 |
You may obtain a copy of the License at |
7 |
7 |
|
8 |
8 |
https://www.apache.org/licenses/LICENSE-2.0 |
9 |
9 |
|
10 |
10 |
Unless required by applicable law or agreed to in writing, software |
11 |
11 |
distributed under the License is distributed on an "AS IS" BASIS, |
12 |
12 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
13 |
See the License for the specific language governing permissions and |
14 |
14 |
limitations under the License. |
15 |
15 |
""" |
16 |
16 |
|
17 |
17 |
# I don't think I need to explain this, do I? Although I will explain how the |
18 |
18 |
# program works: |
19 |
19 |
# One is supposed to give an array of arrays, which will each contain a series |
20 |
20 |
# of integer values in range [1,9]. Some of them may be 0, these will then stand |
21 |
21 |
# for an empty value. |
22 |
22 |
# This program's goal is to remove all 0's (blank spaces) and fill them so, that |
23 |
23 |
# the sudoku is considered solved. After that, it prints the sudoku to the |
24 |
24 |
# terminal. |
25 |
25 |
|
26 |
26 |
# Prints the given sudoku to the terminal in a readable way: |
27 |
27 |
def print_sudoku(sudoku): |
28 |
28 |
print(sudoku) |
29 |
- | """for x in sudoku: |
30 |
- | print(sudoku[x]) |
31 |
- | for y in x: |
+ |
29 |
#print(sudoku[x]) |
+ |
30 |
for y in x: |
32 |
31 |
pass |
33 |
- | #print(str(sudoku[x][y]) + " ", end="") |
+ |
32 |
#print(str(sudoku[x][y]) + " ", end="") |
34 |
33 |
print() # Start a new line. |
35 |
34 |
""" |
36 |
- | |
37 |
35 |
# Given an empty sudoku, the solution is very simple. |
38 |
36 |
def solve_empty_sudoku(sudoku): |
39 |
37 |
n = 3 |
40 |
38 |
for i in range(0, n*n): |
41 |
39 |
for j in range(0, n*n): |
42 |
40 |
sudoku[i][j] = (i*n + i/n + j) % (n*n) + 1; |
43 |
41 |
return sudoku |
44 |
42 |
|
45 |
43 |
# Creates a table of 9x9 in the form of an array containing arrays. |
46 |
44 |
def create_sudoku(): |
47 |
45 |
x = [] |
48 |
46 |
for i in range(0,9): |
49 |
47 |
x.append([0, 0, 0, 0, 0, 0, 0, 0, 0]) |
50 |
48 |
return x |
51 |
49 |
|
52 |
50 |
EMPTY = 0 |
53 |
51 |
|
54 |
52 |
# Checks if the given sudoku qualifies as a root solution. |
55 |
53 |
def is_possible_root_solution(sudoku): |
56 |
54 |
root_solution = solve_empty_sudoku() |
57 |
55 |
for i in range(0, len(root_solution)): |
58 |
56 |
for j in range(0, len(root_solution[i])): |
59 |
57 |
if sudoku[i][j] != root_solution[i][j] and sudoku[i][j] != 0: |
60 |
58 |
return False |
61 |
59 |
# When here, all the sudoku can be filled as a root solution. |
62 |
60 |
return True |
63 |
61 |
|
64 |
62 |
# Checks whether the given number already exists in the given array. |
65 |
63 |
def exists_in_array(x, y, sudoku): |
66 |
- | value = sudoku[x][y] |
67 |
- | for i in range(len(sudoku)): |
68 |
- | if sudoku[i][y] == value and i != x and sudoku[i][y] != 0: |
69 |
- | return True |
+ |
64 |
#value = sudoku[x][y] |
+ |
65 |
for i in range(0, len(sudoku)): |
+ |
66 |
if sudoku[i][y] == value and sudoku[i][y] != EMPTY: |
+ |
67 |
return True |
70 |
68 |
return False |
71 |
69 |
|
72 |
70 |
# Checks whether the number on the given location is unique in its column. |
73 |
71 |
def exists_in_column(x, y, sudoku): |
74 |
- | value = sudoku[x][y] |
75 |
- | for i in range(0, len(sudoku[x])): |
+ |
72 |
#value = sudoku[x][y] |
+ |
73 |
for i in range(0, len(sudoku[x])): |
76 |
74 |
if sudoku[x][i] == value and i != y and sudoku[x][i] != 0: |
77 |
- | return True |
+ |
75 |
return True |
78 |
76 |
return False |
79 |
77 |
|
80 |
78 |
# Checks whether the number on the given location is unique in its (3x3) grid. |
81 |
79 |
def exists_in_grid(x, y, sudoku): |
82 |
- | value = sudoku[x][y] |
83 |
- | # We're going to find out now in which part of the grid the (x,y) is put. |
+ |
80 |
#value = sudoku[x][y] |
+ |
81 |
# We're going to find out now in which part of the grid the (x,y) is put. |
84 |
82 |
# We'll increment x and y by 1 in order to have some 1-indexed math: |
85 |
83 |
|
86 |
84 |
# The idea I'm going for now: |
87 |
85 |
# I'll first try to find out in which segment of the sudoku the value is in. |
88 |
86 |
# When I've found it, I trim the data to that segment, after I'll be |
89 |
87 |
# checking on that segment only. |
90 |
88 |
# Chart: |
91 |
89 |
""" |
92 |
90 |
+-----+ |
93 |
91 |
|A B C| |
94 |
92 |
|D E F| |
95 |
93 |
|G H I| |
96 |
94 |
+-----+ |
97 |
95 |
""" |
98 |
96 |
n = 3 |
99 |
97 |
|
100 |
98 |
|
101 |
99 |
A = x%n |
102 |
100 |
B = y%n |
103 |
101 |
for i in range(0, n): |
104 |
102 |
for j in range(0, n): |
105 |
103 |
C = x - A + i |
106 |
104 |
D = y - B + j |
107 |
105 |
if sudoku[C][D] == value and (C != x and D != y): |
108 |
106 |
return True |
109 |
107 |
return False |
110 |
108 |
|
111 |
109 |
def is_filled_sudoku(sudoku): |
112 |
110 |
for i in range(0, len(sudoku)): |
113 |
111 |
for j in range(0, len(sudoku[i])): |
114 |
112 |
if sudoku[i][j] == 0: |
115 |
113 |
return False |
116 |
114 |
return True |
117 |
115 |
|
118 |
116 |
def find_first_empty_grid(sudoku): |
119 |
117 |
for i in range(0, len(sudoku)): |
120 |
118 |
for j in range(0, len(sudoku[i])): |
121 |
119 |
if sudoku[i][j] == 0: |
122 |
120 |
return i, j |
123 |
121 |
|
124 |
122 |
def is_valid_assignment(x, y, value, sudoku): |
125 |
123 |
sudoku[x][y] = value |
126 |
- | if exists_in_array(x, y, sudoku): |
127 |
- | print("Exists in array!") |
128 |
- | return False |
+ |
124 |
if exists_in_array(x, y, value, sudoku): |
+ |
125 |
#print("Exists in array!") |
+ |
126 |
return False |
129 |
127 |
if exists_in_column(x, y, sudoku): |
130 |
- | print("Exists in column!") |
131 |
- | return False |
+ |
128 |
#print("Exists in column!") |
+ |
129 |
return False |
132 |
130 |
if exists_in_grid(x, y, sudoku): |
133 |
- | print("Exists in grid!") |
134 |
- | return False |
+ |
131 |
#print("Exists in grid!") |
+ |
132 |
return False |
135 |
133 |
return True |
136 |
134 |
|
137 |
135 |
# Makes a deep copy of the given sudoku, and returns that copy. |
138 |
136 |
def make_copy(sudoku): |
139 |
137 |
newSudoku = [] |
140 |
138 |
for array in sudoku: |
141 |
139 |
newSudoku.append(array.copy()) |
142 |
140 |
return newSudoku |
143 |
141 |
|
144 |
142 |
|
145 |
143 |
sudokuA = create_sudoku() |
146 |
144 |
|
147 |
145 |
def recursive_solution(sudoku): |
148 |
146 |
if is_filled_sudoku(sudoku): |
149 |
147 |
return True # The sudoku is solved. |
150 |
148 |
else: |
151 |
149 |
x, y = find_first_empty_grid(sudoku) |
152 |
150 |
print(str(x) + "-" + str(y)) |
153 |
- | for i in range(1, 10): |
+ |
151 |
for i in range(1, 10): |
154 |
152 |
# We're going to fill in numbers from 1 to 9, to find a solution |
155 |
153 |
# that works. |
156 |
154 |
sudoku[x][y] = i |
157 |
- | if is_valid_assignment(x, y, i, sudoku): |
158 |
155 |
oldSudoku = make_copy(sudoku) |
159 |
- | if recursive_solution(sudoku) is False: |
+ |
156 |
#oldSudoku = make_copy(sudoku) |
+ |
157 |
if recursive_solution(sudoku) is False: |
160 |
158 |
sudoku = oldSudoku |
161 |
- | else: |
+ |
159 |
else: |
162 |
160 |
return True |
163 |
161 |
return False |
164 |
162 |
|
165 |
163 |
# MAIN |
+ |
164 |
discovered = [] |
+ |
165 |
# Rows: |
+ |
166 |
for column in sudoku: |
+ |
167 |
discovered.clear() |
+ |
168 |
for loc in column: |
+ |
169 |
if loc in discovered: |
+ |
170 |
return False |
+ |
171 |
else: |
+ |
172 |
discovered.append(loc) |
+ |
173 |
|
+ |
174 |
#Columns: |
+ |
175 |
for i in range(0, len(sudoku)): |
+ |
176 |
discovered.clear() |
+ |
177 |
for y in range(0, len(sudoku[i])): |
+ |
178 |
if y in discovered: |
+ |
179 |
return False |
+ |
180 |
else: |
+ |
181 |
discovered.append(y) |
+ |
182 |
|
+ |
183 |
discovered.clear() |
+ |
184 |
|
+ |
185 |
#Grids: |
+ |
186 |
for k in [0, 3, 6]: |
+ |
187 |
discovered.clear() |
+ |
188 |
for x in range(0,3): |
+ |
189 |
for y in range(0,3): |
+ |
190 |
if sudoku[x+k][y+k] in discovered: |
+ |
191 |
return False |
+ |
192 |
else: |
+ |
193 |
discovered.append(sudoku[x+k][y+k]) |
+ |
194 |
|
+ |
195 |
return True |
+ |
196 |
|
+ |
197 |
|
+ |
198 |
|
+ |
199 |
# MAIN |
166 |
200 |
|
+ |
201 |
[8, 4, 0, 0, 0, 0, 0, 0, 0], |
+ |
202 |
[0, 0, 5, 7, 0, 1, 0, 3, 4], |
+ |
203 |
[9, 0, 7, 0, 0, 0, 0, 0, 0], |
+ |
204 |
[5, 9, 0, 2, 0, 0, 0, 0, 0], |
+ |
205 |
[0, 0, 0, 3, 8, 6, 0, 0, 0], |
+ |
206 |
[0, 0, 0, 0, 0, 4, 0, 8, 2], |
+ |
207 |
[0, 0, 0, 0, 0, 0, 3, 0, 1], |
+ |
208 |
[7, 3, 0, 5, 0, 9, 6, 0, 0], |
+ |
209 |
[0, 0, 0, 0, 0, 0, 0, 7, 5] |
+ |
210 |
] |
+ |
211 |
|
167 |
212 |
if recursive_solution(sudokuA): |
168 |
213 |
print_sudoku(sudokuA) |
169 |
- | else: |
+ |
214 |
print_sudoku(sudokuA) |
+ |
215 |
else: |
170 |
216 |
print("Failed!") |
171 |
217 |
print_sudoku(sudokuA) |
172 |
218 |
|
173 |
219 |