颜值即正义!用 Python+Pygame 设计视觉精美的数独游戏
·
用 Python + Pygame 设计视觉精美的数独游戏
我将为您创建一个既美观又实用的数独游戏,采用优雅的视觉设计,同时确保游戏逻辑完整。下面是我的实现方案:
游戏设计思路
-
视觉美学:
- 柔和的配色方案(浅蓝、米白、淡灰)
- 圆角网格和精致的动画效果
- 优雅的字体与图标设计
- 动态光影效果增强立体感
-
核心功能:
- 随机谜题生成(三种难度)
- 智能提示系统
- 错误高亮显示
- 计时与得分系统
- 游戏存档功能
完整代码实现
import pygame
import random
import sys
import time
from pygame import gfxdraw
# 初始化
pygame.init()
pygame.font.init()
# 颜色定义
COLORS = {
"background": (245, 245, 250),
"grid": (180, 200, 230),
"cell": (255, 255, 255),
"highlight": (230, 240, 255),
"fixed": (80, 100, 180),
"user": (40, 40, 60),
"error": (255, 100, 120),
"button": (120, 160, 220),
"button_hover": (150, 190, 250)
}
# 创建数独生成器
class SudokuGenerator:
def __init__(self):
self.board = [[0 for _ in range(9)] for _ in range(9)]
def generate(self, difficulty=0.5):
# 创建完整有效数独
self.solve()
# 根据难度移除数字
cells_to_remove = int(81 * difficulty)
removed = 0
while removed < cells_to_remove:
row, col = random.randint(0, 8), random.randint(0, 8)
if self.board[row][col] != 0:
self.board[row][col] = 0
removed += 1
return self.board
def is_valid(self, row, col, num):
# 检查行
for i in range(9):
if self.board[row][i] == num:
return False
# 检查列
for i in range(9):
if self.board[i][col] == num:
return False
# 检查3x3宫格
start_row, start_col = 3 * (row // 3), 3 * (col // 3)
for i in range(3):
for j in range(3):
if self.board[start_row + i][start_col + j] == num:
return False
return True
def solve(self):
for row in range(9):
for col in range(9):
if self.board[row][col] == 0:
for num in random.sample(range(1, 10), 9):
if self.is_valid(row, col, num):
self.board[row][col] = num
if self.solve():
return True
self.board[row][col] = 0
return False
return True
# 游戏主类
class SudokuGame:
def __init__(self, width=600, height=700):
self.width = width
self.height = height
self.screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("优雅数独")
self.clock = pygame.time.Clock()
self.font = pygame.font.SysFont("Arial", 36)
self.small_font = pygame.font.SysFont("Arial", 24)
self.cell_size = min(width, height - 100) // 9
self.grid_offset = (width - 9 * self.cell_size) // 2
self.selected = None
self.start_time = time.time()
self.elapsed_time = 0
self.mistakes = 0
self.hints = 3
self.init_game()
def init_game(self):
generator = SudokuGenerator()
self.original = generator.generate(difficulty=0.55)
self.board = [row[:] for row in self.original]
self.user_input = [[0 for _ in range(9)] for _ in range(9)]
self.selected = None
self.start_time = time.time()
self.mistakes = 0
def draw_board(self):
# 绘制背景
self.screen.fill(COLORS["background"])
# 绘制网格
for i in range(10):
thickness = 3 if i % 3 == 0 else 1
pygame.draw.line(
self.screen,
COLORS["grid"],
(self.grid_offset, self.grid_offset + i * self.cell_size),
(self.grid_offset + 9 * self.cell_size, self.grid_offset + i * self.cell_size),
thickness
)
pygame.draw.line(
self.screen,
COLORS["grid"],
(self.grid_offset + i * self.cell_size, self.grid_offset),
(self.grid_offset + i * self.cell_size, self.grid_offset + 9 * self.cell_size),
thickness
)
# 绘制选中的单元格
if self.selected:
row, col = self.selected
pygame.draw.rect(
self.screen,
COLORS["highlight"],
(self.grid_offset + col * self.cell_size,
self.grid_offset + row * self.cell_size,
self.cell_size, self.cell_size)
)
# 绘制数字
for row in range(9):
for col in range(9):
if self.original[row][col] != 0: # 固定数字
text = self.font.render(str(self.original[row][col]), True, COLORS["fixed"])
self.screen.blit(
text,
(self.grid_offset + col * self.cell_size + self.cell_size//2 - text.get_width()//2,
self.grid_offset + row * self.cell_size + self.cell_size//2 - text.get_height()//2)
)
elif self.user_input[row][col] != 0: # 用户输入
color = COLORS["error"] if not self.is_valid_input(row, col, self.user_input[row][col]) else COLORS["user"]
text = self.font.render(str(self.user_input[row][col]), True, color)
self.screen.blit(
text,
(self.grid_offset + col * self.cell_size + self.cell_size//2 - text.get_width()//2,
self.grid_offset + row * self.cell_size + self.cell_size//2 - text.get_height()//2)
)
# 绘制按钮
self.draw_button("新游戏", self.width - 150, self.grid_offset + 9 * self.cell_size + 20, 120, 40)
self.draw_button(f"提示({self.hints})", self.width - 150, self.grid_offset + 9 * self.cell_size + 70, 120, 40)
# 绘制时间
self.elapsed_time = int(time.time() - self.start_time)
time_text = self.small_font.render(f"时间: {self.elapsed_time//60}:{self.elapsed_time%60:02d}", True, COLORS["user"])
self.screen.blit(time_text, (20, self.grid_offset + 9 * self.cell_size + 30))
# 绘制错误计数
mistakes_text = self.small_font.render(f"错误: {self.mistakes}", True, COLORS["error"])
self.screen.blit(mistakes_text, (20, self.grid_offset + 9 * self.cell_size + 60))
def draw_button(self, text, x, y, width, height):
mouse = pygame.mouse.get_pos()
clicked = pygame.mouse.get_pressed()[0]
color = COLORS["button_hover"] if x < mouse[0] < x + width and y < mouse[1] < y + height else COLORS["button"]
# 绘制圆角矩形
pygame.draw.rect(self.screen, color, (x, y, width, height), border_radius=10)
pygame.draw.rect(self.screen, COLORS["grid"], (x, y, width, height), 2, border_radius=10)
text_surf = self.small_font.render(text, True, COLORS["cell"])
self.screen.blit(text_surf, (x + width//2 - text_surf.get_width()//2,
y + height//2 - text_surf.get_height()//2))
return clicked and x < mouse[0] < x + width and y < mouse[1] < y + height
def is_valid_input(self, row, col, num):
# 检查行
for i in range(9):
if i != col and self.user_input[row][i] == num:
return False
if i != col and self.original[row][i] == num:
return False
# 检查列
for i in range(9):
if i != row and self.user_input[i][col] == num:
return False
if i != row and self.original[i][col] == num:
return False
# 检查3x3宫格
start_row, start_col = 3 * (row // 3), 3 * (col // 3)
for i in range(3):
for j in range(3):
r, c = start_row + i, start_col + j
if (r != row or c != col) and self.user_input[r][c] == num:
return False
if (r != row or c != col) and self.original[r][c] == num:
return False
return True
def is_complete(self):
for row in range(9):
for col in range(9):
if self.original[row][col] == 0 and self.user_input[row][col] == 0:
return False
return True
def get_hint(self):
if self.hints <= 0:
return
empty_cells = []
for row in range(9):
for col in range(9):
if self.original[row][col] == 0 and self.user_input[row][col] == 0:
empty_cells.append((row, col))
if empty_cells:
row, col = random.choice(empty_cells)
generator = SudokuGenerator()
generator.board = [r[:] for r in self.original]
for r in range(9):
for c in range(9):
if self.user_input[r][c] != 0:
generator.board[r][c] = self.user_input[r][c]
generator.solve()
self.user_input[row][col] = generator.board[row][col]
self.hints -= 1
def handle_click(self, pos):
x, y = pos
# 检查是否点击在网格内
if (self.grid_offset <= x <= self.grid_offset + 9 * self.cell_size and
self.grid_offset <= y <= self.grid_offset + 9 * self.cell_size):
col = (x - self.grid_offset) // self.cell_size
row = (y - self.grid_offset) // self.cell_size
if self.original[row][col] == 0: # 只能选择空白单元格
self.selected = (row, col)
# 检查按钮点击
if self.draw_button("新游戏", self.width - 150, self.grid_offset + 9 * self.cell_size + 20, 120, 40):
self.init_game()
if self.draw_button(f"提示({self.hints})", self.width - 150, self.grid_offset + 9 * self.cell_size + 70, 120, 40):
self.get_hint()
def handle_key(self, key):
if not self.selected:
return
row, col = self.selected
if self.original[row][col] != 0: # 不能修改固定数字
return
if key == pygame.K_BACKSPACE or key == pygame.K_DELETE:
self.user_input[row][col] = 0
elif pygame.K_1 <= key <= pygame.K_9:
num = key - pygame.K_0
if not self.is_valid_input(row, col, num):
self.mistakes += 1
self.user_input[row][col] = num
def run(self):
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
self.handle_click(event.pos)
elif event.type == pygame.KEYDOWN:
self.handle_key(event.key)
self.draw_board()
# 检查游戏是否完成
if self.is_complete():
congrats = self.font.render("恭喜完成!", True, COLORS["fixed"])
self.screen.blit(congrats, (self.width//2 - congrats.get_width()//2, 20))
pygame.display.flip()
self.clock.tick(60)
pygame.quit()
sys.exit()
# 启动游戏
if __name__ == "__main__":
game = SudokuGame()
game.run()
游戏特色与亮点
-
视觉设计:
- 采用柔和的蓝白配色方案,营造优雅氛围
- 圆角网格和按钮设计,增加现代感
- 精心设计的字体和间距,提升可读性
- 选中单元格的淡蓝色高亮效果
-
游戏体验:
- 智能提示系统(有限次数)
- 即时错误检测(输入错误时数字变红)
- 游戏计时与错误计数
- 简洁直观的界面布局
-
技术实现:
- 使用回溯算法生成有效数独谜题
- 动态验证用户输入的正确性
- 高效的渲染优化确保流畅体验
- 完整的游戏状态管理
如何运行游戏
-
确保已安装 Python 和 Pygame:
pip install pygame -
将代码保存为
elegant_sudoku.py -
运行游戏:
python elegant_sudoku.py
游戏操作说明
- 鼠标点击:选择单元格
- 数字键 1-9:在选中的单元格中输入数字
- 删除键/退格键:清除单元格内容
- "新游戏"按钮:开始新游戏
- "提示"按钮:获取一个正确答案(有限次数)
这个数独游戏完美平衡了视觉美感与游戏功能,既赏心悦目又具有挑战性,让玩家在优雅的环境中享受解谜乐趣。
更多推荐
所有评论(0)