|
|
|
|
"""
|
|
|
|
|
@Author: packy945
|
|
|
|
|
@FileName: sudoku.py
|
|
|
|
|
@DateTime: 2023/4/27 17:41
|
|
|
|
|
@SoftWare: PyCharm
|
|
|
|
|
"""
|
|
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
from data import *
|
|
|
|
|
from UI import *
|
|
|
|
|
|
|
|
|
|
def game_menu(menu:tk.Menu):
|
|
|
|
|
"""游戏菜单显示"""
|
|
|
|
|
menu_node = Menu(menu, tearoff=False)
|
|
|
|
|
menu_node.add_command(label="重新开始", command=lambda: restart(N))
|
|
|
|
|
menu_node.add_command(label="暂停计时", command=lambda: stop_time())
|
|
|
|
|
menu_node.add_command(label="载入题目", command=lambda: load_game())
|
|
|
|
|
# 在主目录菜单上新增"菜单"选项,并通过menu参数与下拉菜单绑定
|
|
|
|
|
menu.add_cascade(label="游戏设置", menu=menu_node)
|
|
|
|
|
|
|
|
|
|
def auto_menu(menu:tk.Menu):
|
|
|
|
|
"""自动求解菜单显示"""
|
|
|
|
|
menu_node = Menu(menu, tearoff=False)
|
|
|
|
|
menu_node.add_command(label="自动求解-顺序求解", command=lambda: [init_u_M(), auto_Solve(u_martix, STEP.copy())])
|
|
|
|
|
menu_node.add_command(label="自动求解-优化", command=lambda: [init_u_M(), auto_Solve_new(u_martix, STEP.copy())])
|
|
|
|
|
fmenu = Menu(menu, tearoff=False)
|
|
|
|
|
fmenu.add_command(label='低速', command=lambda: speed_set(1))
|
|
|
|
|
fmenu.add_command(label='中速', command=lambda: speed_set(0.5))
|
|
|
|
|
fmenu.add_command(label='高速', command=lambda: speed_set(0.2))
|
|
|
|
|
menu_node.add_cascade(label="求解速度选择", menu=fmenu)
|
|
|
|
|
# 在主目录菜单上新增"菜单"选项,并通过menu参数与下拉菜单绑定
|
|
|
|
|
menu.add_cascade(label="自动求解", menu=menu_node)
|
|
|
|
|
|
|
|
|
|
main_menu = tk.Menu(root)
|
|
|
|
|
root.config(menu=main_menu)
|
|
|
|
|
game_menu(main_menu)
|
|
|
|
|
auto_menu(main_menu)
|
|
|
|
|
martix_position_mapping = creat_martix_mapping(side)
|
|
|
|
|
|
|
|
|
|
def speed_set(spd):
|
|
|
|
|
global wait_time
|
|
|
|
|
wait_time = spd
|
|
|
|
|
|
|
|
|
|
def stop_time():
|
|
|
|
|
global time1
|
|
|
|
|
time1.stop()
|
|
|
|
|
|
|
|
|
|
def init_u_M():
|
|
|
|
|
global martix, u_martix, step, STEP, seq
|
|
|
|
|
u_martix = martix.copy()
|
|
|
|
|
step = [] # 操作步骤
|
|
|
|
|
STEP = [[0, (0, 0), 0, martix]] # 记录每一步的状态
|
|
|
|
|
seq = 1 # 全局操作次序初始化
|
|
|
|
|
|
|
|
|
|
def restart(N):
|
|
|
|
|
'''
|
|
|
|
|
重新生成一个游戏
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
global martix, u_martix, time1, show_possible, loca_o, loca, note_mark, step, STEP, seq
|
|
|
|
|
time1.restart()
|
|
|
|
|
martix = InitMartix(N) # 生成一个数独数组
|
|
|
|
|
u_martix = martix.copy() # 复制用户martix数组
|
|
|
|
|
show_possible = 0 # 是否显示提示
|
|
|
|
|
p = []# 初始化可能值数组
|
|
|
|
|
possible = []
|
|
|
|
|
for i in range(9):
|
|
|
|
|
p.append({}.copy())
|
|
|
|
|
for i in range(9):
|
|
|
|
|
possible.append(p.copy())
|
|
|
|
|
loca_o = tuple() # martix 数组上次滞留坐标
|
|
|
|
|
loca = tuple()# martix 数组当前坐标
|
|
|
|
|
note_mark = False # 笔记开启标志
|
|
|
|
|
show_Time(time1)# 显示时间信息
|
|
|
|
|
step = [] # 操作步骤
|
|
|
|
|
STEP = [[0, (0, 0), 0, martix]] # 记录每一步的状态
|
|
|
|
|
seq = 1 # 全局操作次序初始化
|
|
|
|
|
draw(loca, martix, u_martix, STEP)
|
|
|
|
|
|
|
|
|
|
def auto_Solve(u_M, STEP):
|
|
|
|
|
'''
|
|
|
|
|
自动解题算法(按顺序求解)
|
|
|
|
|
:param u_M:待解答的数独矩阵
|
|
|
|
|
:return:求解完毕的矩阵
|
|
|
|
|
'''
|
|
|
|
|
for row in range(9):
|
|
|
|
|
for col in range(9):
|
|
|
|
|
if u_M[row, col] == 0:
|
|
|
|
|
possible = GetPossible(u_M, row, col) # 计算所有的可能的数字
|
|
|
|
|
for value in possible:#尝试所有可能数字
|
|
|
|
|
u_M[row, col] = value # 将可能的数组填入
|
|
|
|
|
seq = STEP[-1][0] + 1 # 记录顺序
|
|
|
|
|
step = [seq, (row + 1, col + 1), value, u_M.copy()] # 记录一步
|
|
|
|
|
STEP.append(step)#将操作记录加入Step中
|
|
|
|
|
draw((row + 1, col + 1), martix, u_M, STEP)#更新显示界面
|
|
|
|
|
time.sleep(wait_time) # 防止运算过快,减慢演算步骤
|
|
|
|
|
if auto_Solve(u_M, STEP.copy()): # 继续深度优先遍历填入数字
|
|
|
|
|
return True # 求解完成,直接返回
|
|
|
|
|
u_M[row, col] = 0 # 若用这个数无解,则重置为0,进行回溯
|
|
|
|
|
draw((row + 1, col + 1), martix, u_M, STEP)#更新显示界面
|
|
|
|
|
time.sleep(wait_time) # 停顿一段时间,显示求解步骤
|
|
|
|
|
return False #当前无解,返回False
|
|
|
|
|
return True# 当所有的数字填完,数独求解完毕
|
|
|
|
|
|
|
|
|
|
def auto_Solve_new(u_M, STEP):
|
|
|
|
|
'''
|
|
|
|
|
自动求解(优化)
|
|
|
|
|
:param u_M:待求解数独矩阵
|
|
|
|
|
:return:求解完成的矩阵
|
|
|
|
|
'''
|
|
|
|
|
flag = 0
|
|
|
|
|
# 优化:如果某个位置只有一个可能值,则先填这个格子
|
|
|
|
|
for row in range(9):
|
|
|
|
|
for col in range(9):
|
|
|
|
|
if u_M[row, col] == 0:
|
|
|
|
|
possible = GetPossible(u_M, row, col) # 所有的可能的数字
|
|
|
|
|
if len(possible) == 1:
|
|
|
|
|
flag = 1#标记可以进行求解优化
|
|
|
|
|
value = possible.pop()#只有一个可能数组,则直接将该数字填入
|
|
|
|
|
u_M[row, col] = value # 将确定的数字填入填入
|
|
|
|
|
seq = STEP[-1][0] + 1 # 记录顺序
|
|
|
|
|
step = [seq, (row + 1, col + 1), value, u_M.copy()] # 记录一步
|
|
|
|
|
STEP.append(step)#将步骤记录加到记录中
|
|
|
|
|
draw((row + 1, col + 1), martix, u_M, STEP) #重新绘图
|
|
|
|
|
time.sleep(wait_time) # 停顿一段时间,显示求解步骤
|
|
|
|
|
if auto_Solve_new(u_M, STEP): # 继续深度优先遍历填入数字
|
|
|
|
|
return True # 填完最后一个数字
|
|
|
|
|
u_M[row, col] = 0 # 如果当前填入的数字会导致后面无解则依然填入0表示空白待填
|
|
|
|
|
draw((row + 1, col + 1), martix, u_M, STEP)#重新绘图
|
|
|
|
|
time.sleep(wait_time) # 停顿一段时间,显示求解步骤
|
|
|
|
|
if not flag:#若没有能直接确定的格子,则按原方法进行求解
|
|
|
|
|
for row in range(9):
|
|
|
|
|
for col in range(9):
|
|
|
|
|
if u_M[row, col] == 0:
|
|
|
|
|
possible = GetPossible(u_M, row, col) # 所有的可能的数字
|
|
|
|
|
for value in possible:
|
|
|
|
|
u_M[row, col] = value # 将可能的数组填入
|
|
|
|
|
seq = STEP[-1][0] + 1 # 记录顺序
|
|
|
|
|
step = [seq, (row + 1, col + 1), value, u_M.copy()] # 记录一步
|
|
|
|
|
STEP.append(step)
|
|
|
|
|
draw((row + 1, col + 1), martix, u_M, STEP)
|
|
|
|
|
time.sleep(wait_time) # 停顿一段时间,显示求解步骤
|
|
|
|
|
if auto_Solve_new(u_M, STEP): # 继续深度优先遍历填入数字
|
|
|
|
|
return True # 填完最后一个数字
|
|
|
|
|
u_M[row, col] = 0 # 如果当前填入的数字会导致后面无解则依然填入0表示空白待填
|
|
|
|
|
draw((row + 1, col + 1), martix, u_M, STEP)
|
|
|
|
|
time.sleep(wait_time) # 停顿一段时间,显示求解步骤
|
|
|
|
|
return False
|
|
|
|
|
return True# 当所有的数字填完,数独求解完毕
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_game():
|
|
|
|
|
'''
|
|
|
|
|
载入存储的游戏
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
global martix, u_martix, time1, show_possible, loca_o, loca, note_mark, step, STEP, seq
|
|
|
|
|
time1.restart()
|
|
|
|
|
martix = np.load("martix.npy")
|
|
|
|
|
u_martix = martix.copy() # 复制用户martix数组
|
|
|
|
|
show_possible = 0 # 是否显示提示
|
|
|
|
|
# 初始化可能值数组
|
|
|
|
|
p = []
|
|
|
|
|
possible = []
|
|
|
|
|
for i in range(9):
|
|
|
|
|
p.append({}.copy())
|
|
|
|
|
for i in range(9):
|
|
|
|
|
possible.append(p.copy())
|
|
|
|
|
loca_o = tuple() # martix 数组上次滞留坐标
|
|
|
|
|
loca = tuple()
|
|
|
|
|
note_mark = False # 笔记开启标志
|
|
|
|
|
# 显示时间信息
|
|
|
|
|
show_Time(time1)
|
|
|
|
|
step = [] # 操作步骤
|
|
|
|
|
STEP = [[0, (0, 0), 0, martix]] # 记录每一步的状态
|
|
|
|
|
seq = 1 # 全局操作次序初始化
|
|
|
|
|
|
|
|
|
|
draw(loca, martix, u_martix, STEP)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def left1(event):
|
|
|
|
|
'''
|
|
|
|
|
martix坐标映射
|
|
|
|
|
:param event: 鼠标左键点击的坐标值
|
|
|
|
|
:return: 点击的方格坐标
|
|
|
|
|
'''
|
|
|
|
|
""""""
|
|
|
|
|
x = event.x
|
|
|
|
|
y = event.y
|
|
|
|
|
global STEP, loca, loca_o
|
|
|
|
|
|
|
|
|
|
for key, posit_lt in martix_position_mapping.items():
|
|
|
|
|
if posit_lt[0] < x < posit_lt[0] + side and posit_lt[1] < y < posit_lt[1] + side:
|
|
|
|
|
print(key)
|
|
|
|
|
|
|
|
|
|
if key[0] >= 0 and key[1] >= 0 and not martix[key[0], key[1]]:
|
|
|
|
|
# print(col, row, "dks")
|
|
|
|
|
if loca:#若已经点击过坐标
|
|
|
|
|
loca_o = loca # 保留上次点击过的坐标
|
|
|
|
|
loca = (key[0] + 1, key[1] + 1) # 更新选中的格子位置
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#绘制界面
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def win(u_martix, timer: MyTimer, STEP):
|
|
|
|
|
'''
|
|
|
|
|
检查游戏是否结束,若结束则输出结束界面
|
|
|
|
|
:param u_martix:用户矩阵
|
|
|
|
|
:param timer:计时器
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
count = 0 # 计数
|
|
|
|
|
|
|
|
|
|
for i in range(9):
|
|
|
|
|
for j in range(9):
|
|
|
|
|
if u_martix[i][j] == 0 or u_martix[i][j] == 10:
|
|
|
|
|
count += 1
|
|
|
|
|
if count == 0:
|
|
|
|
|
if Judge(u_martix):
|
|
|
|
|
for w in right.winfo_children():
|
|
|
|
|
w.destroy()
|
|
|
|
|
timer.stop()
|
|
|
|
|
win_words = tk.Label(right, text=f"游戏结束,恭喜通过", bg='white', fg='red', font=('幼圆', 16, 'bold'),
|
|
|
|
|
anchor=W)
|
|
|
|
|
win_words.place(relx=0.1, rely=0.3)
|
|
|
|
|
win_time = tk.Label(right, text=f"耗时:{timer.min.get()}分{timer.sec.get()}秒", bg='white', fg='red',
|
|
|
|
|
font=('幼圆', 16, 'bold'), anchor=W)
|
|
|
|
|
win_time.place(relx=0.1, rely=0.4)
|
|
|
|
|
win_step = tk.Label(right, text=f"花费步数:{len(STEP)}步", bg='white', fg='red',
|
|
|
|
|
font=('幼圆', 16, 'bold'), anchor=W)
|
|
|
|
|
win_step.place(relx=0.1, rely=0.5)
|
|
|
|
|
re = tk.Button(right, text=f"重新开始", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 14, 'bold'), command=lambda: restart(N))
|
|
|
|
|
re.place(relx=0.1, rely=0.6, relwidth=0.5)
|
|
|
|
|
|
|
|
|
|
def u_number(num, loca):
|
|
|
|
|
'''
|
|
|
|
|
添加功能:
|
|
|
|
|
:param num: 用户点击数字按钮
|
|
|
|
|
:param loca: 当前位置
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
if loca != ():#若选择了一个格子
|
|
|
|
|
row, col = loca[0] - 1, loca[1] - 1
|
|
|
|
|
global step, seq
|
|
|
|
|
if num: # 直接点击了数字按钮,就添加num
|
|
|
|
|
if martix[row][col] == 0 and not note_mark: # 空格且笔记模式为False才可以填写
|
|
|
|
|
if u_martix[row][col] == 10: # 如果修改了这个格子的值,删除原来的记录
|
|
|
|
|
possible[row][col].clear()
|
|
|
|
|
# for note in note_martix: # 如果这个空格有笔记直接覆盖清空笔记
|
|
|
|
|
# if note[0] == row and note[1] == col:
|
|
|
|
|
# note_martix.remove(note)
|
|
|
|
|
u_martix[row][col] = num # 为用户数组添加数值
|
|
|
|
|
seq = STEP[-1][0] + 1 # 记录顺序
|
|
|
|
|
step = [seq, loca, num, u_martix.copy()] # 记录一步
|
|
|
|
|
# print(step[:3])
|
|
|
|
|
if len(step) != 0: # 添加步骤
|
|
|
|
|
STEP.append(step)
|
|
|
|
|
step = []
|
|
|
|
|
print(loca, num, "添加")
|
|
|
|
|
elif note_mark: # 笔记模式打开时
|
|
|
|
|
if u_martix[row][col] == 0:# 若当前没有笔记,则只有num
|
|
|
|
|
u_martix[row][col] = 10#标记该格为笔记格子
|
|
|
|
|
possible[row][col] = {num}# 将笔记加入记录中
|
|
|
|
|
elif u_martix[row][col] == 10:# 若已有笔记,则添加num
|
|
|
|
|
possible[row][col].add(num)
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#重新绘制界面
|
|
|
|
|
|
|
|
|
|
def control_draw_old(side):
|
|
|
|
|
'''
|
|
|
|
|
绘制数字按钮
|
|
|
|
|
:param side: 全局按钮大小
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
for i in range(9):#遍历0~9
|
|
|
|
|
top_x, top_y = i * (side + 3) + 50, 12 * (side + 3) + 10#计算每个按钮左上角坐标
|
|
|
|
|
B = tk.Button(root, text=f"{i + 1}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 14, 'bold'), command=lambda num=i + 1: u_number(num, loca))
|
|
|
|
|
B.place(x=top_x, y=top_y, width=side + 3, height=side + 3)#绘制按钮到图上
|
|
|
|
|
def control_draw(bc, u_martix):
|
|
|
|
|
'''
|
|
|
|
|
绘制数字按钮
|
|
|
|
|
:param bc: 全局按钮大小
|
|
|
|
|
:param u_martix: 当前用户矩阵
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
num = [9] * 11#记录每个数字缺失的个数为9
|
|
|
|
|
for i in range(9):
|
|
|
|
|
for j in range(9):#遍历当前数组,每有一个数字,则该数字的缺失值减一
|
|
|
|
|
num[u_martix[i][j]] = num[u_martix[i][j]] - 1
|
|
|
|
|
for i in range(9):
|
|
|
|
|
top_x, top_y = i * (bc + 3) + 50, 12 * (bc + 3) + 10#计算每个按钮左上角坐标
|
|
|
|
|
if num[i + 1]:#如果该数字还有缺失(图中不到9个),则按钮可以显示
|
|
|
|
|
B = tk.Button(root, text=f"{i + 1}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 14, 'bold'), command=lambda num=i + 1: u_number(num, loca))
|
|
|
|
|
else:#若某个数字已经有9个,说明已经不能选择该数字,设置这个按钮为不可按
|
|
|
|
|
B = tk.Button(root, text=f"{i + 1}", bg='grey', fg='black', state=DISABLED,
|
|
|
|
|
font=('幼圆', 14, 'bold'), command=lambda num=i + 1: u_number(num, loca))
|
|
|
|
|
B.place(x=top_x, y=top_y, width=bc + 3, height=bc + 3)#绘制按钮到图上
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def listen_del(loca, possible):
|
|
|
|
|
'''
|
|
|
|
|
擦除功能
|
|
|
|
|
:param loca: 当前位置
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
if loca != ():
|
|
|
|
|
# 选定了位置才能擦除
|
|
|
|
|
row, col = loca[0] - 1, loca[1] - 1
|
|
|
|
|
if not u_martix[row][col] == 0 and martix[row][col] == 0: # 原题目中没有数字才可以删除
|
|
|
|
|
u_martix[row][col] = 0#设置为空
|
|
|
|
|
possible[row][col].clear()#清除可能值
|
|
|
|
|
print(loca, 2, "删除")#输出删除信息
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#显示界面
|
|
|
|
|
|
|
|
|
|
def listen_all_note(possible, u_martix):
|
|
|
|
|
'''
|
|
|
|
|
一键提示功能
|
|
|
|
|
:param pos:
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
print('all note')#输出提示信息
|
|
|
|
|
global show_possible, loca
|
|
|
|
|
show_possible = 1 # 显示提示信息
|
|
|
|
|
for row in range(9):
|
|
|
|
|
for col in range(9):
|
|
|
|
|
if u_martix[row][col] == 10 or u_martix[row][col] == 0:# 只有空格才会更新提示
|
|
|
|
|
possible[row][col] = GetPossible(u_martix, row, col)#计算可能取值
|
|
|
|
|
u_martix[row][col] = 10#标记这个格子为存在提示
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#重新绘制界面
|
|
|
|
|
|
|
|
|
|
def show_hint(side, u_martix, possible):
|
|
|
|
|
'''
|
|
|
|
|
显示提示信息
|
|
|
|
|
:param side: 方格大小
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
hint_bc = int(side // 3)# 小数字的间距
|
|
|
|
|
for row in range(9):
|
|
|
|
|
for col in range(9):
|
|
|
|
|
if not u_martix[row][col] == 10:
|
|
|
|
|
possible[row][col].clear()# 只有u_martix[j][i]为10才表示该格子有笔记存在
|
|
|
|
|
continue# 若该格无笔记,则跳过该格
|
|
|
|
|
x, y = col * (side + 3) + 50 + side / 2, row * (side + 3) + 50 + side / 2# 计算大方格中点坐标
|
|
|
|
|
x = x - hint_bc#计算左上角第一个数字坐标
|
|
|
|
|
y = y - hint_bc#计算左上角第一个数字坐标
|
|
|
|
|
for ii in range(3):# 按照九宫格显示小数字
|
|
|
|
|
for jj in range(3):
|
|
|
|
|
n = ii * 3 + jj + 1#遍历1~9
|
|
|
|
|
if possible[row][col]: # 在用户已填的部分有可能导致部分格子无解,所以先加一个判断
|
|
|
|
|
for k in possible[row][col]:
|
|
|
|
|
if n == k:# 如果possible[row][col]中含有k,则说明该格子可以填k,则将k显示在方格中
|
|
|
|
|
canvas.create_text(x + ii * hint_bc, y + jj * hint_bc,
|
|
|
|
|
text=f"{n}", font=('楷体', 10), fill='purple', tags="mini_num")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def listen_hint(loca, u_martix):
|
|
|
|
|
'''
|
|
|
|
|
提示功能:显示当前格子的可能值
|
|
|
|
|
:param loca: 当前格子
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
if loca != ():
|
|
|
|
|
row, col = loca[0] - 1, loca[1] - 1
|
|
|
|
|
if u_martix[row][col] == 0 or u_martix[row][col] == 10:#只有该方格没有数字时才能提示
|
|
|
|
|
possible[row][col] = GetPossible(u_martix, row, col)#计算该方格可能取值
|
|
|
|
|
u_martix[row][col] = 10#标记该方格为存在提示
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#重新绘制界面
|
|
|
|
|
|
|
|
|
|
def listen_note(loca):
|
|
|
|
|
'''
|
|
|
|
|
笔记功能
|
|
|
|
|
:param loca: 当前位置
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
if loca != ():#若已经选中一个方格
|
|
|
|
|
global note_mark # 函数内修改全局变量
|
|
|
|
|
note_mark = not note_mark#切换笔记模式
|
|
|
|
|
print(f"笔记功能:{note_mark}")#输出信息
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#绘制界面
|
|
|
|
|
|
|
|
|
|
def listen_backspace():
|
|
|
|
|
'''
|
|
|
|
|
撤回功能
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
global u_martix, STEP
|
|
|
|
|
if len(STEP) > 1:# 如果操作记录中有操作
|
|
|
|
|
STEP.pop()# 删除操作记录最后一步操作
|
|
|
|
|
u_martix = STEP[-1][-1].copy()#将数独状态复位到上一步操作
|
|
|
|
|
loca = STEP[-1][1]#将当前选定方格复位到上一步操作
|
|
|
|
|
draw(loca, martix, u_martix, STEP)#重新绘制界面
|
|
|
|
|
else:#若已经没有操作步骤(已经回到游戏初始状态)
|
|
|
|
|
u_martix = STEP[-1][-1].copy()#将数独状态复制到初始状态
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def control(bc):
|
|
|
|
|
'''
|
|
|
|
|
绘制五个控制按键
|
|
|
|
|
:param bc:全局按钮大小
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
global loca, loca_o
|
|
|
|
|
lon = 9 * (bc + 3) + 50
|
|
|
|
|
lon = lon // 5 - 12 # 控制按键长度
|
|
|
|
|
ctl = ["撤回", "擦除", "笔记", "一键笔记", "提示"]
|
|
|
|
|
# 撤回
|
|
|
|
|
B1 = tk.Button(root, text=f"{ctl[0]}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 12, 'bold'), command=lambda: listen_backspace())
|
|
|
|
|
B1.place(x=(lon + 2) * 0 + 50, y=(bc + 3) * 11 + 8, width=lon, height=bc)
|
|
|
|
|
# 擦除
|
|
|
|
|
B2 = tk.Button(root, text=f"{ctl[1]}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 12, 'bold'), command=lambda: listen_del(loca, possible))
|
|
|
|
|
B2.place(x=(lon + 2) * 1 + 50, y=(bc + 3) * 11 + 8, width=lon, height=bc)
|
|
|
|
|
# 笔记
|
|
|
|
|
B3 = tk.Button(root, text=f"{ctl[2]}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 12, 'bold'), command=lambda: listen_note(loca))
|
|
|
|
|
B3.place(x=(lon + 2) * 2 + 50, y=(bc + 3) * 11 + 8, width=lon, height=bc)
|
|
|
|
|
# 一键笔记
|
|
|
|
|
B4 = tk.Button(root, text=f"{ctl[3]}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 12, 'bold'), command=lambda: listen_all_note(possible, u_martix))
|
|
|
|
|
B4.place(x=(lon + 2) * 3 + 50, y=(bc + 3) * 11 + 8, width=lon, height=bc)
|
|
|
|
|
# 提示
|
|
|
|
|
B5 = tk.Button(root, text=f"{ctl[4]}", bg='white', fg='black',
|
|
|
|
|
font=('幼圆', 12, 'bold'), command=lambda: listen_hint(loca, u_martix))
|
|
|
|
|
B5.place(x=(lon + 2) * 4 + 50, y=(bc + 3) * 11 + 8, width=lon, height=bc)
|
|
|
|
|
|
|
|
|
|
# def draw(loca, martix, u_martix, STEP):
|
|
|
|
|
|
|
|
|
|
def draw_fixation_num():
|
|
|
|
|
for i in range(9):
|
|
|
|
|
for n in range(9):
|
|
|
|
|
x,y = martix_position_mapping[i,n]
|
|
|
|
|
canvas.create_text(x+ side / 2, x + side / 2,
|
|
|
|
|
text=f"{u_martix[i, n]}", font=('幼圆', 18, 'bold'), fill="black", tags="fixa_num")
|
|
|
|
|
num_draw(side,i,n,martix,u_martix)
|
|
|
|
|
|
|
|
|
|
def draw(loca, martix, u_martix, STEP):
|
|
|
|
|
'''
|
|
|
|
|
绘制游戏界面
|
|
|
|
|
:param loca: 当前位置
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
if loca != ():#根据当前选中位置绘制阴影
|
|
|
|
|
canvas.delete("rect")
|
|
|
|
|
row, col = loca[0] - 1, loca[1] - 1
|
|
|
|
|
for i in range(9):
|
|
|
|
|
for j in range(9):
|
|
|
|
|
if col == i and row == j:#当前选中的方格使用灰色标记
|
|
|
|
|
canvas.create_rectangle(col * (side + 3) + 52, row * (side + 3) + 52, col * (side + 3) + 48 + side,
|
|
|
|
|
row * (side + 3) + 48 + side, fill='grey', width=0, tags="rect")
|
|
|
|
|
elif col == i or row == j:#当前选中方格所在行列使用浅灰色标记
|
|
|
|
|
canvas.create_rectangle(i * (side + 3) + 52, j * (side + 3) + 52, i * (side + 3) + 48 + side,
|
|
|
|
|
j * (side + 3) + 48 + side, fill='#CCCCCC', width=0, tags="rect")
|
|
|
|
|
canvas.delete("num")
|
|
|
|
|
num_draw(side, row, col, martix, u_martix)
|
|
|
|
|
|
|
|
|
|
canvas.delete("mini_num")
|
|
|
|
|
show_hint(side, u_martix, possible)# 显示提示信息
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# print(u_martix.tolist())
|
|
|
|
|
# print(loca)
|
|
|
|
|
|
|
|
|
|
empty_count = empty(martix)# 计算数组空格
|
|
|
|
|
Difficulty(empty_count)# 根据空格数量显示难度
|
|
|
|
|
noter(note_mark)# 显示笔记提示
|
|
|
|
|
seq_recode(right, STEP)# 显示步骤
|
|
|
|
|
win(u_martix, time1, STEP)# 检查游戏是否结束
|
|
|
|
|
# canvas.update()# 更新画布
|
|
|
|
|
|
|
|
|
|
N = 30 # 空白格子数
|
|
|
|
|
martix = InitMartix(N)
|
|
|
|
|
u_martix = martix.copy() # 复制用户martix数组
|
|
|
|
|
BC = 40
|
|
|
|
|
show_possible = 0 # 是否显示提示
|
|
|
|
|
# 初始化可能值数组
|
|
|
|
|
p = []
|
|
|
|
|
possible = []
|
|
|
|
|
for i in range(9):
|
|
|
|
|
p.append({}.copy())
|
|
|
|
|
for i in range(9):
|
|
|
|
|
possible.append(p.copy())
|
|
|
|
|
# print(possible)
|
|
|
|
|
loca_o = tuple() # martix 数组上次滞留坐标
|
|
|
|
|
loca = tuple()
|
|
|
|
|
note_mark = False # 笔记开启标志
|
|
|
|
|
# note_list = [] # 笔记数字记录
|
|
|
|
|
# note_martix = [] # 笔记位置记录列表
|
|
|
|
|
wait_time = 0.5 # 自动求解速度
|
|
|
|
|
step = [] # 操作步骤
|
|
|
|
|
STEP = [[0, (0, 0), 0, martix]] # 记录每一步的状态
|
|
|
|
|
seq = 1 # 全局操作次序初始化
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
draw_all_rect() # 显示底图
|
|
|
|
|
control(side) # 绘制五个控制按键
|
|
|
|
|
control_draw(side, u_martix) # 绘制9个数字按钮
|
|
|
|
|
draw_fixation_num()
|
|
|
|
|
|
|
|
|
|
time1 = MyTimer() # 实例计时器
|
|
|
|
|
# 显示时间信息
|
|
|
|
|
show_Time(time1)
|
|
|
|
|
draw(loca, martix, u_martix, STEP)
|
|
|
|
|
|
|
|
|
|
canvas.bind('<Button-1>', left1)
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
mainloop()
|