前言
使用python简单实现一下数独小游戏,并且使用tkinter进行界面展示。
一、题目生成
1.数独规则
- 在 9x9 的棋盘网格中将数字 1 ~ 9 填入空白格
- 每一列只能包含数字 1 到 9,不能重复
- 每一行只能包含数字 1 到 9,不能重复
- 每个 3×3 的小九宫格只能包含数字 1 到 9,每一列或每一行中的每个数字只能使用一次
2.生成初始题目
先直接得到若干个数独游戏的答案,然后再随机让一些数字变成待填入的空白格就OK了,而随机变成空白格的数量就决定着游戏难度的大小。
使用递归的方法实现,并且在create_board函数
返回答案和题目,来看代码:
# 生成题库
import random
import copy
def generate_sudoku_board():
# 创建一个9x9的二维列表,表示数独棋盘
board = [[0] * 9 for _ in range(9)]
# 递归函数,用于填充数独棋盘的每个单元格
def filling_board(row, col):
# 检查是否填充完成整个数独棋盘
if row == 9:
return True
# 计算下一个单元格的行和列索引
next_row = row if col < 8 else row + 1
next_col = (col + 1) % 9
# 获取当前单元格在小九宫格中的索引
box_row = row // 3
box_col = col // 3
# 随机生成1到9的数字
numbers = random.sample(range(1, 10), 9)
for num in numbers:
# 检查行、列、小九宫格是否已经存在相同的数字
if num not in board[row] and all(board[i][col] != num for i in range(9)) and all(num != board[i][j] for i in range(box_row*3, box_row*3+3) for j in range(box_col*3, box_col*3+3)):
board[row][col] = num
# 递归填充下一个单元格
if filling_board(next_row, next_col):
return True
# 回溯,将当前单元格重置为0
board[row][col] = 0
return False
# 填充数独棋盘
filling_board(0, 0)
return board
def create_board(level): # level数字越大代表游戏难度越大
"""
生成一个随机的数独棋盘,空白格少
"""
board = generate_sudoku_board()
board1 = copy.deepcopy(board)
for i in range(81):
row = i // 9
col = i % 9
if random.randint(0, 9) < level:
board1[row][col] = 0
return (board,board1)
打印一下结果
v = create_board(5)[1]
print(v)
>>>
[[1, 0, 0, 8, 0, 6, 0, 0, 4],
[5, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 6, 0, 7, 0, 2, 0, 0, 1],
[2, 0, 0, 3, 7, 9, 0, 0, 0],
[7, 0, 0, 6, 8, 0, 0, 3, 2],
[0, 0, 5, 4, 0, 0, 7, 6, 9],
[6, 0, 7, 0, 0, 8, 9, 4, 0],
[3, 0, 1, 0, 4, 0, 0, 0, 0],
[9, 0, 4, 5, 6, 0, 0, 2, 7]]
二、界面设计
这里选择使用tkinter
进行界面展示。
希望设计的界面效果如下:
UI部分的代码:
import tkinter as tk
import ctypes
root = tk.Tk()
# 界面优化代码---------------------------------
# 调用api设置成由应用程序缩放
ctypes.windll.shcore.SetProcessDpiAwareness(1)
# 调用api获得当前的缩放因子
ScaleFactor=ctypes.windll.shcore.GetScaleFactorForDevice(0)
# 设置缩放因子
root.tk.call('tk', 'scaling', ScaleFactor/75)
#--------------------------------------------
root.title('数独游戏')
root.geometry('900x1000')
frame = tk.Frame(root,width=500,height=500)
frame.pack()
# 绘制九宫格
def print_board(frame,board):
"""
在界面上显示数独棋盘
"""
for i in range(9):
for j in range(9):
if board[i][j] == 0:
label = tk.Label(frame, text="", width=3, height=2, font=("Arial", 16), bg="white")
else:
label = tk.Label(frame, text=str(board[i][j]), width=3, height=2, font=("Arial", 16), bg="white")
label.grid(row=i, column=j)
def XX(level):
global aa
aa = create_board(level)
return aa
board = XX(5)[1]
print_board(frame,board)
# 添加按键组件
def Button(root,level1,level2):
button1 = tk.Button(root, text="出题:难度1", command=lambda:print_board(frame,XX(level1)[1])) #注意加上lambda!
button1.pack(side=tk.LEFT, padx=10, pady=10)
button2 = tk.Button(root, text="出题:难度2", command=lambda:print_board(frame,XX(level2)[1]))
button2.pack(side=tk.LEFT, padx=10, pady=10)
button3 = tk.Button(root, text="解题",command=lambda:print_board(frame,aa[0]))
button3.pack(side=tk.LEFT, padx=10, pady=10)
button_quit = tk.Button(root,text='退出',command=root.quit)
button_quit.pack(side=tk.RIGHT, padx=10, pady=10)
Button(root,5,7)
root.mainloop()
最终效果:
三、升级优化
以上版本称之为1.0版本,只有出题和解题两个最基本的功能,没有交互的体验感,离一个真正的“数独”游戏还差的很远,下面对代码升级一下,加入用户交互的方式。
- 既然需要交互式设计,自然会想到采用
tk.Entry()
组件。因此绘制九宫格就从之前的文本显示修改为输入框显示,然后将已经出现的数字设置为“可读状态”,避免被修改;而对于待填入的空白格,将输入内容做个限制:只能填入1个数字,而且是1~9,输入其他内容时无效。思路有了,修改代码如下:
# 输入框验证函数
def validate_input(new_value):
if new_value.isdigit() and int(new_value) >= 1 and int(new_value) <= 9:
return True
return False
validate_cmd = frame.register(validate_input)
# 绘制九宫格
def print_board(frame,board):
"""
在界面上显示数独棋盘
"""
for i in range(9):
for j in range(9):
if board[i][j] == 0:
label = tk.Entry(frame,width=4,font=('TimesNewom',15,'bold'), validate="key", validatecommand=(validate_cmd, "%P"))
else:
label = tk.Entry(frame,width=4,font=('TimesNewom',15,'bold'), validate="key", validatecommand=(validate_cmd, "%P")) # 注意这里的参数要和上面的label一致,否则会出奇怪的bug~
label.insert(-1,str(board[i][j]))
label.config(state='readonly')
label.grid(row=i, column=j,padx=5,pady=5)
在上面的代码中,Entry组件中的validate
和validatecommand方法
用于设置验证类型和验证函数。validate_input函数
用于验证输入内容,只有当输入内容是1到9的数字时才返回True,否则返回False。root.register方法
用于将这个函数注册为一个验证函数,并返回一个函数名,这个函数名可以用于validatecommand方法
中。%P
表示当前输入框中的内容。
注意:两个entry组件中的参数必须要一致,否则界面就会出现奇怪的bug。
评论(0)
您还未登录,请登录后发表或查看评论