基于Python+PyGame实现的一款功能完整的数独游戏,支持多难度选择、实时验证、提示系统、成绩记录,并采用多线程优化加载体验。(文末附全部代码)

基于Python+PyGame实现的一款功能完整的数独游戏,支持多难度选择、实时验证、提示系统、成绩记录,并采用多线程优化加载体验。(文末附全部代码)

✨ 项目亮点

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

📌 一、功能概览

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

🧩 二、核心技术实现

1. 游戏状态管理

python

game_states = { "初级": {"matrix": ..., "blank_ij": ..., "start_time": ...}, "中级": {...}, "高级": {...} }

  • 每个难度独立存储状态,切换时不丢失进度
  • 包含盘面、空白格坐标、开始时间等信息

2. 多线程加载优化

python

def initialize_game_in_background(): thread = threading.Thread(target=background_task) thread.daemon = True thread.start()

  • 后台生成所有难度初始盘面,避免主界面卡顿
  • 显示加载动画,提升用户体验

3. 实时视觉反馈

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

4. 成绩持久化存储

python

best_times = {"初级": 120.5, "中级": 300.0, "高级": float('inf')}

  • 使用文本文件保存各难度最佳成绩
  • 首次完成时自动记录,支持破纪录提示

📁 三、代码结构

text

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) – 重置指定难度游戏

🎯 四、运行说明

环境要求

bash

pip install pygame

启动游戏

bash

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}") pass # 如果文件损坏,忽略错误 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)) # 绘制游戏区域网格 # 绘制粗线(3x3的大格子) 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 # 如果是0就不绘制 # 初始题目数字用黑色 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}秒") # 显示6位小数 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 # 2秒后结束提示 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) # 短暂延迟,减少CPU占用 def show_records(): """显示最佳成绩记录""" # 创建新窗口 - 修改为540x700 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 # 检查重置按钮 (350,585) 到 (430,615) elif 350 <= x <= 430 and 585 <= y <= 615: reset_confirm_active = True # 激活重置确认对话框 # 检查记录按钮 (440,585) to (520,615) elif 440 <= x <= 520 and 585 <= y <= 615: show_records() # 检查提示按钮 (350,625) to (430,650) elif 350 <= x <= 430 and 625 <= y <= 650: show_hint() # 检查帮助按钮 (440,625) to (520,650) 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 # 删除/清除:空格键、DEL、Backspace 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"]: # 只在值非0时更新 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 # 重置游戏 (R键) elif event.key == pygame.K_r: reset_confirm_active = True # 激活重置确认对话框 # 提示 (T键) elif event.key == pygame.K_t: show_hint() # 帮助 (H键) elif event.key == pygame.K_h: show_help_screen() # 取消重置确认 (ESC键) 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"]): # try: # draw_message("恭喜!你赢了!", COLORS['green']) # except: # draw_message("You Win! Congratulations!", COLORS['green']) 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): """检查在位置(i,j)放置number是否合法""" # 检查行 if number in matrix[i]: return False # 检查列 for row in matrix: if row[j] == number: return False # 检查3x3宫格 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 # 无解 return matrix # 全部填满 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]

Read more

ubuntu-24.04安装配置rime(中州韵)输入法

ubuntu-24.04安装配置rime(中州韵)输入法

由于搜狗输入法版本较老,在ubuntu-24.04上总是有些毛病,遂尝试使用社区较好的rime输入法。 笔者前前后后也是踩了不少坑,故做一次记录,也希望对各位有用。 有关rime的一些基础知识 安装前先介绍一下rime的相关知识,这样对于后续的操作就比较易于理解,遇到问题也容易解决。 详细参见rime中州韵小狼毫 保姆级安装配置教程 100种增强功能_小狼毫输入法100+增强功能-ZEEKLOG博客 1、rime与小狼毫、中州韵、鼠须管有什么关系? rime全平台通用,不过在不同平台有着不同的名字;windows叫小狼毫,linux叫中州韵,mac上叫鼠须管。 2、rime和東風破有什么关系? 東風破是中州韻輸入引擎的配置管理工具,更易于管理rime的第三方包(比如笔者使用的雾凇)。東風破安装可选,但是强烈推荐。 3、rime自定义配置好用吗? 包好用的;只要搞明白.custom.yaml以及部署流程(并不难,在配置部分会讲)。 注意:针对windows、macos、linux(ibus、fcitx)有不同的配置文件,不同平台请勿错用。 输入法平台主配置文

