# -*- encoding: utf-8 -*- """ @File : new_UI.py @License : (C)Copyright 2021-2023 @Modify Time @Author @Version @Description ------------ ------- -------- ----------- 2023/10/11 16:14 zart20 1.0 None """ import re import time import tkinter as tk from pprint import pprint from tkinter import scrolledtext from typing import List import data from data import * BLACK = "#000000" HINT = '#6D2DFA' SILVER = '#FFFFFF' u_NUMBER = '#6D2DFA' WHITE = "#FFFFFF" ORANGE = '#FF8C00' RED = "#C80000" YELLOW = "#FAE067" GREY = '#808080' GREYLESS = '#CCCCCC' martix_position_mapping = dict() side = 40 # 方格边长 # martix = np.load("martix.npy") quest = [[0, 1, 0, 8, 5, 2, 0, 6, 9], [0, 6, 5, 7, 9, 3, 0, 0, 0], [9, 0, 8, 0, 0, 4, 0, 5, 7], [0, 7, 2, 4, 3, 0, 9, 0, 0], [6, 0, 9, 2, 0, 8, 1, 3, 0], [0, 8, 0, 1, 0, 0, 0, 2, 5], [7, 9, 1, 5, 2, 6, 8, 4, 3], [5, 4, 0, 9, 0, 1, 6, 7, 2], [8, 0, 0, 3, 4, 7, 0, 0, 0]] martix = np.array(quest) u_martix = martix.copy() info_labels = [] # label对象列表 note_mark = False # 笔记标志 all_hint_mark = False # 一键笔记标记 STEP = [] # 回撤存储列表 wait_time = 0.1 # 运行阻滞时间 set_question_mark = False # 手动修改数组标志 running = True # 运行状态 r_step = list() # 单步记录列表 hint_mark = None # 提示状态 possible_num_mark = False # 手动预期功能标志 row = None col = None def creat_note_dict(): usr_note_dict = dict() for i in range(9): for n in range(9): usr_note_dict[i, n] = set() return usr_note_dict usr_note_dict = creat_note_dict() # 记录对应单元格笔记数字 def creat_martix_mapping(side): mapping_dict = dict() offeset_x = 50 side = side + 3 for i in range(9): # 坐标映射 for n in range(9): x = side * i + offeset_x y = side * n + offeset_x mapping_dict[n, i] = [x, y] return mapping_dict mapping = creat_martix_mapping(side) class MyTimer: # 计时器 def __init__(self, root): self.root = root self.elapsed = 0.0 self.running = False self.last_start_time = None self.timestr = tk.IntVar() # 创建可变数据类型 self.min = tk.IntVar() # 创建可变数据类型 self.sec = tk.IntVar() # 创建可变数据类型 self.timestr.set(0) # 只能数值不能等于号 self.starttime = 0 # 开始计时时间 self.elapsedtime = 0.0 # 计时器统计到的时间 self.timer = None self.starttime = time.time() - self.elapsedtime self.update() self.win = 0 def update(self): self.elapsedtime = int(time.time() - self.starttime) self.timestr.set(self.elapsedtime) self.min.set(self.elapsedtime // 60) self.sec.set(self.elapsedtime - self.min.get() * 60) self.timer = self.root.after(100, self.update) def restart(self): self.win = 0 self.elapsedtime = 0.0 self.starttime = time.time() - self.elapsedtime self.update() def stop(self): if self.win: pass else: self.win = 1 self.root.after_cancel(self.timer) self.elapsedtime = int(time.time() - self.starttime) now = int(time.time() - self.starttime) self.timestr.set(now) self.min.set(now // 60) self.sec.set(now - self.min.get() * 60) def draw_all_rect(canvas): rect_mapping = dict() block_color = [3, 4, 5] # 显色宫格依据 for row in range(9): for col in range(9): logi_row = row in block_color and col in block_color # 显色逻辑 logi_col = row in block_color or col in block_color # 显色逻辑 if not logi_row and logi_col: color = "#FAE067" else: color = 'white' x, y = mapping[row, col] rect = canvas.create_rectangle(x, y, x + side, y + side, fill=color, width=0) # 绘制一个方格 rect_mapping[row, col] = rect return rect_mapping def draw_info(canvas): top_x = (side + 3) * 11 # 文本框顶点x top_y = 50 # 文本框顶点y bc_x = (side + 3) * 7 + 20 # 文本框宽度 bc_y = (side + 3) * 12 # 文本框高度 info = tk.Frame(canvas, bg=WHITE, relief="sunken", width=bc_x, height=bc_y) info.place(x=top_x, y=top_y, anchor=tk.NW) # 右边栏 info_labels.append(info) return info # def label_destroy_info(labels: list[tk.Label], canvas): # """销毁label""" # for label in labels: # label.destroy() # draw_info(canvas) def label_destroy_info(labels: List[tk.Label], canvas): """销毁label""" for label in labels: label.destroy() draw_info(canvas) def show_Time(right: tk.Frame, timer: MyTimer): ''' 显示计时器 :param timer:计时器示例 :return: ''' Time = tk.Label(right, text="运行时间: 分 秒", bg=WHITE, fg=BLACK, font=('幼圆', 16, 'bold'), anchor=tk.W) Time.place(x=30, y=30, width=340, height=30) Min = tk.Label(right, textvariable=timer.min, bg=WHITE, fg=BLACK, font=('幼圆', 16, 'bold')) # 分钟计数 Min.place(x=150, y=30, width=30, height=30) Sec = tk.Label(right, textvariable=timer.sec, bg=WHITE, fg=BLACK, font=('幼圆', 16, 'bold')) # 秒钟计数 Sec.place(x=220, y=30, width=30, height=30) info_labels.append(Time) info_labels.append(Min) info_labels.append(Sec) def difficulty(): """ 难度显示: :param empty: 原始空格数目 :return: """ count = 0 # 计数 for i in range(9): for j in range(9): if martix[i][j] == 0 or martix[i][j] == 10: count += 1 Level = tk.Label(info, text="难度:{:.2f}".format(count / 81), bg=WHITE, fg=BLACK, font=('幼圆', 16, 'bold'), anchor=tk.W) Level.place(x=30, y=60, width=300, height=30) info_labels.append(Level) def noter(): ''' 显示笔记提示 :param mark: 笔记模式是否开启 :return: ''' note_help1 = tk.Label(info, text=f"False 时保存笔记,当前笔记:{note_mark}", bg=WHITE, fg=RED, font=('幼圆', 12, 'bold'), anchor=tk.W) note_help1.place(x=30, y=90, width=300, height=30) info_labels.append(note_help1) def seq_recode(): ''' 显示步骤 ''' note_help1 = tk.Label(info, text=f"步骤记录:", bg=WHITE, fg=RED, font=('幼圆', 12, 'bold'), anchor=tk.W) note_help1.place(x=30, y=120, width=300, height=30) scr = scrolledtext.ScrolledText(info, fg='red', font=('幼圆', 16, 'bold')) # 设置一个可滚动文本框 scr.place(x=30, y=150, width=280, height=300) # for i in range(len(STEP)): scr.insert('end', f"{i + 1} {STEP[i][:2]}\n") # 末尾插入 scr.config(state=tk.DISABLED) # 设置文本框为 “不能编辑” scr.see(tk.END) # 将视图移到末尾 info_labels.append(note_help1) info_labels.append(scr) def basics_info(): difficulty() noter() seq_recode() def draw_one_num(x, y, num, color="black", tags=None): """显示一个数字""" if num != 0: canvas.create_text(x + side / 2, y + side / 2, text=num, font=('幼圆', 18, 'bold'), fill=color, tags=tags) def show_usr_num(): """显示所有用户数字""" canvas.delete("usr_input") canvas.delete("usr_num") for row in range(9): for col in range(9): if martix[row, col] == 0: x, y = mapping[row, col] draw_one_num(x, y, u_martix[row, col], "blue", tags="usr_num") if all_hint_mark: show_all_hint() def show_fixa_num(): """显示固定数字""" canvas.delete("fixa_num") canvas.delete("set_fixa_num") for i in range(9): for n in range(9): x, y = mapping[i, n] draw_one_num(x, y, martix[i, n], tags="fixa_num") def show_set_all_num(): global set_martix canvas.delete("fixa_num") canvas.delete("set_fixa_num") for i in range(9): for n in range(9): x, y = mapping[i, n] draw_one_num(x, y, set_martix[i, n], tags="set_fixa_num") def click_event(event): print(event.x, event.y) global row, col, set_martix, cell_info_label # 全局坐标映射,方便其他功能知道操作单元格 x = event.x y = event.y for key, posit_lt in mapping.items(): if posit_lt[0] < x < posit_lt[0] + side and posit_lt[1] < y < posit_lt[1] + side: row, col = key[0], key[1] if set_question_mark: set_martix[row, col] = 0 show_set_all_num() else: draw(row, col) print(key) if possible_num_mark: if row >= 0: if cell_info_label: # cell_info_label = tk.Label(info, text=f"当前选中单元格:{row}-{col}", bg=WHITE, font=("幼圆", 12, "bold")) cell_info_label.config(text=f"当前选中单元格:{row}-{col}") lebel_width = cell_info_label.winfo_reqwidth() x = 160 x = x - lebel_width / 2 cell_info_label.place(x=x, y=105, ) else: if cell_info_label: cell_info_label.config(text=f"请选中一个单元格以开始实验") lebel_width = cell_info_label.winfo_reqwidth() x = 160 x = x - lebel_width / 2 cell_info_label.place(x=x, y=105, ) def draw_mini_num(row, col, possible: set, color: str, tags="mini_num"): hint_bc = int(side // 3) # 小数字的间距 x, y = mapping[row, col] x, y = x + side / 2, y + 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 len(possible) != 0: # 在用户已填的部分有可能导致部分格子无解,所以先加一个判断 for k in possible: if n == k: # 如果possible[row][col]中含有k,则说明该格子可以填k,则将k显示在方格中 canvas.create_text(x + ii * hint_bc, y + jj * hint_bc, text=f"{n}", font=('hei', 12, "bold"), fill=color, tags=tags) def hint_on_off(): global hint_mark if hint_mark == None: show_hint() hint_mark = 1 else: canvas.delete("one_hint") hint_mark = None def show_hint(): """提示""" global row, col tags = "one_hint" canvas.delete(tags) if row != None and u_martix[row, col] == 0: possible = GetPossible(u_martix, row, col) draw_mini_num(row, col, possible, "green", tags) def all_note_on_off(): """一键笔记控制""" global all_hint_mark if not all_hint_mark: show_all_hint() all_hint_mark = True else: canvas.delete("mini_num") all_hint_mark = False def show_all_hint(): """一键笔记""" canvas.delete("mini_num") for row in range(9): for col in range(9): if set_question_mark: if set_martix[row, col] == 0: possible = GetPossible(set_martix, row, col) draw_mini_num(row, col, possible, "grey") else: if u_martix[row, col] == 0: possible = GetPossible(u_martix, row, col) draw_mini_num(row, col, possible, "grey") def note_on_off(): global note_mark, usr_note_dict if not note_mark: note_mark = True else: note_mark = False noter() def number_button(root): ''' 绘制数字按钮 :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: usr_input(row, col, num)) B.place(x=top_x, y=top_y, width=side + 3, height=side + 3) # 绘制按钮到图上 def control_button(root, side): ''' 绘制五个控制按键 :param side:全局按钮大小 :return: ''' global loca, loca_o lon = 9 * (side + 3) + 50 lon = lon // 5 - 12 # 控制按键长度 ctl = ["撤回", "擦除", "笔记", "一键笔记", "提示"] # 撤回 B1 = tk.Button(root, text=f"撤回", bg='white', fg='black', font=('幼圆', 12, 'bold'), command=backspace) B1.place(x=(lon + 2) * 0 + 50, y=(side + 3) * 11 + 8, width=lon, height=side) # 擦除 B2 = tk.Button(root, text=f"擦除", bg='white', fg='black', font=('幼圆', 12, 'bold'), command=delete_ctrl) B2.place(x=(lon + 2) * 1 + 50, y=(side + 3) * 11 + 8, width=lon, height=side) # 笔记 B3 = tk.Button(root, text=f"笔记", bg='white', fg='black', font=('幼圆', 12, 'bold'), command=note_on_off) B3.place(x=(lon + 2) * 2 + 50, y=(side + 3) * 11 + 8, width=lon, height=side) # 一键笔记 B4 = tk.Button(root, text=f"一键笔记", bg='white', fg='black', font=('幼圆', 12, 'bold'), command=all_note_on_off) B4.place(x=(lon + 2) * 3 + 50, y=(side + 3) * 11 + 8, width=lon, height=side) # 提示 B5 = tk.Button(root, text=f"提示", bg='white', fg='black', font=('幼圆', 12, 'bold'), command=hint_on_off) B5.place(x=(lon + 2) * 4 + 50, y=(side + 3) * 11 + 8, width=lon, height=side) def restart(): for child in info.winfo_children(): child.destroy() canvas.delete("usr_input") global u_martix, info_labels, note_mark, all_hint_mark, STEP, row, col global usr_note_dict, mapping, time1, set_question_mark, r_step, hint_mark global possible_num_mark u_martix = martix.copy() info_labels = [] # label对象列表 note_mark = False # 笔记标志 all_hint_mark = False # 一键笔记标记 STEP = [] # 回撤存储列表 set_question_mark = False # 手动挖空标志 r_step = list() # 单步记录列表 hint_mark = None # 提示状态 possible_num_mark = False row = None col = None usr_note_dict = creat_note_dict() # 记录对应单元格笔记数字 mapping = creat_martix_mapping(side) basics_info() seq_recode() show_usr_num() show_fixa_num() canvas.delete("mini_num") time1 = MyTimer(root) show_Time(info, time1) def game_menu(menu: tk.Menu): """游戏菜单显示""" menu_node = tk.Menu(menu, tearoff=False) menu_node.add_command(label="重新开始", command=lambda: restart()) menu_node.add_command(label="暂停计时", command=lambda: time1.stop()) 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 = tk.Menu(menu, tearoff=False) menu_node.add_command(label="自动求解-顺序求解", command=launch_auto) menu_node.add_command(label="自动求解-优化", command=launch_auto_better) fmenu = tk.Menu(menu, tearoff=False) fmenu.add_command(label='低速', command=lambda: speed_set(0.5)) fmenu.add_command(label='中速', command=lambda: speed_set(0.2)) fmenu.add_command(label='高速', command=lambda: speed_set(0)) menu_node.add_cascade(label="求解速度选择", menu=fmenu) # 在主目录菜单上新增"菜单"选项,并通过menu参数与下拉菜单绑定 menu.add_cascade(label="自动求解", menu=menu_node) def question_menu(menu: tk.Menu): menu_node = tk.Menu(menu, tearoff=False) menu_node.add_command(label="手动出题", command=lambda: auto_question()) menu_node.add_command(label="挖空出题", command=lambda: set_question()) menu_node.add_command(label="单步求解模式", command=lambda: one_step_ctrl()) menu_node.add_command(label="手动解预期数", command=lambda: show_possible_num()) menu.add_cascade(label="手动模式", menu=menu_node) def one_step_ctrl(): global martix, u_martix for w in info.winfo_children(): w.destroy() info_label = tk.Label(info, text="单步求解模式", bg=WHITE, font=("幼圆", 12, "bold")) info_label.place(x=120, y=21) next_step_button = tk.Button(info, text="下一步", font=("幼圆", 12, "bold"), command=lambda: one_step_start(1)) next_step_button.place(x=100, y=60) next_step_button = tk.Button(info, text="上一步", font=("幼圆", 12, "bold"), command=lambda: one_step_start(-1)) next_step_button.place(x=180, y=60) one_martix = martix.copy() one_step_solve(one_martix) # 运行单步求解函数,获得满记录的r_step列表 def one_step_start(step): global STEP, u_martix, row, col index = len(STEP) - 1 if step == 1 and index + 1 < len(r_step): item = r_step[index + 1] u_martix = item[2] row, col = item[0][0], item[0][1] STEP.append(item) seq_recode() draw(row, col) show_usr_num() elif step == -1: if len(STEP) > 1: item = r_step[index - 1] u_martix = item[2] row, col = item[0][0], item[0][1] else: u_martix = martix try: STEP.pop() except: pass seq_recode() draw(row, col) show_usr_num() def one_step_solve(martix): for row in range(9): for col in range(9): if martix[row, col] == 0: possible = GetPossible(martix, row, col) # 所有的可能的数字 for value in possible: martix[row, col] = value # 将可能的数组填入 recoder_one(row, col, value, martix) if one_step_solve(martix): # 继续深度优先遍历填入数字 return True # 填完最后一个数字 martix[row, col] = 0 # 如果当前状况无解则归位,然后回溯 recoder_one(row, col, value, martix) return False # 当所有的数字填完,数独求解完毕 return True def get_user_input(entry: tk.Entry): global martix, u_martix num = entry.get() martix = data.InitMartix(int(num)) u_martix = martix.copy() restart() def auto_question(): global martix, u_martix for w in info.winfo_children(): w.destroy() num_info = tk.Label(info, text="输入挖空数", bg=WHITE, font=('幼圆', 12, 'bold'), anchor=tk.W) num_info.place(x=15, y=21) num_entry = tk.Entry(info) num_entry.config(font=('幼圆', 12, 'bold'), width=10) num_entry.place(x=110, y=23) get_input_button = tk.Button(info, text="出题", font=('幼圆', 12, 'bold'), command=lambda: get_user_input(num_entry)) get_input_button.place(x=90, y=60) def question_ok(set_martix): global martix, u_martix, set_question_mark martix = set_martix set_question_mark = False restart() def set_question(): global martix, u_martix, set_question_mark, set_martix martix = data.InitMartix(0) set_martix = martix.copy() show_fixa_num() for w in info.winfo_children(): w.destroy() num_info = tk.Label(info, text="鼠标左键点击你想挖空的单元格", bg=WHITE, font=('幼圆', 12, 'bold'), anchor=tk.W) num_info.place(x=30, y=21) set_question_mark = True get_input_button = tk.Button(info, text="确认", font=('幼圆', 12, 'bold'), command=lambda: question_ok(set_martix=set_martix)) get_input_button.place(x=90, y=60) get_input_button = tk.Button(info, text="取消", font=('幼圆', 12, 'bold'), command=set_question) get_input_button.place(x=150, y=60) # pprint(martix) def show_possible_num(): global martix, u_martix, row_entry, col_entry, big_cell_entry, cell_entry, cell_info_label global possible_num_mark possible_num_mark = True for w in info.winfo_children(): w.destroy() jx = 75 info_label = tk.Label(info, text="手动解预期数", bg=WHITE, font=("幼圆", 12, "bold")) info_label.place(x=120, y=21) if row and col: cell_info_label = tk.Label(info, text=f"当前选中单元格:{row}-{col}", bg=WHITE, font=("幼圆", 12, "bold")) lebel_width = cell_info_label.winfo_reqwidth() x = 160 x = x - lebel_width / 2 cell_info_label.place(x=x, y=105, ) else: cell_info_label = tk.Label(info, text=f"请选中一个单元格以开始实验", bg=WHITE, fg=RED, font=("幼圆", 12, "bold")) lebel_width = cell_info_label.winfo_reqwidth() x = 160 x = x - lebel_width / 2 cell_info_label.place(x=x, y=105, ) next_cell_button = tk.Button(info, text="自动解", font=("幼圆", 12, "bold"), command=lambda: auto_possible_verify()) next_cell_button.place(x=100, y=60) next_cell_button1 = tk.Button(info, text="验证", font=("幼圆", 12, "bold"), command=lambda: possible_verify()) next_cell_button1.place(x=180, y=60) row_label = tk.Label(info, text=f"当前行可填数字:", bg=WHITE, font=("幼圆", 12, "bold")) row_label.place(x=20, y=150) row_entry = tk.Entry(info) row_entry.place(x=20, y=175) row_entry.config(font=('幼圆', 12, 'bold'), width=10) col_label = tk.Label(info, text=f"当前列可填数字:", bg=WHITE, font=("幼圆", 12, "bold")) col_label.place(x=20, y=150 + jx) col_entry = tk.Entry(info) col_entry.place(x=20, y=175 + jx) col_entry.config(font=('幼圆', 12, 'bold'), width=10) big_cell = tk.Label(info, text=f"当前宫格可填数字:", bg=WHITE, font=("幼圆", 12, "bold")) big_cell.place(x=20, y=150 + 2 * jx) big_cell_entry = tk.Entry(info) big_cell_entry.place(x=20, y=175 + 2 * jx) big_cell_entry.config(font=("幼圆", 12, "bold"), width=10) cell = tk.Label(info, text=f"当前格可能填写数字:", bg=WHITE, font=("幼圆", 12, "bold")) cell.place(x=20, y=150 + 3 * jx) cell_entry = tk.Entry(info) cell_entry.place(x=20, y=175 + 3 * jx) cell_entry.config(font=("幼圆", 12, "bold"), width=10) def possible_verify(): """验证功能""" global martix, u_martix, row_entry, col_entry, big_cell_entry, cell_entry global row, col if row is not None: row_nums = row_entry.get() row_nums = trans_int_set(row_nums) col_nums = col_entry.get() col_nums = trans_int_set(col_nums) big_cell_nums = big_cell_entry.get() big_cell_nums = trans_int_set(big_cell_nums) cell_nums = cell_entry.get() cell_nums = trans_int_set(cell_nums) row_entry.delete(0, tk.END) if get_row_possible(u_martix, row, col) == row_nums: row_entry.insert(0, "正确") else: row_entry.insert(0, "错误") col_entry.delete(0, tk.END) if get_col_possible(u_martix, row, col) == col_nums: col_entry.insert(0, "正确") else: col_entry.insert(0, "错误") big_cell_entry.delete(0, tk.END) if get_big_cell_possible(u_martix, row, col) == big_cell_nums: big_cell_entry.insert(0, "正确") else: big_cell_entry.insert(0, "错误") cell_entry.delete(0, tk.END) if GetPossible(u_martix, row, col) == cell_nums: cell_entry.insert(0, "正确") else: cell_entry.insert(0, "错误") print(row_nums, col_nums, big_cell_nums, cell_nums) print(get_row_possible(u_martix, row, col), get_col_possible(u_martix, row, col), get_big_cell_possible(u_martix, row, col), GetPossible(u_martix, row, col)) def auto_possible_verify(): """验证功能""" global martix, u_martix, row_entry, col_entry, big_cell_entry, cell_entry global row, col if row is not None: row_entry.delete(0, tk.END) row_nums = get_row_possible(u_martix, row, col) row_nums = [str(i) for i in row_nums] row_nums = ",".join(row_nums) row_entry.insert(0, row_nums) col_entry.delete(0, tk.END) col_nums = get_col_possible(u_martix, row, col) col_nums = [str(i) for i in col_nums] col_nums = ",".join(col_nums) col_entry.insert(0, col_nums) big_cell_entry.delete(0, tk.END) big_cell_nums = get_big_cell_possible(u_martix, row, col) big_cell_nums = [str(i) for i in big_cell_nums] big_cell_nums = ",".join(big_cell_nums) big_cell_entry.insert(0, big_cell_nums) cell_entry.delete(0, tk.END) cell_nums = GetPossible(u_martix, row, col) cell_nums = [str(i) for i in cell_nums] cell_nums = ",".join(cell_nums) cell_entry.insert(0, cell_nums) print(row_nums, col_nums, big_cell_nums, cell_nums) print(get_row_possible(u_martix, row, col), get_col_possible(u_martix, row, col), get_big_cell_possible(u_martix, row, col), GetPossible(u_martix, row, col)) def trans_int_set(nums: str): nums = re.findall(r"\d+", nums) nums = "".join(nums) Set = set() for num in list(nums): Set.add(int(num)) return Set def get_row_possible(martix, row, col): """当前行的可能数字""" Nums = {1, 2, 3, 4, 5, 6, 7, 8, 9} Set = set() for i in range(9): if i == row: continue Set.add(int(martix[i, col])) # 将所在列出现数字加入集合 # print(martix[i,col]) return Nums - Set def get_col_possible(martix, row, col): """当前列的可能数字""" Nums = {1, 2, 3, 4, 5, 6, 7, 8, 9} Set = set() for i in range(9): if i == col: continue Set.add(int(martix[row, i])) # 将所在列出现数字加入集合 # print(martix[row,i]) return Nums - Set def get_big_cell_possible(martix, row, col): """当前宫格可能的数字""" Nums = {1, 2, 3, 4, 5, 6, 7, 8, 9} Set = set() m, n = row // 3, col // 3 # 计算所在九宫格的编号 for i in range(m * 3, m * 3 + 3): for j in range(n * 3, n * 3 + 3): if row == i and j == col: continue Set.add(int(martix[i, j])) # 将所在九宫格出现数字加入集合 # print(martix[i,j]) return Nums - Set # 返回1~9中没出现过的数字,即可选的 def next_cell_query(): for row in range(9): for col in range(9): if martix[row, col] == 0: return row, col def speed_set(num): global wait_time wait_time = num def launch_auto(): time1.restart() auto_Solve(u_martix) win(u_martix, time1) def auto_Solve(martix): for row in range(9): for col in range(9): if martix[row, col] == 0: possible = GetPossible(martix, row, col) # 所有的可能的数字 for value in possible: martix[row, col] = value # 将可能的数组填入 draw(row, col) recoder(row, col, value, u_martix) show_usr_num() canvas.update_idletasks() time.sleep(wait_time) # 防止运算过快,减慢演算步骤 if auto_Solve(martix): # 继续深度优先遍历填入数字 return True # 填完最后一个数字 martix[row, col] = 0 # 如果当前状况无解则归位,然后回溯 recoder(row, col, value, u_martix) draw(row, col) show_usr_num() canvas.update() time.sleep(wait_time) # 防止运算过快,减慢演算步骤 return False # 当所有的数字填完,数独求解完毕 return True def recoder_one(row, col, num, u_martix): # 返回记录表 global r_step step_martix = u_martix.copy() r_step.append([(row, col), num, step_martix]) def recoder(row, col, num, u_martix): global STEP step_martix = u_martix.copy() if len(STEP) > 0: old_row, old_col = STEP[-1][0] if old_row == row and old_col == col: STEP[-1] = [(row, col), num, step_martix] else: STEP.append([(row, col), num, step_martix]) else: STEP.append([(row, col), num, step_martix]) seq_recode() def launch_auto_better(): time1.restart() auto_Solve_better(u_martix) win(u_martix, time1) noter() def auto_Solve_better(u_M): global note_mark note_mark = False # 关闭笔记 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 # 将确定的数字填入填入 draw(row, col) canvas.update() time.sleep(wait_time) # 防止运算过快,减慢演算步骤 recoder(row, col, value, u_martix) show_usr_num() time.sleep(wait_time) # 停顿一段时间,显示求解步骤 if auto_Solve_better(u_M): # 继续深度优先遍历填入数字 return True # 填完最后一个数字 u_M[row, col] = 0 # 如果当前填入的数字会导致后面无解则依然填入0表示空白待填 draw(row, col) canvas.update() time.sleep(wait_time) # 防止运算过快,减慢演算步骤 recoder(row, col, value, u_martix) show_usr_num() 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 # 将可能的数组填入 recoder(row, col, value, u_martix) draw(row, col) canvas.update() time.sleep(wait_time) # 防止运算过快,减慢演算步骤 show_usr_num() time.sleep(wait_time) # 停顿一段时间,显示求解步骤 if auto_Solve_better(u_M): # 继续深度优先遍历填入数字 return True # 填完最后一个数字 u_M[row, col] = 0 # 如果当前填入的数字会导致后面无解则依然填入0表示空白待填 draw(row, col) canvas.update() time.sleep(wait_time) # 防止运算过快,减慢演算步骤 recoder(row, col, value, u_martix) show_usr_num() time.sleep(wait_time) # 停顿一段时间,显示求解步骤 return False return True # 当所有的数字填完,数独求解完毕 def win(u_martix, timer: MyTimer): 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 info.winfo_children(): w.destroy() timer.stop() win_words = tk.Label(info, text=f"游戏结束,恭喜通过", bg='white', fg='red', font=('幼圆', 16, 'bold'), anchor=tk.W) win_words.place(relx=0.1, rely=0.3) win_time = tk.Label(info, text=f"耗时:{timer.min.get()}分{timer.sec.get()}秒", bg='white', fg='red', font=('幼圆', 16, 'bold'), anchor=tk.W) win_time.place(relx=0.1, rely=0.4) win_step = tk.Label(info, text=f"花费步数:{len(STEP)}步", bg='white', fg='red', font=('幼圆', 16, 'bold'), anchor=tk.W) win_step.place(relx=0.1, rely=0.5) re = tk.Button(info, text=f"重新开始", bg='white', fg='black', font=('幼圆', 14, 'bold'), command=lambda: restart()) re.place(relx=0.1, rely=0.6, relwidth=0.5) def load_game(): global martix, u_martix martix = data.InitMartix(30) pprint(martix) restart() def draw(row, col): rect_color(row, col) select_rect_color(row, col) # show_usr_num() def show_all_note(): canvas.delete("usr_note") for i in range(9): for n in range(9): possible = usr_note_dict[i, n] draw_mini_num(i, n, possible, "red", "usr_note") def delete_ctrl(): """擦除功能""" global u_martix, usr_note_dict if row != None: if martix[row, col] == 0: canvas.delete("usr_input") u_martix[row, col] = 0 show_usr_num() if len(usr_note_dict[row, col]) != 0: usr_note_dict[row, col] = set() show_all_note() def backspace(): """撤回功能""" global STEP, u_martix, row, col if len(STEP) > 0: loca, num, step_martix = STEP.pop() u_martix = step_martix row, col = loca seq_recode() delete_ctrl() draw(row, col) def usr_input(row, col, num, color="blue"): global STEP if not note_mark: if row != None and martix[row, col] == 0: canvas.delete("usr_input") canvas.delete("usr_note") canvas.delete("mini_num") step_martix = u_martix.copy() u_martix[row, col] = num usr_note_dict[row, col] = set() x, y = mapping[row, col] draw_one_num(x, y, num, color, "usr_input") u_martix[row, col] = num show_all_note() show_usr_num() if len(STEP) > 0: old_row, old_col = STEP[-1][0] if old_row == row and old_col == col: STEP[-1] = [(row, col), num, step_martix] else: STEP.append([(row, col), num, step_martix]) else: STEP.append([(row, col), num, step_martix]) seq_recode() win(u_martix, time1) else: if row != None and martix[row, col] == 0: u_martix[row, col] = 0 usr_note_dict[row, col].add(num) draw_mini_num(row, col, usr_note_dict[row, col], "red", "usr_note") show_usr_num() def rect_color(row, col): # 显示当前选中格之外所有颜色 block_color = [3, 4, 5] # 显色宫格依据 for key, item in rect_mapping.items(): if key[0] != row and key[1] != col: logi_row = key[0] in block_color and key[1] in block_color # 显色逻辑 logi_col = key[0] in block_color or key[1] in block_color # 显色逻辑 if not logi_row and logi_col: color = "#FAE067" canvas.itemconfig(item, fill=color) else: color = 'white' canvas.itemconfig(item, fill=color) def select_rect_color(row, col): # 突出显示当前选中格 for key, item in rect_mapping.items(): if key[0] == row and key[1] == col: canvas.itemconfig(item, fill='#DCA0DC') elif key[0] == row or key[1] == col: canvas.itemconfig(item, fill='#ECD0EC') if __name__ == '__main__': root = tk.Tk() root.title("数独") # 设置窗口标题 width, height = 840, 610 # 设置长宽 root.geometry(f'{width}x{height}') # 设置窗口大小 canvas = tk.Canvas(root, bg=WHITE, bd=2, width=width, height=height, background=ORANGE, highlightthickness=0) # 设置画布 canvas.place(x=0, y=0, anchor=tk.NW) # 设置画布位置 info = draw_info(canvas) time1 = MyTimer(root) show_Time(info, time1) main_menu = tk.Menu(root) root.config(menu=main_menu) game_menu(main_menu) auto_menu(main_menu) question_menu(main_menu) rect_mapping = draw_all_rect(canvas) number_button(root) control_button(root, side) basics_info() canvas.bind('', click_event) # info.bind('', click_event) # time.sleep(3) # label_destroy_info(info_labels, canvas) show_fixa_num() tk.mainloop() # test2()