跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Python算法

Python+PyGame 实现数独游戏:多难度、实时验证与成绩记录

综述由AI生成一款使用 Python 和 PyGame 开发的数独游戏。项目支持初级、中级、高级三种难度,具备实时输入验证、提示系统及成绩记录功能。技术实现包括多线程后台加载优化、文件持久化存储及模块化代码设计。文章提供了完整的 SuDoKu.py 和 bulid.py 源代码,适合作为 Python GUI 编程和游戏开发入门案例。

字节跳动发布于 2026/3/23更新于 2026/4/238.6K 浏览
Python+PyGame 实现数独游戏:多难度、实时验证与成绩记录

项目亮点

  • ✅ 三种难度可选(初级 30 空/中级 45 空/高级 60 空)
  • ✅ 实时颜色提示(正确蓝色/错误红色)
  • ✅ 内置'提示'功能(显示答案 2 秒)
  • ✅ 成绩记录系统(分难度保存最快用时)
  • ✅ 流畅界面 + 加载进度优化
  • ✅ 完整游戏交互(键盘 + 鼠标)

功能概览

功能说明
难度切换初级/中级/高级,对应不同空格数量
实时验证输入后立即颜色反馈(蓝/红)
提示系统按 T 键或按钮显示当前格答案(持续 2 秒)
成绩记录自动保存各难度最佳用时,支持查看历史记录
重置游戏可随时重置当前难度盘面
帮助说明内置游戏规则与操作指南

核心技术实现

1. 游戏状态管理

game_states = {
    "初级": {"matrix": ..., "blank_ij": ..., "start_time": ...},
    "中级": {...},
    "高级": {...}
}
  • 每个难度独立存储状态,切换时不丢失进度
  • 包含盘面、空白格坐标、开始时间等信息

2. 多线程加载优化

def initialize_game_in_background():
    thread = threading.Thread(target=background_task)
    thread.daemon = True
    thread.start()
  • 后台生成所有难度初始盘面,避免主界面卡顿
  • 显示加载动画,提升用户体验

3. 实时视觉反馈

  • 根据数独规则实时检查输入数字合法性
  • 正确数字显示为蓝色,错误显示为红色
  • 提示数字使用紫色高亮(2 秒后消失)

4. 成绩持久化存储

best_times = {"初级": 120.5, "中级": 300.0, "高级": float('inf')}
  • 使用文本文件保存各难度最佳成绩
  • 首次完成时自动记录,支持破纪录提示

代码结构

sudoku/
├── main.py          # 主程序入口
├── bulid.py         # 数独生成与验证核心
└── resource/
    └── sudoku_best_times.txt  # 成绩记录文件

核心函数说明

  • give_me_a_game(blank_size) – 生成数独题目与答案
  • check(matrix, i, j, num) – 验证数字是否合法
  • draw_background() – 绘制游戏界面与网格
  • show_hint() – 显示当前格提示答案
  • show_records() – 弹出最佳成绩窗口
  • reset_game(difficulty) – 重置指定难度游戏

运行说明

环境要求

pip install pygame

启动游戏

python main.py

操作方式

  • 方向键/鼠标点击 – 移动选择格子
  • 数字键 1-9 – 填入数字
  • 空格/DEL/Backspace – 清除数字
  • 重置游戏
  • 请求提示
  • 查看帮助

可扩展方向

  1. 主题切换 – 增加深色/彩色主题选项
  2. 音效反馈 – 添加正确/错误提示音
  3. 解题器 – 集成自动求解功能
  4. 云同步 – 将成绩上传至网络排行榜

项目总结

该项目展示了如何用 Python + PyGame 构建一个完整的桌面游戏应用,涉及:

  • ✅ 状态管理 – 多难度独立存储
  • ✅ UI 交互 – 按钮、提示、对话框等组件
  • ✅ 文件存储 – 简单数据持久化方案
  • ✅ 性能优化 – 多线程避免界面卡顿
  • ✅ 代码结构 – 模块化设计,便于扩展

适合作为 Python GUI 编程、游戏开发入门 的实战案例。


完整代码

SuDoKu.py

