-
Notifications
You must be signed in to change notification settings - Fork 0
/
sudoku_generator.py
321 lines (279 loc) · 10.6 KB
/
sudoku_generator.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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import math, random, copy, pygame
from constants import *
from cell import *
class SudokuGenerator:
def __init__(self, row_length, removed_cells):
self.row_length = row_length
self.removed_cells = removed_cells
self.board = [[0 for j in range(self.row_length)] for i in range(self.row_length)]
self.board_empty = self.board
self.box_length = int(math.sqrt(self.row_length))
self.height = HEIGHT
self.width = WIDTH
self.screen = SCREEN
self.rows = row_length
self.cols = row_length
self.cells = [[Cell(self.board[i][j], i, j, self.height // self.rows,
self.width // self.cols) for j in range(row_length)] for i in range(row_length)]
def draw(self):
# draw lines
for i in range(1, 9):
if i % 3 == 0:
pygame.draw.line(self.screen, DARKER_LINE_COLOR, (0, SQUARE_SIZE * i),
(BOARD_WIDTH, SQUARE_SIZE * i), THICK_LINE_WIDTH)
else:
pygame.draw.line(self.screen, LINE_COLOR, (0, SQUARE_SIZE * i),
(BOARD_WIDTH, SQUARE_SIZE * i), LINE_WIDTH)
# draw vertical lines
for i in range(1, 9):
if i % 3 == 0:
pygame.draw.line(self.screen, DARKER_LINE_COLOR, (SQUARE_SIZE * i, 0),
(SQUARE_SIZE * i, BOARD_HEIGHT), THICK_LINE_WIDTH)
else:
pygame.draw.line(self.screen, LINE_COLOR, (SQUARE_SIZE * i, 0),
(SQUARE_SIZE * i, BOARD_HEIGHT), LINE_WIDTH)
for i in range(self.rows):
for j in range(self.cols):
self.cells[i][j].draw(self.screen)
def update_cells(self):
#updates cells when changes are made
self.cells = [[Cell(self.board[i][j], i, j, self.height // self.rows,
self.width // self.cols) for j in range(self.cols)] for i in range(self.rows)]
def mark_square(self, col, row, number):
#marks numbers in board
self.board[col][row] = number
self.update_cells()
def board_is_full(self):
#checks if board is full
for i in range(self.rows):
for j in range(self.cols):
if self.board[i][j] == 0:
return False
return True
def restart_board(self):
#resets the board to its original values
self.board =self.board_empty
self.update_cells()
def available_square(self, col, row):
#returns true if a square is available
return self.board[col][row] == 0
def get_board(self):
#returns board as list
return self.board
def print_board(self):
#prints board to console
the_board = self.get_board()
for item in the_board:
for i in item:
print(i, end=" ")
print()
def valid_in_row(self, row, num):
part = self.board[row]
for i in part:
if int(i) == int(num):
return False
return True
def valid_in_col(self, col, num):
col_values = []
for row in range(9):
col_values += [self.board[row][col]]
for i in col_values:
if int(i) == int(num):
return False
return True
def valid_in_box(self, row_start, col_start, num):
not_in_box = True
if row_start < 3 and row_start >= 0:
for row in range(0, 3):
if col_start < 3 and col_start >= 0:
for col in range(0, 3):
if self.board[row][col] == num:
not_in_box = False
elif col_start < 6:
for col in range(3, 6):
if self.board[row][col] == num:
not_in_box = False
elif col_start < 9:
for col in range(6, 9):
if self.board[row][col] == num:
not_in_box = False
else:
print("Error")
elif row_start < 6:
for row in range(3, 6):
if col_start < 3 and col_start >= 0:
for col in range(0, 3):
if self.board[row][col] == num:
not_in_box = False
elif col_start < 6:
for col in range(3, 6):
if self.board[row][col] == num:
not_in_box = False
elif col_start < 9:
for col in range(6, 9):
if self.board[row][col] == num:
not_in_box = False
else:
print("Error")
elif row_start < 9:
for row in range(6, 9):
if col_start < 3 and col_start >= 0:
for col in range(0, 3):
if self.board[row][col] == num:
not_in_box = False
elif col_start < 6:
for col in range(3, 6):
if self.board[row][col] == num:
not_in_box = False
elif col_start < 9:
for col in range(6, 9):
if self.board[row][col] == num:
not_in_box = False
else:
print("Error")
else:
print("Error")
return not_in_box
def is_valid(self, row, col, num):
not_in_row = self.valid_in_row(row, num)
not_in_col = self.valid_in_col(col, num)
not_in_box = self.valid_in_box(row, col, num)
if not_in_row == False:
return False
elif not_in_col == False:
return False
elif not_in_box == False:
return False
else:
return True
def fill_box(self, row_start, col_start):
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for row in range(3):
for col in range(3):
random.shuffle(nums)
num = nums.pop(0)
self.board[row_start + row][col_start + col] = num
def fill_diagonal(self):
self.fill_box(0, 0)
self.fill_box(3, 3)
self.fill_box(6, 6)
'''
Removes the appropriate number of cells from the board
This is done by setting some values to 0
Should be called after the entire solution has been constructed
i.e. after fill_values has been called
# NOTE: Be careful not to 'remove' the same cell multiple times
i.e. if a cell is already 0, it cannot be removed again
Parameters: None
Return: None
'''
def remove_cells(self):
row = 0
count = int(self.removed_cells)
while count > 0:
col_list = []
col = None
for i in range(9):
if self.board[row][i] != 0:
col_list += [i]
random.shuffle(col_list)
if len(col_list) > 0:
col = col_list[0]
else:
print("empty")
self.board[row][col] = 0
count -= 1
row += 1
if row == 9:
row = 0
def reset_board(self):
#resets board to original values
self.board = self.board_empty
self.update_cells()
'''
DO NOT CHANGE
Provided for students
Fills the remaining cells of the board
Should be called after the diagonal boxes have been filled
Parameters:
row, col specify the coordinates of the first empty (0) cell
Return:
boolean (whether or not we could solve the board)
'''
def fill_remaining(self, row, col):
if (col >= self.row_length and row < self.row_length - 1):
row += 1
col = 0
if row >= self.row_length and col >= self.row_length:
return True
if row < self.box_length:
if col < self.box_length:
col = self.box_length
elif row < self.row_length - self.box_length:
if col == int(row // self.box_length * self.box_length):
col += self.box_length
else:
if col == self.row_length - self.box_length:
row += 1
col = 0
if row >= self.row_length:
return True
for num in range(1, self.row_length + 1):
if self.is_valid(row, col, num):
self.board[row][col] = num
if self.fill_remaining(row, col + 1):
return True
self.board[row][col] = 0
return False
'''
DO NOT CHANGE
Provided for students
Constructs a solution by calling fill_diagonal and fill_remaining
Parameters: None
Return: None
'''
def fill_values(self):
self.fill_diagonal()
self.fill_remaining(0, self.box_length)
'''
DO NOT CHANGE
Provided for students
Given a number of rows and number of cells to remove, this function:
1. creates a SudokuGenerator
2. fills its values and saves this as the solved state
3. removes the appropriate number of cells
4. returns the representative 2D Python Lists of the board and solution
Parameters:
size is the number of rows/columns of the board (9 for this project)
removed is the number of cells to clear (set to 0)
Return: list[list] (a 2D Python list to represent the board)
'''
def generate_sudoku(size, removed):
sudoku = SudokuGenerator(size, removed)
sudoku.fill_values()
board = sudoku.get_board()
sudoku.remove_cells()
board = sudoku.get_board()
return board
def generate_sudo(size, removed):
#initializes a sudoku object
sudoku = SudokuGenerator(size, removed)
sudoku.fill_values()
board = copy.deepcopy(sudoku.get_board())
sudoku.remove_cells()
second_board = sudoku.get_board()
list_of_boards = [board, second_board]
return list_of_boards
# The bit of code that actually runs everthing else
if __name__ == '__main__':
x = generate_sudo(9, 30)
print("Answer Key:")
for i in x[0]:
for j in i:
print(j, end=' ')
print()
print("Game Board:")
for i in x[1]:
for j in i:
print(j, end=' ')
print()