You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1116 lines
37 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- encoding: utf-8 -*-
"""
@File : main.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
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 # 一键笔记标记
answer_martix = [] # 回撤存储列表
wait_time = 0.1 # 运行阻滞时间
set_question_mark = False # 手动修改数组标志
running = True # 运行状态
r_step = list() # 单步记录列表
hint_mark = None # 提示状态
possible_num_mark = False # 手动预期功能标志
row = None
col = None
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) # 设置画布位置
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)
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 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
rect_mapping = draw_all_rect(canvas)
main_menu = tk.Menu(root)
info = draw_info(canvas)
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)
time1 = MyTimer(root)
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 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=300, 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(answer_martix)):
scr.insert('end', f"{i + 1} {answer_martix[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 i in range(3): # 按照九宫格显示小数字
for j in range(3):
n = i * 3 + j + 1 # 遍历1~9
if len(possible) != 0: # 在用户已填的部分有可能导致部分格子无解,所以先加一个判断
for k in possible:
if n == k: # 如果possible[row][col]中含有k则说明该格子可以填k则将k显示在方格中
canvas.create_text(x + i * hint_bc, y + j * 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, answer_martix, 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 # 一键笔记标记
answer_martix = [] # 回撤存储列表
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: manual_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 answer_martix, u_martix, row, col
index = len(answer_martix) - 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]
answer_martix.append(item)
seq_recode()
draw(row, col)
show_usr_num()
elif step == -1:
if len(answer_martix) > 1:
item = r_step[index - 1]
u_martix = item[2]
row, col = item[0][0], item[0][1]
else:
u_martix = martix
try:
answer_martix.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 manual_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=manual_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 answer_martix
step_martix = u_martix.copy()
if len(answer_martix) > 0:
old_row, old_col = answer_martix[-1][0]
if old_row == row and old_col == col:
answer_martix[-1] = [(row, col), num, step_martix]
else:
answer_martix.append([(row, col), num, step_martix])
else:
answer_martix.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(answer_martix)}", 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 answer_martix, u_martix, row, col
if len(answer_martix) > 0:
loca, num, step_martix = answer_martix.pop()
u_martix = step_martix
row, col = loca
seq_recode()
delete_ctrl()
draw(row, col)
def usr_input(row, col, num, color="blue"):
global answer_martix
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(answer_martix) > 0:
old_row, old_col = answer_martix[-1][0]
if old_row == row and old_col == col:
answer_martix[-1] = [(row, col), num, step_martix]
else:
answer_martix.append([(row, col), num, step_martix])
else:
answer_martix.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')
def main():
show_Time(info, time1)
root.config(menu=main_menu)
game_menu(main_menu)
auto_menu(main_menu)
question_menu(main_menu)
number_button(root)
control_button(root, side)
basics_info()
canvas.bind('<Button-1>', click_event)
show_fixa_num()
if __name__ == '__main__':
main()
tk.mainloop()
# config = Config()
# config.max_depth = 5