import sys
import pygame
from pygame.color import THECOLORS as COLORS
import random
from bulid import give_me_a_game, check
import time
import os
import threading

# 全局变量
# 难度级别配置:空白格子数量、是否启用提示、是否实时检查
DIFFICULTY_LEVELS = {
    "初级": {"blank_size": 30, "hint": True, "real_time_check": True},
    "中级": {"blank_size": 45, "hint": True, "real_time_check": True},
    "高级": {"blank_size": 60, "hint": True, "real_time_check": True}
}

# 存储每个难度的最佳时间
best_times = {
    "初级": float('inf'),
    "中级": float('inf'),
    "高级": float('inf')
}

# 各难度的游戏状态存储
game_states = {
    "初级": {"start_time": 0, "elapsed_time": 0, "completed": False},
    "中级": {"start_time": 0, "elapsed_time": 0, "completed": False},
    "高级": {"start_time": 0, "elapsed_time": 0, "completed": False}
}

# 隐藏控制台窗口(仅 Windows 有效)
if os.name == 'nt':
    import ctypes
    ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)

def resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

BEST_TIMES_FILE = resource_path("resource/sudoku_best_times.txt")

def load_best_times():
    try:
        if os.path.exists(BEST_TIMES_FILE):
            with open(BEST_TIMES_FILE, 'r', encoding='utf-8') as f:
                for line in f:
                    difficulty, time_str = line.strip().split(':')
                    best_times[difficulty] = float(time_str)
    except Exception as e:
        print(f"加载最佳成绩失败:{e}")

def save_best_times():
    try:
        with open(BEST_TIMES_FILE, 'w', encoding='utf-8') as f:
            for difficulty, time_val in best_times.items():
                if time_val != float('inf'):
                    f.write(f"{difficulty}:{time_val}\n")
    except Exception as e:
        print(f"保存最佳成绩失败:{e}")

def draw_background():
    screen.fill(COLORS['whitesmoke'])
    pygame.draw.rect(screen, COLORS['lightgray'], (0, 0, 540, 40))
    pygame.draw.line(screen, COLORS['darkgray'], (0, 40), (540, 40), 2)
    pygame.draw.rect(screen, COLORS['white'], (0, 40, 540, 540))
    for i in range(0, 4):
        pygame.draw.line(screen, COLORS['black'], (0, 40 + i * 180), (540, 40 + i * 180), 3)
        pygame.draw.line(screen, COLORS['black'], (i * 180, 40), (i * 180, 580), 3)
    for i in range(0, 10):
        if i % 3 != 0:
            pygame.draw.line(screen, COLORS['lightgray'], (0, 40 + i * 60), (540, 40 + i * 60), 1)
            pygame.draw.line(screen, COLORS['lightgray'], (i * 60, 40), (i * 60, 580), 1)

def draw_choose():
    s = pygame.Surface((60, 60), pygame.SRCALPHA)
    s.fill((0, 255, 0, 50))
    screen.blit(s, (cur_j * 60, cur_i * 60 + 40))

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['blue']
    return COLORS['red']

