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