Python打造视觉精美的数独小游戏实战记录
一、前言 想用Python做点有趣的东西?数独游戏是个绝佳的练手项目。它逻辑清晰,规则简单,但实现起来却能让你把Python的基础语法和Pygame图形库玩个遍。 今天,我们就来动手打造一个视觉精美、交互流畅的数独小游戏。从生成谜题到绘制界面,再到处理鼠标键盘事件,整个过程就像搭积木一样,一步步把想
一、前言
想用Python做点有趣的东西?数独游戏是个绝佳的练手项目。它逻辑清晰,规则简单,但实现起来却能让你把Python的基础语法和Pygame图形库玩个遍。
今天,我们就来动手打造一个视觉精美、交互流畅的数独小游戏。从生成谜题到绘制界面,再到处理鼠标键盘事件,整个过程就像搭积木一样,一步步把想法变成现实。无论你是刚学完Python语法想找项目练手,还是对游戏开发感兴趣,这篇教程都能带你走完全程。
准备好了吗?让我们开始吧。
二、开发准备
2.1 所需环境
- Python 3.6 或更高版本
- Pygame库(负责图形界面和交互)
2.2 安装依赖
Pygame不是Python自带的,所以第一步就是安装它。打开你的命令行工具,输入下面这行命令,回车即可:
pip install pygame
安装过程通常很快。如果遇到网络问题,可以尝试加上国内镜像源,比如 pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple。
三、项目结构设计
为了让代码清晰好维护,我们把项目拆成两个核心文件:
main.py:这是游戏的前端,负责一切你看得见的部分——窗口、网格、数字、颜色,以及处理你的鼠标点击和键盘输入。build.py:这是游戏的后端引擎,负责生成数独谜题、验证你填的数字对不对。它只管逻辑,不管显示。
前后端分离,思路清晰,以后想加新功能或者改界面,也不会牵一发而动全身。
四、核心功能实现
4.1. 数独谜题生成与验证(build.py)
数独游戏的核心,首先得有一个合法的谜题。这里我们采用经典的递归回溯算法来生成一个完整的数独盘面,然后再随机“挖”掉一些格子,形成题目。
打开build.py,我们来实现这个逻辑引擎:
import random
def print_matrix(matrix):
"""打印矩阵,用于调试"""
print('—'*19)
for row in matrix:
print('|'+' '.join([str(col) for col in row])+'|')
print('—'*19)
def shuffle_number(_list):
"""随机打乱列表,用于生成不同的数独"""
random.shuffle(_list)
return _list
def check(matrix, i, j, number):
"""
检查数字是否可以放置在指定位置
规则:行、列、3x3子网格中不能有重复数字
"""
# 检查行
if number in matrix[i]:
return False
# 检查列
if number in [row[j] for row in matrix]:
return False
# 检查3x3子网格
group_i, group_j = int(i/3), int(j/3)
if number in [matrix[i][j] for i in range(group_i*3, (group_i+1)*3)
for j in range(group_j*3, (group_j+1)*3)]:
return False
return True
def build_game(matrix, i, j, number):
"""递归构建完整数独矩阵"""
if i > 8 or j > 8:
return matrix
if check(matrix, i, j, number):
# 创建矩阵副本进行修改
_matrix = [[col for col in row] for row in matrix]
_matrix[i][j] = number
# 计算下一个位置
next_i, next_j = (i+1, 0) if j == 8 else (i, j+1)
# 尝试下一个位置的所有可能数字
for _number in shuffle_number(number_list):
__matrix = build_game(_matrix, next_i, next_j, _number)
# 验证是否生成了完整有效的数独
if __matrix and sum([sum(row) for row in __matrix]) == (sum(range(1,10)) * 9):
return __matrix
return None
def give_me_a_game(blank_size=9):
"""生成带空白的数独游戏"""
# 生成完整数独
matrix_all = build_game(matrix, 0, 0, random.choice(number_list))
# 随机生成要空白的位置
set_ij = set()
while len(list(set_ij)) < blank_size:
set_ij.add(f"{random.choice(range(9))},{random.choice(range(9))}")
# 创建带空白的数独
matrix_blank = [[col for col in row] for row in matrix_all]
blank_ij = []
for ij in list(set_ij):
i, j = int(ij.split(',')[0]), int(ij.split(',')[1])
blank_ij.append((i, j))
matrix_blank[i][j] = 0
return matrix_all, matrix_blank, blank_ij
# 全局变量
number_list = [1,2,3,4,5,6,7,8,9]
matrix = [([0]*9) for i in range(9)]
if __name__ == "__main__":
# 测试生成数独
print_matrix(build_game(matrix, 0, 0, random.choice(number_list)))
这段代码的妙处在于build_game函数。它从一个空盘面开始,尝试在每个格子填入数字。如果当前数字合法,就递归地去填下一个格子;如果走到死胡同(所有数字都试了都不行),就回溯到上一步。最终,它要么生成一个完美的数独答案,要么返回None。而give_me_a_game函数则负责在生成答案后,随机挖掉指定数量的格子,把答案、题目以及空白格的位置一起返回给前端。
4.2. 游戏界面与交互(main.py)
逻辑有了,接下来就是让它变得“好看”和“好玩”。main.py文件承载了所有关于视觉和交互的代码。
import sys
import pygame
from pygame.color import THECOLORS as COLORS
from build import print_matrix, give_me_a_game, check
def draw_background():
"""绘制游戏背景和网格"""
# 白色背景
screen.fill(COLORS['white'])
# 绘制9x9网格,加粗绘制3x3大格子边界
# 垂直线
for i in range(10):
line_width = 5 if i % 3 == 0 else 2
pygame.draw.line(screen, COLORS['black'],
(i*100, 0), (i*100, 900), line_width)
# 水平线
for i in range(10):
line_width = 5 if i % 3 == 0 else 2
pygame.draw.line(screen, COLORS['black'],
(0, i*100), (900, i*100), line_width)
def draw_choose():
"""绘制当前选中的单元格高亮框"""
# 绘制蓝色高亮框,留出边框
pygame.draw.rect(screen, COLORS['blue'],
(cur_j*100+5, cur_i*100+5, 100-10, 100-10), 0)
def check_win(matrix_all, matrix):
"""检查是否完成数独"""
return matrix_all == matrix
def check_color(matrix, i, j):
"""检查数字是否符合规则,返回对应的颜色"""
_matrix = [[col for col in row] for row in matrix]
_matrix[i][j] = 0 # 临时清空当前位置以便检查
if check(_matrix, i, j, matrix[i][j]):
return COLORS['green'] # 合法数字显示绿色
return COLORS['red'] # 非法数字显示红色
def draw_number():
"""绘制数独中的数字"""
for i in range(len(MATRIX)):
for j in range(len(MATRIX[0])):
# 空白格子的数字颜色根据合法性变化,固定数字显示灰色
_color = check_color(MATRIX, i, j) if (i,j) in BLANK_IJ else COLORS['gray']
# 渲染数字,空值不显示
txt = font80.render(
str(MATRIX[i][j] if MATRIX[i][j] not in [0, '0'] else ''),
True, _color)
x, y = j*100 + 30, i*100 + 10 # 居中显示
screen.blit(txt, (x, y))
def draw_context():
"""绘制游戏状态信息"""
txt = font100.render(
f'Blank: {cur_blank_size} Changes: {cur_change_size}',
True, COLORS['black'])
screen.blit(txt, (10, 900)) # 显示在底部
if __name__ == "__main__":
# 初始化pygame
pygame.init()
# 常量定义
SIZE = [900, 1000] # 窗口大小,额外100像素显示状态信息
font80 = pygame.font.SysFont('Times', 80) # 数字字体
font100 = pygame.font.SysFont('Times', 90) # 状态信息字体
# 创建窗口
screen = pygame.display.set_mode(SIZE)
pygame.display.set_caption("数独小游戏") # 设置窗口标题
# 变量初始化
cur_i, cur_j = 0, 0 # 当前选中的单元格
cur_blank_size = int(sys.argv[1]) if len(sys.argv) > 1 else 10 # 空白数量,可通过命令行参数设置
cur_change_size = 0 # 已修改次数
# 生成数独游戏
MATRIX_ANSWER, MATRIX, BLANK_IJ = give_me_a_game(blank_size=cur_blank_size)
print(BLANK_IJ)
print_matrix(MATRIX)
# 主游戏循环
running = True
while running:
# 事件处理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
break
elif event.type == pygame.MOUSEBUTTONDOWN:
# 鼠标点击选择单元格
x, y = event.pos
cur_j, cur_i = int(x / 100), int(y / 100)
# 确保选择在有效范围内
cur_i = max(0, min(8, cur_i))
cur_j = max(0, min(8, cur_j))
elif event.type == pygame.KEYUP:
# 键盘输入数字
if chr(event.key) in ['1','2','3','4','5','6','7','8','9'] and (cur_i, cur_j) in BLANK_IJ:
MATRIX[cur_i][cur_j] = int(chr(event.key))
# 更新空白数量
cur_blank_size = sum(1 for row in MATRIX for col in row if col in [0, '0'])
cur_change_size += 1
# 绘制游戏元素
draw_background()
draw_choose()
draw_number()
draw_context()
# 刷新屏幕
pygame.display.flip()
# 检查是否完成
if check_win(MATRIX_ANSWER, MATRIX):
print('恭喜你,完成了数独挑战!')
break
# 退出游戏
pygame.quit()
代码看起来不少,但结构非常清晰。主循环while running是游戏的心脏,它不断做三件事:监听你的操作(事件处理)、根据当前状态重绘屏幕、判断游戏是否结束。几个绘图函数各司其职,把背景、网格、数字、高亮框和状态信息一层层画到屏幕上。
运行效果图:

五、功能解析
5.1. 数独生成逻辑
- 递归回溯:
build_game()函数是核心,它尝试所有可能的数字组合,利用递归深入,遇到矛盾则回溯,最终生成一个合法解。 - 挖洞生成谜题:
give_me_a_game()函数在得到完整答案后,随机将指定数量的格子清零,并记录下这些空白格的位置,生成可玩的题目。 - 实时验证:
check()函数是规则的守护者,确保你填入的每个数字都满足行、列、宫格内不重复的约束。
5.2. 游戏界面设计
- 清晰的网格:用细线画9x9小格,用粗线区分3x3的宫格,这是数独的标准视觉呈现。
- 视觉反馈:选中的格子有蓝色高亮;题目自带的固定数字是灰色的,不可更改;而你填入的数字,会实时检查——绿色代表合法,红色则提醒你与现有数字冲突了。
- 状态提示:窗口底部实时显示还剩多少空白格,以及你已经修改了多少次,让你对自己的游戏进度一目了然。
5.3. 用户交互
- 鼠标选择:点到哪,就选到哪,简单直观。
- 键盘输入:选中空白格后,直接按数字键1-9即可填入,操作流畅。
- 即时反馈:输入后颜色立刻变化,对错立判,学习成本极低。
- 自动结束:当你填满所有格子且答案完全正确时,游戏会自动在控制台恭喜你,然后优雅退出。
六、游戏玩法
启动游戏:在命令行中运行程序。你可以指定难度(空白格数量),不指定则默认为10格。
python main.py 20 # 生成一个有20个空白格的数独(更难)
选择格子:用鼠标点击你想填数字的格子,它会变成蓝色。
填入数字:直接按键盘上的数字键1到9。
观察反馈:填入的数字会显示颜色。绿色表示目前没有冲突,可以放心;红色则表示这个数字在当前行、列或宫格里重复了,需要修改。
赢得游戏:当所有空白格都被正确填满,游戏结束,控制台会弹出成功提示。
七、优化建议
一个基础版本完成了,但让它变得更好玩,还有很大的空间。这里有几个方向供你参考:
- 图形化难度选择:别再用命令行参数了,做个开始菜单,让玩家用鼠标点选“简单”、“中等”、“困难”。
- 加入计时器:在界面角落增加一个计时器,记录玩家通关用时,增加挑战性。
- 错误次数限制:比如只允许犯5次错误(输入红色数字算一次),增加紧张感。
- 提示功能:当玩家卡住时,可以按“H”键获得一个正确数字的提示。
- 美化UI:换掉纯色背景,使用更柔和的颜色、添加数字填入时的动画效果,甚至加入音效。
- 保存与读取:实现游戏进度的保存,下次打开可以接着玩。
八、总结
走到这里,一个由Python和Pygame驱动的数独游戏就从无到有地诞生了。这个项目虽然不大,但“麻雀虽小,五脏俱全”。你不仅实践了递归回溯算法来解决生成问题,还亲手用Pygame搭建了一个包含事件处理、图形绘制、状态管理的完整游戏循环。
更重要的是,你看到了清晰的代码结构如何让项目易于理解和扩展。无论是想增加新功能,还是修改游戏规则,现在你都有了坚实的基础。
希望这个亲手打造的游戏能带给你成就感,也为你打开Python游戏开发的大门。接下来的路,就由你自由探索和创造了。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