def draw_number():
    game_state = game_states[current_difficulty]
    if not game_state:
        return
    MATRIX = game_state["matrix"]
    MATRIX_ANSWER = game_state["matrix_answer"]
    BLANK_IJ = game_state["blank_ij"]
    for i in range(9):
        for j in range(9):
            if game_state.get("hint_cell") == (i, j) and pygame.time.get_ticks() < game_state.get("hint_end_time", 0):
                value = MATRIX_ANSWER[i][j]
                color = COLORS['purple']
                txt = font.render(str(value), True, color)
                x, y = j * 60 + 30, i * 60 + 70
                screen.blit(txt, (x - txt.get_width() // 2, y - txt.get_height() // 2))
                continue
            value = MATRIX[i][j]
            if value == 0:
                continue
            if (i, j) not in BLANK_IJ:
                color = COLORS['black']
            else:
                color = COLORS['blue']
            if DIFFICULTY_SETTINGS['real_time_check'] and DIFFICULTY_SETTINGS['hint']:
                color = check_color(MATRIX, i, j)
            txt = font.render(str(value), True, color)
            x, y = j * 60 + 30, i * 60 + 70
            screen.blit(txt, (x - txt.get_width() // 2, y - txt.get_height() // 2))

def draw_difficulty_buttons():
    for i, level in enumerate(["初级", "中级", "高级"]):
        color = COLORS['lightgreen'] if current_difficulty == level else COLORS['lightblue']
        pygame.draw.rect(screen, color, (i * 180, 0, 180, 40))
        pygame.draw.rect(screen, COLORS['black'], (i * 180, 0, 180, 40), 1)
        try:
            txt = font_small.render(level, True, COLORS['black'])
        except:
            level_en = ["Easy", "Medium", "Hard"][i]
            txt = font_small.render(level_en, True, COLORS['black'])
        x, y = i * 180 + 90, 20
        screen.blit(txt, (x - txt.get_width() // 2, y - txt.get_height() // 2))

def draw_context():
    game_state = game_states[current_difficulty]
    if not game_state:
        return
    txt = font_tiny.render(f'空白:{game_state["cur_blank_size"]}', True, COLORS['black'])
    screen.blit(txt, (10, 610))
    txt = font_tiny.render(f'操作:{game_state["cur_change_size"]}', True, COLORS['black'])
    screen.blit(txt, (150, 610))
    pygame.draw.rect(screen, COLORS['lightblue'], (350, 585, 80, 30))
    pygame.draw.rect(screen, COLORS['black'], (350, 585, 80, 30), 1)
    try:
        txt_reset = font_tiny.render('重置 (R)', True, COLORS['black'])
    except:
        txt_reset = font_tiny.render('Reset', True, COLORS['black'])
    screen.blit(txt_reset, (390 - txt_reset.get_width() // 2, 600 - txt_reset.get_height() // 2))
    pygame.draw.rect(screen, COLORS['lightgreen'], (440, 585, 80, 30))
    pygame.draw.rect(screen, COLORS['black'], (440, 585, 80, 30), 1)
    try:
        txt_records = font_tiny.render('成绩', True, COLORS['black'])
    except:
        txt_records = font_tiny.render('Records', True, COLORS['black'])
    screen.blit(txt_records, (480 - txt_records.get_width() // 2, 600 - txt_records.get_height() // 2))
    pygame.draw.rect(screen, COLORS['lightyellow'], (350, 625, 80, 25))
    pygame.draw.rect(screen, COLORS['black'], (350, 625, 80, 25), 1)
    try:
        txt_hint = font_tiny.render('提示 (T)', True, COLORS['black'])
    except:
        txt_hint = font_tiny.render('Hint', True, COLORS['black'])
    screen.blit(txt_hint, (390 - txt_hint.get_width() // 2, 637 - txt_hint.get_height() // 2))
    pygame.draw.rect(screen, COLORS['lightpink'], (440, 625, 80, 25))
    pygame.draw.rect(screen, COLORS['black'], (440, 625, 80, 25), 1)
    try:
        txt_help = font_tiny.render('说明 (H)', True, COLORS['black'])
    except:
        txt_help = font_tiny.render('Help', True, COLORS['black'])
    screen.blit(txt_help, (480 - txt_help.get_width() // 2, 637 - txt_help.get_height() // 2))
    try:
        help_txt = font_tiny.render('方向键移动 | 数字键填写 | 空格/DEL 清除', True, COLORS['darkgray'])
    except:
        help_txt = font_tiny.render('Arrow:Move | Num:Input | Space/DEL:Clear', True, COLORS['darkgray'])
    screen.blit(help_txt, (10, 655))

def draw_message(message, color):
    msg_surface = font_small.render(message, True, color)
    msg_rect = msg_surface.get_rect(center=(270, 610))
    s = pygame.Surface((540, 40), pygame.SRCALPHA)
    s.fill((255, 255, 255, 200))
    screen.blit(s, (0, 590))
    screen.blit(msg_surface, msg_rect)
    pygame.display.flip()
    pygame.time.delay(1500)
    pygame.draw.rect(screen, COLORS['whitesmoke'], (0, 590, 540, 110))

def reset_game(difficulty):
    try:
        draw_message("正在生成新游戏...", COLORS['blue'])
    except:
        draw_message("Generating...", COLORS['blue'])
    pygame.display.flip()
    start_time = time.perf_counter()
    MATRIX_ANSWER, MATRIX, BLANK_IJ = give_me_a_game(blank_size=DIFFICULTY_LEVELS[difficulty]["blank_size"])
    elapsed = time.perf_counter() - start_time
    print(f"生成时间:{elapsed:.6f}秒")
    cur_blank_size = sum(1 for row in MATRIX for cell in row if cell == 0)
    cur_change_size = 0
    game_states[difficulty] = {
        "matrix_answer": MATRIX_ANSWER,
        "matrix": MATRIX,
        "blank_ij": set(BLANK_IJ),
        "cur_blank_size": cur_blank_size,
        "cur_change_size": cur_change_size,
        "hint_cell": None,
        "hint_end_time": 0,
        "show_reset_confirm": False,
        "start_time": pygame.time.get_ticks(),
        "completed": False
    }
    return game_states[difficulty]

def check_solution():
    game_state = game_states[current_difficulty]
    if not game_state:
        return False
    MATRIX = game_state["matrix"]
    MATRIX_ANSWER = game_state["matrix_answer"]
    for i in range(9):
        for j in range(9):
            if MATRIX[i][j] != MATRIX_ANSWER[i][j]:
                return False
    return True

def show_hint():
    game_state = game_states[current_difficulty]
    if not game_state:
        return
    if (cur_i, cur_j) not in game_state["blank_ij"]:
        try:
            draw_message("请选择空白格子", COLORS['red'])
        except:
            draw_message("Select a blank cell", COLORS['red'])
        return
    game_state["hint_cell"] = (cur_i, cur_j)
    game_state["hint_end_time"] = pygame.time.get_ticks() + 2000
    try:
        draw_message("显示正确答案 1 秒", COLORS['purple'])
    except:
        draw_message("Showing answer for 2 seconds", COLORS['purple'])

def show_help_screen():
    help_screen = pygame.display.set_mode((540, 700))
    pygame.display.set_caption("游戏说明")
    try:
        title_font = pygame.font.SysFont('Microsoft YaHei', 30)
        text_font = pygame.font.SysFont('Microsoft YaHei', 20)
    except:
        title_font = pygame.font.SysFont(None, 30)
        text_font = pygame.font.SysFont(None, 20)
    try:
        title = title_font.render("数独游戏规则", True, COLORS['black'])
    except:
        title = title_font.render("Sudoku Rules", True, COLORS['black'])
    texts = [
        "游戏目标:在 9x9 的格子中填入数字 1-9,使得:",
        "- 每一行包含 1-9 的所有数字,且不重复;",
        "- 每一列包含 1-9 的所有数字,且不重复;",
        "- 每一个 3x3 的宫格包含 1-9 的所有数字,且不重复。",
        "",
        "操作说明:",
        "- 方向键或鼠标点击选择格子;",
        "- 数字键 1-9 填写数字;",
        "- 空格键、DEL 或 Backspace 删除数字;",
        "- 顶部按钮切换难度;",
        "- 重置:生成新游戏;",
        "- 提交:检查解答是否正确;",
        "- 提示:显示当前格子的正确答案(1 秒)。",
        "",
        "难度说明:",
        "- 初级:30 个空格",
        "- 中级:45 个空格",
        "- 高级:60 个空格",
        "",
        "所有难度都有实时提示:",
        "- 正确数字显示为蓝色",
        "- 错误数字显示为红色",
        "",
        "按任意键返回游戏..."
    ]
    running = True
    while running:
        help_screen.fill(COLORS['whitesmoke'])
        help_screen.blit(title, (270 - title.get_width() // 2, 10))
        y_pos = 70
        for text in texts:
            try:
                rendered = text_font.render(text, True, COLORS['black'])
            except:
                rendered = text_font.render(text, True, COLORS['black'])
            help_screen.blit(rendered, (20, y_pos))
            y_pos += 25
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
                running = False
                pygame.display.set_mode(SIZE)
                pygame.display.set_caption("数独游戏")

def show_loading_screen():
    screen.fill(COLORS['whitesmoke'])
    try:
        loading_text = font_large.render("数独游戏加载中...", True, COLORS['blue'])
    except:
        loading_text = font_large.render("Sudoku Loading...", True, COLORS['blue'])
    text_rect = loading_text.get_rect(center=(SIZE[0] // 2, SIZE[1] // 2))
    screen.blit(loading_text, text_rect)
    pygame.display.flip()

def initialize_game_in_background():
    def background_task():
        for difficulty in DIFFICULTY_LEVELS:
            reset_game(difficulty)
    thread = threading.Thread(target=background_task)
    thread.daemon = True
    thread.start()
    while thread.is_alive():
        show_loading_screen()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        pygame.time.delay(50)

def show_records():
    records_screen = pygame.display.set_mode((540, 700))
    pygame.display.set_caption("成绩记录")
    try:
        title_font = pygame.font.SysFont('Microsoft YaHei', 40)
        text_font = pygame.font.SysFont('Microsoft YaHei', 30)
        hint_font = pygame.font.SysFont('Microsoft YaHei', 24)
    except:
        title_font = pygame.font.SysFont(None, 40)
        text_font = pygame.font.SysFont(None, 30)
        hint_font = pygame.font.SysFont(None, 24)
    load_best_times()
    running = True
    while running:
        records_screen.fill(COLORS['whitesmoke'])
        try:
            title = title_font.render("最佳成绩记录", True, COLORS['blue'])
        except:
            title = title_font.render("Best Records", True, COLORS['blue'])
        records_screen.blit(title, (270 - title.get_width() // 2, 40))
        y_pos = 120
        for level in ["初级", "中级", "高级"]:
            time_val = best_times[level]
            if time_val == float('inf'):
                time_str = "暂无记录"
            else:
                time_str = f"{time_val:.3f}秒"
            try:
                level_text = text_font.render(f"{level}:", True, COLORS['black'])
                time_text = text_font.render(time_str, True, COLORS['darkblue'])
            except:
                level_text = text_font.render(f"{level}:", True, COLORS['black'])
                time_text = text_font.render(time_str, True, COLORS['darkblue'])
            records_screen.blit(level_text, (150, y_pos))
            records_screen.blit(time_text, (260, y_pos))
            y_pos += 70
        pygame.draw.line(records_screen, COLORS['lightgray'], (50, y_pos + 20), (490, y_pos + 20), 2)
        y_pos += 50
        try:
            hint1 = hint_font.render("提示:", True, COLORS['darkred'])
            hint2 = hint_font.render("1. 成绩按不同难度分别记录", True, COLORS['black'])
            hint3 = hint_font.render("2. 只记录最快完成时间", True, COLORS['black'])
            hint4 = hint_font.render("3. 空白格子越多难度越高", True, COLORS['black'])
        except:
            hint1 = hint_font.render("Note:", True, COLORS['darkred'])
            hint2 = hint_font.render("1. Records are saved per difficulty", True, COLORS['black'])
            hint3 = hint_font.render("2. Only the fastest time is saved", True, COLORS['black'])
            hint4 = hint_font.render("3. More blanks = higher difficulty", True, COLORS['black'])
        records_screen.blit(hint1, (90, y_pos))
        records_screen.blit(hint2, (120, y_pos + 40))
        records_screen.blit(hint3, (120, y_pos + 80))
        records_screen.blit(hint4, (120, y_pos + 120))
        try:
            hint = hint_font.render("按任意键返回...", True, COLORS['darkgray'])
        except:
            hint = hint_font.render("Press any key to return...", True, COLORS['darkgray'])
        records_screen.blit(hint, (270 - hint.get_width() // 2, 620))
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
                running = False
                pygame.display.set_mode(SIZE)
                pygame.display.set_caption("数独游戏")

def draw_reset_confirm_dialog():
    s = pygame.Surface(SIZE, pygame.SRCALPHA)
    s.fill((0, 0, 0, 128))
    screen.blit(s, (0, 0))
    dialog_rect = pygame.Rect(120, 250, 300, 150)
    pygame.draw.rect(screen, COLORS['white'], dialog_rect)
    pygame.draw.rect(screen, COLORS['black'], dialog_rect, 2)
    try:
        title = font_small.render("确认重置游戏吗?", True, COLORS['black'])
    except:
        title = font_small.render("Confirm Reset Game?", True, COLORS['black'])
    screen.blit(title, (dialog_rect.centerx - title.get_width() // 2, dialog_rect.y + 20))
    confirm_rect = pygame.Rect(dialog_rect.x + 50, dialog_rect.y + 80, 80, 40)
    pygame.draw.rect(screen, COLORS['lightgreen'], confirm_rect)
    pygame.draw.rect(screen, COLORS['black'], confirm_rect, 1)
    try:
        confirm_text = font_tiny.render("确认", True, COLORS['black'])
    except:
        confirm_text = font_tiny.render("Yes", True, COLORS['black'])
    screen.blit(confirm_text, (confirm_rect.centerx - confirm_text.get_width() // 2, confirm_rect.centery - confirm_text.get_height() // 2))
    cancel_rect = pygame.Rect(dialog_rect.x + 170, dialog_rect.y + 80, 80, 40)
    pygame.draw.rect(screen, COLORS['lightcoral'], cancel_rect)
    pygame.draw.rect(screen, COLORS['black'], cancel_rect, 1)
    try:
        cancel_text = font_tiny.render("取消", True, COLORS['black'])
    except:
        cancel_text = font_tiny.render("No", True, COLORS['black'])
    screen.blit(cancel_text, (cancel_rect.centerx - cancel_text.get_width() // 2, cancel_rect.centery - cancel_text.get_height() // 2))
    return confirm_rect, cancel_rect

if __name__ == "__main__":
    pygame.init()
    try:
        font = pygame.font.SysFont('Microsoft YaHei', 36)
        font_small = pygame.font.SysFont('Microsoft YaHei', 24)
        font_tiny = pygame.font.SysFont('Microsoft YaHei', 20)
        font_large = pygame.font.SysFont('Microsoft YaHei', 48)
    except:
        try:
            font = pygame.font.SysFont('SimHei', 36)
            font_small = pygame.font.SysFont('SimHei', 24)
            font_tiny = pygame.font.SysFont('SimHei', 20)
            font_large = pygame.font.SysFont('SimHei', 48)
        except:
            font = pygame.font.SysFont(None, 36)
            font_small = pygame.font.SysFont(None, 24)
            font_tiny = pygame.font.SysFont(None, 20)
            font_large = pygame.font.SysFont(None, 48)
    current_difficulty = "初级"
    DIFFICULTY_SETTINGS = DIFFICULTY_LEVELS[current_difficulty]
    SIZE = (540, 700)
    screen = pygame.display.set_mode(SIZE)
    pygame.display.set_caption("数独游戏")
    show_loading_screen()
    initialize_game_in_background()
    cur_i, cur_j = 0, 0
    reset_confirm_active = False
    running = True
    load_best_times()
    while running:
        game_state = game_states[current_difficulty]
        if not game_state:
            continue
        DIFFICULTY_SETTINGS = DIFFICULTY_LEVELS[current_difficulty]
        if game_state.get("hint_end_time", 0) > 0 and pygame.time.get_ticks() > game_state["hint_end_time"]:
            game_state["hint_cell"] = None
            game_state["hint_end_time"] = 0
        draw_background()
        draw_difficulty_buttons()
        draw_number()
        draw_choose()
        draw_context()
        confirm_rect = cancel_rect = None
        if reset_confirm_active:
            confirm_rect, cancel_rect = draw_reset_confirm_dialog()
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                break
            if reset_confirm_active:
                if event.type == pygame.MOUSEBUTTONDOWN:
                    x, y = event.pos
                    if confirm_rect.collidepoint(x, y):
                        game_state = reset_game(current_difficulty)
                        try:
                            draw_message("游戏已重置", COLORS['blue'])
                        except:
                            draw_message("Game Reset", COLORS['blue'])
                        reset_confirm_active = False
                    elif cancel_rect.collidepoint(x, y) or not confirm_rect.collidepoint(x, y):
                        reset_confirm_active = False
                        continue
            if event.type == pygame.MOUSEBUTTONDOWN:
                x, y = event.pos
                if y <= 40:
                    if x < 180:
                        current_difficulty = "初级"
                    elif x < 360:
                        current_difficulty = "中级"
                    else:
                        current_difficulty = "高级"
                    cur_i, cur_j = 0, 0
                    DIFFICULTY_SETTINGS = DIFFICULTY_LEVELS[current_difficulty]
                    try:
                        draw_message(f"已切换到{current_difficulty}难度", COLORS['blue'])
                    except:
                        draw_message(f"Switched to {current_difficulty}", COLORS['blue'])
                elif 40 <= y < 580:
                    cur_j = x // 60
                    cur_i = (y - 40) // 60
                elif 350 <= x <= 430 and 585 <= y <= 615:
                    reset_confirm_active = True
                elif 440 <= x <= 520 and 585 <= y <= 615:
                    show_records()
                elif 350 <= x <= 430 and 625 <= y <= 650:
                    show_hint()
                elif 440 <= x <= 520 and 625 <= y <= 650:
                    show_help_screen()
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_UP and cur_i > 0:
                    cur_i -= 1
                elif event.key == pygame.K_DOWN and cur_i < 8:
                    cur_i += 1
                elif event.key == pygame.K_LEFT and cur_j > 0:
                    cur_j -= 1
                elif event.key == pygame.K_RIGHT and cur_j < 8:
                    cur_j += 1
                elif event.key in [pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, pygame.K_5, pygame.K_6, pygame.K_7, pygame.K_8, pygame.K_9] and (cur_i, cur_j) in game_state["blank_ij"]:
                    if game_state["matrix"][cur_i][cur_j] != int(chr(event.key)):
                        game_state["matrix"][cur_i][cur_j] = int(chr(event.key))
                        game_state["cur_blank_size"] = sum(1 for row in game_state["matrix"] for cell in row if cell == 0)
                        game_state["cur_change_size"] += 1
                elif event.key in [pygame.K_DELETE, pygame.K_BACKSPACE, pygame.K_SPACE, pygame.K_0] and (cur_i, cur_j) in game_state["blank_ij"]:
                    if game_state["matrix"][cur_i][cur_j] != 0:
                        game_state["matrix"][cur_i][cur_j] = 0
                        game_state["cur_blank_size"] = sum(1 for row in game_state["matrix"] for cell in row if cell == 0)
                        game_state["cur_change_size"] += 1
                elif event.key == pygame.K_r:
                    reset_confirm_active = True
                elif event.key == pygame.K_t:
                    show_hint()
                elif event.key == pygame.K_h:
                    show_help_screen()
                elif event.key == pygame.K_ESCAPE and reset_confirm_active:
                    reset_confirm_active = False
                if not reset_confirm_active and check_win(game_state["matrix_answer"], game_state["matrix"]):
                    game_state["completed"] = True
                    elapsed = (pygame.time.get_ticks() - game_state["start_time"]) / 1000.0
                    if elapsed < best_times[current_difficulty]:
                        best_times[current_difficulty] = elapsed
                        save_best_times()
                        message = f"恭喜!新纪录:{elapsed:.1f}秒"
                    else:
                        message = f"完成时间:{elapsed:.1f}秒"
                    draw_message(message, COLORS['green'])
                    pygame.time.delay(3000)
                    game_state = reset_game(current_difficulty)
    pygame.quit()

bulid.py

import random
import time

def print_matrix(matrix):
    print('—' * 19)
    for row in matrix:
        print('|' + ' '.join([str(col) if col != 0 else ' ' for col in row]) + '|')
    print('—' * 19)

def shuffle_number(_list):
    random.shuffle(_list)
    return _list

def check(matrix, i, j, number):
    if number in matrix[i]:
        return False
    for row in matrix:
        if row[j] == number:
            return False
    group_i, group_j = i // 3, j // 3
    for x in range(group_i * 3, group_i * 3 + 3):
        for y in range(group_j * 3, group_j * 3 + 3):
            if matrix[x][y] == number:
                return False
    return True

def solve_sudoku(matrix):
    for i in range(9):
        for j in range(9):
            if matrix[i][j] == 0:
                for num in shuffle_number(number_list[:]):
                    if check(matrix, i, j, num):
                        matrix[i][j] = num
                        if solve_sudoku(matrix):
                            return matrix
                        matrix[i][j] = 0
    return None

def generate_full_sudoku():
    empty_grid = [[0] * 9 for _ in range(9)]
    first_row = shuffle_number(number_list[:])
    empty_grid[0] = first_row
    solution = solve_sudoku(empty_grid)
    return solution

def give_me_a_game(blank_size=30):
    solution = generate_full_sudoku()
    if solution is None:
        solution = [
            [5, 3, 4, 6, 7, 8, 9, 1, 2],
            [6, 7, 2, 1, 9, 5, 3, 4, 8],
            [1, 9, 8, 3, 4, 2, 5, 6, 7],
            [8, 5, 9, 7, 6, 1, 4, 2, 3],
            [4, 2, 6, 8, 5, 3, 7, 9, 1],
            [7, 1, 3, 9, 2, 4, 8, 5, 6],
            [9, 6, 1, 5, 3, 7, 2, 8, 4],
            [2, 8, 7, 4, 1, 9, 6, 3, 5],
            [3, 4, 5, 2, 8, 6, 1, 7, 9]
        ]
    blank_grid = [row[:] for row in solution]
    blank_size = min(max(blank_size, 10), 60)
    all_positions = [(i, j) for i in range(9) for j in range(9)]
    blank_positions = random.sample(all_positions, blank_size)
    for i, j in blank_positions:
        blank_grid[i][j] = 0
    time.sleep(0.001)
    print_matrix(solution)
    return solution, blank_grid, blank_positions

number_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]

目录

  1. 项目亮点
  2. 功能概览
  3. 核心技术实现
  4. 1. 游戏状态管理
  5. 2. 多线程加载优化
  6. 3. 实时视觉反馈
  7. 4. 成绩持久化存储
  8. 代码结构
  9. 核心函数说明
  10. 运行说明
  11. 环境要求
  12. 启动游戏
  13. 操作方式
  14. 可扩展方向
  15. 项目总结
  16. 完整代码
  17. SuDoKu.py
  18. 全局变量
  19. 难度级别配置:空白格子数量、是否启用提示、是否实时检查
  20. 存储每个难度的最佳时间
  21. 各难度的游戏状态存储
  22. 隐藏控制台窗口(仅 Windows 有效)
  23. bulid.py
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • GitHub 界面本地化插件:效率提升与全中文体验技巧
  • 通义万相 2.1 开源视频生成模型功能解析
  • 数据库 SQL 防火墙:拦截率 99.99% 及恶意 SQL 防护
  • RISC-V开源处理器实战:从Verilog RTL设计到FPGA原型验证
自然语言处理在医疗领域的应用与实战
  • Flutter for OpenHarmony 底部导航与 TabBar 多页切换
  • 宇树机器人 G1 二次开发:导航仿真与地图转换教程
  • Flutter 三方库 whatsapp_bot_flutter 在 OpenHarmony 适配指南
  • 解决新机型 Copilot 键替代右 Ctrl 键问题
  • Claude Code 高级编程技巧与实战项目详解
  • 龙虾机器人(OpenClaw)本地部署完全技术指南
  • 向日葵 MCP 接入 AI:实现跨设备远程控制与自动化操作
  • 微调模型成本太高,用 RAG 技术实现低成本 AI 升级
  • C++ 多线程同步之原子操作(atomic)实战
  • Qwen3-TTS-Tokenizer-12Hz在AR眼镜实时语音交互中的低延迟应用
  • Linux 常用开发工具指南:软件包管理与 Vim 编辑器
  • 操作系统智能助手 OS Copilot 新功能测评
  • 哈希表的数据结构与实现详解
  • Visual C++ 运行库修复指南:解决软件启动失败问题
  • OpenSC2K 开源模拟城市 2000 重制版运行指南
  • 相关免费在线工具

    • 加密/解密文本

      使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

    • Gemini 图片去水印

      基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

    • curl 转代码

      解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

    • Markdown转HTML

      将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online