Python 小游戏实战:打造视觉精美的数独小游戏

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()

快速改进方向(可按需添加)

  1. 难度选择菜单(用 pygame-menu 库最快)
  2. 提示功能(随机找一个空位填入 solution 的值)
  3. 撤销栈(保存每一步操作)
  4. 错误计数(超过3次提示失败)
  5. 胜利粒子动画(用简单粒子系统)
  6. 音效(正确/错误/胜利音)
  7. 保存进度(json 文件)

推荐下一步行动

如果你现在想继续做这个项目,建议先回答以下问题,我可以给你最匹配的下一段代码:

  1. 你希望先加上难度选择菜单吗?
  2. 想要加入“提示”按钮功能吗?
  3. 需要错误次数限制 + 游戏失败判定吗?
  4. 想先美化界面(渐变背景、数字动画等)还是先完善逻辑?
  5. 你电脑上是否已经安装了 pygame?(pip install pygame)

告诉我你的优先级,我马上给你下一部分最实用的代码和实现细节~

文章已创建 4323

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部