Python 小游戏实战:打造视觉精美的数独小游戏
我们将使用 pygame 来实现一个相对美观、带有颜色区分、动画提示、错误高亮、胜利动画的数独游戏。
核心功能规划(最终版本)
- 9×9 标准数独棋盘
- 预设难度(简单/中等/困难)——不同挖空数量
- 数字输入(键盘 1~9)
- 鼠标点击选择格子
- 错误输入红色闪烁提示
- 正确输入绿色短暂高亮
- 完成时胜利动画 + 庆祝文字
- 计时器
- “提示”功能(只填一个正确数字)
- 可选:撤销功能(简单实现)
技术栈建议(2025-2026 常用组合)
Python 3.9+
pygame 2.5+ 或 2.6+
pygame-menu (可选,美观菜单)
numpy (可选,方便数独数组操作)
完整代码框架(可直接运行版)
import pygame
import random
import copy
import time
# ==================== 基本配置 ====================
WIDTH, HEIGHT = 540, 700
CELL_SIZE = 60
GRID_SIZE = 9
THICK_LINE = 4
THIN_LINE = 2
FPS = 60
# 颜色(现代深色/浅色主题可选)
BG_COLOR = (28, 28, 36) # 深灰背景
GRID_COLOR = (60, 60, 70)
THICK_LINE_COLOR = (180, 180, 220)
NUMBER_COLOR = (220, 220, 255)
GIVEN_COLOR = (100, 180, 255) # 题目给的数字 - 蓝色
USER_COLOR = (255, 220, 100) # 用户填的 - 暖黄
ERROR_COLOR = (255, 80, 80)
CORRECT_COLOR = (100, 255, 140)
SELECT_COLOR = (80, 180, 255, 80) # 半透明选择框
WIN_COLOR = (50, 205, 50)
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("数独 - 美观版")
clock = pygame.time.Clock()
font = pygame.font.SysFont("Microsoft YaHei", 40, bold=True)
small_font = pygame.font.SysFont("Microsoft YaHei", 24)
# ==================== 数独核心逻辑 ====================
def is_valid(board, row, col, num):
# 行、列、3x3宫 检查
for i in range(9):
if board[row][i] == num or board[i][col] == num:
return False
start_row, start_col = 3 * (row // 3), 3 * (col // 3)
for i in range(3):
for j in range(3):
if board[start_row + i][start_col + j] == num:
return False
return True
def solve_sudoku(board):
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for num in range(1, 10):
if is_valid(board, row, col, num):
board[row][col] = num
if solve_sudoku(board):
return True
board[row][col] = 0
return False
return True
def generate_sudoku(difficulty="medium"):
# difficulty: easy/medium/hard → 挖空数量
empty_cells = {"easy": 35, "medium": 45, "hard": 55}[difficulty]
# 先创建一个完整解
board = [[0] * 9 for _ in range(9)]
numbers = list(range(1, 10))
for i in range(9):
random.shuffle(numbers)
for j in range(9):
if board[i][j] == 0:
for num in numbers:
if is_valid(board, i, j, num):
board[i][j] = num
break
solve_sudoku(board) # 确保有解
# 挖空
solution = copy.deepcopy(board)
positions = [(i, j) for i in range(9) for j in range(9)]
random.shuffle(positions)
for _ in range(empty_cells):
r, c = positions.pop()
board[r][c] = 0
return board, solution
# ==================== 主游戏类 ====================
class SudokuGame:
def __init__(self, difficulty="medium"):
self.board, self.solution = generate_sudoku(difficulty)
self.user_board = copy.deepcopy(self.board)
self.selected = None # (row, col)
self.message = ""
self.message_timer = 0
self.start_time = time.time()
self.won = False
def draw_grid(self):
for i in range(10):
thickness = THICK_LINE if i % 3 == 0 else THIN_LINE
color = THICK_LINE_COLOR if i % 3 == 0 else GRID_COLOR
pygame.draw.line(screen, color, (60, 60 + i * CELL_SIZE),
(540, 60 + i * CELL_SIZE), thickness)
pygame.draw.line(screen, color, (60 + i * CELL_SIZE, 60),
(60 + i * CELL_SIZE, 600), thickness)
def draw_numbers(self):
for r in range(9):
for c in range(9):
num = self.user_board[r][c]
if num == 0:
continue
color = GIVEN_COLOR if self.board[r][c] != 0 else USER_COLOR
text = font.render(str(num), True, color)
x = 60 + c * CELL_SIZE + (CELL_SIZE - text.get_width()) // 2
y = 60 + r * CELL_SIZE + (CELL_SIZE - text.get_height()) // 2
screen.blit(text, (x, y))
def draw_selected(self):
if self.selected:
r, c = self.selected
s = pygame.Surface((CELL_SIZE, CELL_SIZE), pygame.SRCALPHA)
s.fill(SELECT_COLOR)
screen.blit(s, (60 + c * CELL_SIZE, 60 + r * CELL_SIZE))
def draw_message(self):
if self.message and time.time() - self.message_timer < 2:
color = ERROR_COLOR if "错误" in self.message else CORRECT_COLOR
text = small_font.render(self.message, True, color)
screen.blit(text, (WIDTH // 2 - text.get_width() // 2, 620))
def draw_timer(self):
elapsed = int(time.time() - self.start_time)
mins, secs = divmod(elapsed, 60)
text = small_font.render(f"用时: {mins:02d}:{secs:02d}", True, (200, 200, 220))
screen.blit(text, (20, 620))
def draw_win(self):
if self.won:
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 160))
screen.blit(overlay, (0, 0))
text = font.render("恭喜你!数独完成!", True, WIN_COLOR)
screen.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2 - 60))
small = small_font.render(f"用时 {int(time.time() - self.start_time)} 秒", True, (220, 220, 220))
screen.blit(small, (WIDTH // 2 - small.get_width() // 2, HEIGHT // 2 + 20))
def handle_input(self, num):
if not self.selected or self.won:
return
r, c = self.selected
if self.board[r][c] != 0: # 原始题目不能改
return
if num == 0: # 删除
self.user_board[r][c] = 0
return
if num == self.solution[r][c]:
self.user_board[r][c] = num
self.message = "正确!"
self.message_timer = time.time()
else:
self.message = "错误!请重试"
self.message_timer = time.time()
def check_win(self):
return all(self.user_board[r][c] == self.solution[r][c]
for r in range(9) for c in range(9))
def run(self):
running = True
while running:
screen.fill(BG_COLOR)
self.draw_grid()
self.draw_selected()
self.draw_numbers()
self.draw_message()
self.draw_timer()
self.draw_win()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
if 60 <= x <= 540 and 60 <= y <= 600:
col = (x - 60) // CELL_SIZE
row = (y - 60) // CELL_SIZE
self.selected = (row, col)
elif event.type == pygame.KEYDOWN:
if event.key in (pygame.K_1, pygame.K_2, ..., pygame.K_9):
num = event.key - pygame.K_0
self.handle_input(num)
elif event.key == pygame.K_BACKSPACE or event.key == pygame.K_DELETE:
self.handle_input(0)
elif event.key == pygame.K_ESCAPE:
self.selected = None
if self.check_win() and not self.won:
self.won = True
self.message = "完成!"
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
if __name__ == "__main__":
game = SudokuGame(difficulty="medium")
game.run()
快速改进方向(可按需添加)
- 难度选择菜单(用 pygame-menu 库最快)
- 提示功能(随机找一个空位填入 solution 的值)
- 撤销栈(保存每一步操作)
- 错误计数(超过3次提示失败)
- 胜利粒子动画(用简单粒子系统)
- 音效(正确/错误/胜利音)
- 保存进度(json 文件)
推荐下一步行动
如果你现在想继续做这个项目,建议先回答以下问题,我可以给你最匹配的下一段代码:
- 你希望先加上难度选择菜单吗?
- 想要加入“提示”按钮功能吗?
- 需要错误次数限制 + 游戏失败判定吗?
- 想先美化界面(渐变背景、数字动画等)还是先完善逻辑?
- 你电脑上是否已经安装了 pygame?(pip install pygame)
告诉我你的优先级,我马上给你下一部分最实用的代码和实现细节~