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.

1109 lines
37 KiB

11 months ago
# -*- 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('<Button-1>', click_event)
# info.bind('<Button-1>', click_event)
# time.sleep(3)
# label_destroy_info(info_labels, canvas)
show_fixa_num()
tk.mainloop()
# test2()