1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ from sudokutools import valid , solve , find_empty
4
+ from copy import deepcopy
5
+ from sys import exit
6
+ import pygame
7
+ import time
8
+ import random
9
+ pygame .init ()
10
+
11
+
12
+ def generate ():
13
+
14
+ while True :
15
+ for event in pygame .event .get ():
16
+ if event .type == pygame .QUIT :
17
+ exit ()
18
+ board = [[0 for i in range (9 )] for j in range (9 )]
19
+
20
+ for i in range (9 ):
21
+ for j in range (9 ):
22
+ if random .randint (1 , 10 ) >= 5 :
23
+ board [i ][j ] = random .randint (1 , 9 )
24
+ if valid (board , (i , j ), board [i ][j ]):
25
+ continue
26
+ else :
27
+ board [i ][j ] = 0
28
+ partialBoard = deepcopy (board )
29
+ if solve (board ):
30
+ return partialBoard
31
+
32
+
33
+ class Board :
34
+
35
+ def __init__ (self , window ):
36
+ self .board = generate ()
37
+ self .solvedBoard = deepcopy (self .board )
38
+ solve (self .solvedBoard )
39
+ self .tiles = [[Tile (self .board [i ][j ], window , i * 60 , j * 60 )
40
+ for j in range (9 )] for i in range (9 )]
41
+ self .window = window
42
+
43
+ def draw_board (self ):
44
+
45
+ for i in range (9 ):
46
+ for j in range (9 ):
47
+ if j % 3 == 0 and j != 0 :
48
+ pygame .draw .line (self .window , (0 , 0 , 0 ), (j // 3
49
+ * 180 , 0 ), (j // 3 * 180 , 540 ), 4 )
50
+
51
+ if i % 3 == 0 and i != 0 :
52
+ pygame .draw .line (self .window , (0 , 0 , 0 ), (0 , i // 3
53
+ * 180 ), (540 , i // 3 * 180 ), 4 )
54
+
55
+ self .tiles [i ][j ].draw ((0 , 0 , 0 ), 1 )
56
+
57
+ if self .tiles [i ][j ].value != 0 :
58
+ self .tiles [i ][j ].display (self .tiles [i ][j ].value ,
59
+ (21 + j * 60 , 16 + i * 60 ), (0 , 0 , 0 ))
60
+
61
+
62
+ pygame .draw .line (self .window , (0 , 0 , 0 ), (0 , (i + 1 ) // 3
63
+ * 180 ), (540 , (i + 1 ) // 3 * 180 ), 4 )
64
+
65
+ def deselect (self , tile ):
66
+
67
+ for i in range (9 ):
68
+ for j in range (9 ):
69
+ if self .tiles [i ][j ] != tile :
70
+ self .tiles [i ][j ].selected = False
71
+
72
+ def redraw (
73
+ self ,
74
+ keys ,
75
+ wrong ,
76
+ time ,
77
+ ):
78
+
79
+ self .window .fill ((255 , 255 , 255 ))
80
+ self .draw_board ()
81
+ for i in range (9 ):
82
+ for j in range (9 ):
83
+ if self .tiles [j ][i ].selected :
84
+ self .tiles [j ][i ].draw ((50 , 205 , 50 ), 4 )
85
+ elif self .tiles [i ][j ].correct :
86
+
87
+ self .tiles [j ][i ].draw ((34 , 139 , 34 ), 4 )
88
+ elif self .tiles [i ][j ].incorrect :
89
+
90
+ self .tiles [j ][i ].draw ((255 , 0 , 0 ), 4 )
91
+
92
+ if len (keys ) != 0 :
93
+ for value in keys :
94
+ self .tiles [value [0 ]][value [1 ]].display (keys [value ], (21
95
+ + value [0 ] * 60 , 16 + value [1 ] * 60 ), (128 ,
96
+ 128 , 128 ))
97
+
98
+ if wrong > 0 :
99
+ font = pygame .font .SysFont ('Bauhaus 93' , 30 )
100
+ text = font .render ('X' , True , (255 , 0 , 0 ))
101
+ self .window .blit (text , (10 , 554 ))
102
+
103
+ font = pygame .font .SysFont ('Bahnschrift' , 40 )
104
+ text = font .render (str (wrong ), True , (0 , 0 , 0 ))
105
+ self .window .blit (text , (32 , 542 ))
106
+
107
+ font = pygame .font .SysFont ('Bahnschrift' , 40 )
108
+ text = font .render (str (time ), True , (0 , 0 , 0 ))
109
+ self .window .blit (text , (388 , 542 ))
110
+ pygame .display .flip ()
111
+
112
+ def visualSolve (self , wrong , time ):
113
+
114
+ for event in pygame .event .get ():
115
+ if event .type == pygame .QUIT :
116
+ exit ()
117
+
118
+ empty = find_empty (self .board )
119
+ if not empty :
120
+ return True
121
+
122
+ for nums in range (9 ):
123
+ if valid (self .board , (empty [0 ], empty [1 ]), nums + 1 ):
124
+ self .board [empty [0 ]][empty [1 ]] = nums + 1
125
+ self .tiles [empty [0 ]][empty [1 ]].value = nums + 1
126
+ self .tiles [empty [0 ]][empty [1 ]].correct = True
127
+ pygame .time .delay (63 )
128
+ self .redraw ({}, wrong , time )
129
+
130
+ if self .visualSolve (wrong , time ):
131
+ return True
132
+
133
+ self .board [empty [0 ]][empty [1 ]] = 0
134
+ self .tiles [empty [0 ]][empty [1 ]].value = 0
135
+ self .tiles [empty [0 ]][empty [1 ]].incorrect = True
136
+ self .tiles [empty [0 ]][empty [1 ]].correct = False
137
+ pygame .time .delay (63 )
138
+ self .redraw ({}, wrong , time )
139
+
140
+ def hint (self , keys ):
141
+
142
+ while True :
143
+ i = random .randint (0 , 8 )
144
+ j = random .randint (0 , 8 )
145
+ if self .board [i ][j ] == 0 :
146
+ if (j , i ) in keys :
147
+ del keys [(j , i )]
148
+ self .board [i ][j ] = self .solvedBoard [i ][j ]
149
+ self .tiles [i ][j ].value = self .solvedBoard [i ][j ]
150
+ return True
151
+ elif self .board == self .solvedBoard :
152
+
153
+ return False
154
+
155
+
156
+ class Tile :
157
+
158
+
159
+ def __init__ (
160
+ self ,
161
+ value ,
162
+ window ,
163
+ x1 ,
164
+ y1 ,
165
+ ):
166
+ self .value = value
167
+ self .window = window
168
+ self .rect = pygame .Rect (x1 , y1 , 60 , 60 )
169
+ self .selected = False
170
+ self .correct = False
171
+ self .incorrect = False
172
+
173
+ def draw (self , color , thickness ):
174
+
175
+ pygame .draw .rect (self .window , color , self .rect , thickness )
176
+
177
+ def display (
178
+ self ,
179
+ value ,
180
+ position ,
181
+ color ,
182
+ ):
183
+
184
+ font = pygame .font .SysFont ('lato' , 45 )
185
+ text = font .render (str (value ), True , color )
186
+ self .window .blit (text , position )
187
+
188
+ def clicked (self , mousePos ):
189
+
190
+ if self .rect .collidepoint (mousePos ):
191
+ self .selected = True
192
+ return self .selected
193
+
194
+
195
+ def main ():
196
+
197
+ screen = pygame .display .set_mode ((540 , 590 ))
198
+ screen .fill ((255 , 255 , 255 ))
199
+ pygame .display .set_caption ('Sudoku Solver' )
200
+ icon = pygame .image .load ('assets/thumbnail.png' )
201
+ pygame .display .set_icon (icon )
202
+
203
+ font = pygame .font .SysFont ('Bahnschrift' , 40 )
204
+ text = font .render ('Generating' , True , (0 , 0 , 0 ))
205
+ screen .blit (text , (175 , 245 ))
206
+
207
+ font = pygame .font .SysFont ('Bahnschrift' , 40 )
208
+ text = font .render ('Random Grid' , True , (0 , 0 , 0 ))
209
+ screen .blit (text , (156 , 290 ))
210
+ pygame .display .flip ()
211
+
212
+ wrong = 0
213
+ board = Board (screen )
214
+ selected = (- 1 , - 1 )
215
+ keyDict = {}
216
+ running = True
217
+ startTime = time .time ()
218
+ while running :
219
+ elapsed = time .time () - startTime
220
+ passedTime = time .strftime ('%H:%M:%S' , time .gmtime (elapsed ))
221
+
222
+ if board .board == board .solvedBoard :
223
+ for i in range (9 ):
224
+ for j in range (9 ):
225
+ board .tiles [i ][j ].selected = False
226
+ running = False
227
+
228
+ for event in pygame .event .get ():
229
+ if event .type == pygame .QUIT :
230
+ exit ()
231
+ elif event .type == pygame .MOUSEBUTTONUP :
232
+
233
+ mousePos = pygame .mouse .get_pos ()
234
+ for i in range (9 ):
235
+ for j in range (9 ):
236
+ if board .tiles [i ][j ].clicked (mousePos ):
237
+ selected = (i , j )
238
+ board .deselect (board .tiles [i ][j ])
239
+ elif event .type == pygame .KEYDOWN :
240
+
241
+ if board .board [selected [1 ]][selected [0 ]] == 0 \
242
+ and selected != (- 1 , - 1 ):
243
+ if event .key == pygame .K_1 :
244
+ keyDict [selected ] = 1
245
+
246
+ if event .key == pygame .K_2 :
247
+ keyDict [selected ] = 2
248
+
249
+ if event .key == pygame .K_3 :
250
+ keyDict [selected ] = 3
251
+
252
+ if event .key == pygame .K_4 :
253
+ keyDict [selected ] = 4
254
+
255
+ if event .key == pygame .K_5 :
256
+ keyDict [selected ] = 5
257
+
258
+ if event .key == pygame .K_6 :
259
+ keyDict [selected ] = 6
260
+
261
+ if event .key == pygame .K_7 :
262
+ keyDict [selected ] = 7
263
+
264
+ if event .key == pygame .K_8 :
265
+ keyDict [selected ] = 8
266
+
267
+ if event .key == pygame .K_9 :
268
+ keyDict [selected ] = 9
269
+ elif event .key == pygame .K_BACKSPACE or event .key \
270
+ == pygame .K_DELETE :
271
+
272
+ if selected in keyDict :
273
+ board .tiles [selected [1 ]][selected [0 ]].value = \
274
+ 0
275
+ del keyDict [selected ]
276
+ elif event .key == pygame .K_RETURN :
277
+
278
+ if selected in keyDict :
279
+ if keyDict [selected ] \
280
+ != board .solvedBoard [selected [1 ]][selected [0 ]]:
281
+ wrong += 1
282
+ board .tiles [selected [1 ]][selected [0 ]].value = \
283
+ 0
284
+ del keyDict [selected ]
285
+ break
286
+
287
+ board .tiles [selected [1 ]][selected [0 ]].value = \
288
+ keyDict [selected ]
289
+ board .board [selected [1 ]][selected [0 ]] = \
290
+ keyDict [selected ]
291
+ del keyDict [selected ]
292
+
293
+ if event .key == pygame .K_h :
294
+ board .hint (keyDict )
295
+
296
+ if event .key == pygame .K_SPACE :
297
+ for i in range (9 ):
298
+ for j in range (9 ):
299
+ board .tiles [i ][j ].selected = False
300
+ keyDict = {}
301
+ board .visualSolve (wrong , passedTime )
302
+ for i in range (9 ):
303
+ for j in range (9 ):
304
+ board .tiles [i ][j ].correct = False
305
+ board .tiles [i ][j ].incorrect = False
306
+ running = False
307
+
308
+ board .redraw (keyDict , wrong , passedTime )
309
+ while True :
310
+ for event in pygame .event .get ():
311
+ if event .type == pygame .QUIT :
312
+ return
313
+
314
+
315
+ main ()
316
+ pygame .quit ()
0 commit comments