By Ne0inhk
【图文】Codex接入Kimi K2/GLM-4.6 环境配置指南 (Windows/macOS/Ubuntu)

【图文】Codex接入Kimi K2/GLM-4.6 环境配置指南 (Windows/macOS/Ubuntu)

Codex接入Kimi K2/GLM-4.6 环境配置指南 (Windows/macOS/Ubuntu) 前言 紧跟DeepSeek的步伐,智谱也在节前发布了GLM-4.6,并称它是智谱"最强的代码Coding模型(较GLM-4.5提升27%)" * 代码能力对齐Claude Sonnet 4,部分榜单对齐Claude Sonnet 4.5。 * 上下文长度增加到200K。 * 推理能力提升,增加图像识别与搜索能力。 * token消耗较GLM-4.5节省30%以上。 GLM Coding Plan订阅自动升级至GLM-4.6(包含已订阅的用户): * 支持Claude Code、Roo Code、Kilo Code、Cline等10+主流编程工具。 * 套餐命名对齐Claude,用量为Claude x3,费用为Claude x1/7,

By Ne0inhk
Flutter 三方库 vy_string_utils 的鸿蒙化适配指南 - 实现高效的字符串模式校检、支持富文本清洗与多维度命名规范转换

Flutter 三方库 vy_string_utils 的鸿蒙化适配指南 - 实现高效的字符串模式校检、支持富文本清洗与多维度命名规范转换

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 vy_string_utils 的鸿蒙化适配指南 - 实现高效的字符串模式校检、支持富文本清洗与多维度命名规范转换 前言 在进行 Flutter for OpenHarmony 开发时,字符串处理几乎无处不在。从校验用户输入的手机号,到将后台返回的 snake_case 字段转化为鸿蒙 UI 需要的文本格式,这类基础工作如果通过硬编码实现,会产生大量的冗余逻辑。vy_string_utils 是一款轻量级却功能强悍的字符串工具包。它通过一系列精心设计的扩展方法,让鸿蒙开发者能以极简的语法管理所有文本流。本文将带大家领略这款“字符串手术刀”的威力。 一、原理解析 / 概念介绍 1.1 基础原理 vy_string_utils 基于 Dart

By Ne0inhk
Flutter 三方库 flutter_test_config 的鸿蒙化适配指南 - 实现具备全局上下文配置与测试桩自动化注入的质量管理中心、支持端侧测试资源预加载与环境归一化实战

Flutter 三方库 flutter_test_config 的鸿蒙化适配指南 - 实现具备全局上下文配置与测试桩自动化注入的质量管理中心、支持端侧测试资源预加载与环境归一化实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 flutter_test_config 的鸿蒙化适配指南 - 实现具备全局上下文配置与测试桩自动化注入的质量管理中心、支持端侧测试资源预加载与环境归一化实战 前言 在进行 Flutter for OpenHarmony 的大规模质量建设时,我们经常需要为整个项目的测试用例配置统一的参数。例如:为所有 UI 测试注入统一的字体包、配置模拟的鸿蒙屏幕尺寸,或者在每个测试开始前重置分布式数据库状态。flutter_test_config 是 Flutter 官方提供的一种特殊的配置机制,用于在测试执行前注入全局逻辑。本文将探讨如何在鸿蒙端构建极致、专业的全局测试治理中心。 一、原直观解析 / 概念介绍 1.1 基础原理 该库通过在 test 目录下搜索名为 flutter_test_config.dart 的特殊入口文件,

By Ne0inhk