from
PIL
import
ImageGrab, ImageOps, ImageEnhance
import
pytesseract
import
numpy as np
import
pyautogui
import
time
import
tkinter as tk
from
threading
import
Thread
from
functools
import
wraps
def
timer_decorator(func):
@wraps
(func)
def
wrapper(
*
args,
*
*
kwargs):
start_time
=
time.perf_counter()
result
=
func(
*
args,
*
*
kwargs)
end_time
=
time.perf_counter()
elapsed
=
end_time
-
start_time
print
(f
"[{func.__name__}] 执行时间: {elapsed:.4f} 秒"
)
return
result, elapsed
return
wrapper
@timer_decorator
def
capture_and_recognize():
left, top
=
403
,
300
right, bottom
=
937
,
833
width
=
right
-
left
height
=
bottom
-
top
screen
=
ImageGrab.grab(bbox
=
(left, top, right, bottom))
sudoku
=
np.zeros((
9
,
9
), dtype
=
int
)
cell_width
=
width
/
9
cell_height
=
height
/
9
for
row
in
range
(
9
):
for
col
in
range
(
9
):
box
=
(
col
*
cell_width
+
4
,
row
*
cell_height
+
4
,
(col
+
1
)
*
cell_width
-
4
,
(row
+
1
)
*
cell_height
-
4
,
)
cell
=
screen.crop(box)
cell
=
ImageOps.grayscale(cell)
cell
=
ImageEnhance.Contrast(cell).enhance(
2.0
)
cell
=
cell.point(
lambda
x:
0
if
x <
200
else
255
)
text
=
pytesseract.image_to_string(
cell,
config
=
"--psm 10 --oem 3 -c tessedit_char_whitelist=123456789 "
,
)
sudoku[row][col]
=
int
(text)
if
text.strip()
else
0
print
(sudoku)
return
sudoku
@timer_decorator
def
solve_sudoku(board):
def
is_valid(row, col, num):
for
i
in
range
(
9
):
if
board[row][i]
=
=
num
or
board[i][col]
=
=
num:
return
False
start_row, start_col
=
3
*
(row
/
/
3
),
3
*
(col
/
/
3
)
for
i
in
range
(
3
):
for
j
in
range
(
3
):
if
board[start_row
+
i][start_col
+
j]
=
=
num:
return
False
return
True
def
backtrack():
for
row
in
range
(
9
):
for
col
in
range
(
9
):
if
board[row][col]
=
=
0
:
for
num
in
range
(
1
,
10
):
if
is_valid(row, col, num):
board[row][col]
=
num
if
backtrack():
return
True
board[row][col]
=
0
return
False
return
True
backtrack()
return
board
def
auto_fill(original, solution):
base_x, base_y
=
403
,
300
cell_width
=
(
937
-
403
)
/
9
cell_height
=
(
833
-
300
)
/
9
for
row
in
range
(
9
):
for
col
in
range
(
9
):
if
original[row][col]
=
=
0
:
num
=
solution[row][col]
x
=
base_x
+
col
*
cell_width
+
cell_width
/
2
y
=
base_y
+
row
*
cell_height
+
cell_height
/
2
pyautogui.click(x, y)
pyautogui.typewrite(
str
(num))
time.sleep(
0.1
)
class
SudokuSolverGUI:
def
__init__(
self
):
self
.window
=
tk.Tk()
self
.window.title(
"Sudoku Solver"
)
self
.btn
=
tk.Button(
self
.window,
text
=
"Start"
,
command
=
self
.start_solving,
font
=
(
"Arial"
,
14
),
width
=
15
,
height
=
2
,
)
self
.btn.pack(padx
=
20
, pady
=
20
)
def
start_solving(
self
):
print
(
"Strart capture_and_recognize"
)
Thread(target
=
self
.solve_process).start()
def
solve_process(
self
):
original, recognize_time
=
capture_and_recognize()
solution, solve_time
=
solve_sudoku(original.copy())
print
(f
"\n性能统计:"
)
print
(f
"屏幕识别耗时: {recognize_time:.4f}s"
)
print
(f
"数独求解耗时: {solve_time:.4f}s"
)
print
(f
"总耗时: {recognize_time + solve_time:.4f}s\n\n"
)
auto_fill(original, solution)
def
run(
self
):
self
.window.mainloop()
if
__name__
=
=
"__main__"
:
gui
=
SudokuSolverGUI()
gui.run()