扫雷
`import pygame
import random
import sys
from typing import List, Tuple, Optional
初始化pygame
pygame.init()
确保中文显示正常
pygame.font.init()
font_path = pygame.font.match_font('simsun') or pygame.font.match_font('microsoftyahei')
if not font_path:
# 如果找不到中文字体,使用默认字体
font_path = pygame.font.get_default_font()
class Cell:
"""扫雷游戏中的单个格子"""
def init(self, x: int, y: int):
self.x = x
self.y = y
self.is_mine = False
self.is_revealed = False
self.is_flagged = False
self.adjacent_mines = 0
def toggle_flag(self) -> None:
"""切换旗帜状态"""
if not self.is_revealed:
self.is_flagged = not self.is_flagged
def reveal(self) -> bool:
"""翻开格子,返回是否是地雷"""
if self.is_revealed or self.is_flagged:
return False
self.is_revealed = True
return self.is_mine
class Minesweeper:
"""扫雷游戏主类"""
def init(self, width: int = 10, height: int = 10, mines: int = 10):
self.width = width
self.height = height
self.mines = mines
self.board = [[Cell(x, y) for y in range(height)] for x in range(width)]
self.game_over = False
self.victory = False
self.first_click = True
self.revealed_cells = 0
self.flags_placed = 0
self.start_time = 0
self.elapsed_time = 0
self.clock = pygame.time.Clock()
# 设置窗口
self.cell_size = 30
self.header_height = 100
self.screen_width = width * self.cell_size
self.screen_height = height * self.cell_size + self.header_height
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height))
pygame.display.set_caption("扫雷游戏")
# 加载字体
self.font_large = pygame.font.Font(font_path, 36)
self.font_small = pygame.font.Font(font_path, 24)
# 颜色定义
self.COLORS = {
'BACKGROUND': (200, 200, 200),
'GRID': (100, 100, 100),
'CELL_HIDDEN': (180, 180, 180),
'CELL_REVEALED': (230, 230, 230),
'MINE': (255, 0, 0),
'FLAG': (255, 0, 0),
'TEXT': (0, 0, 0),
'HEADER': (150, 150, 150),
'NUMBER_1': (0, 0, 255),
'NUMBER_2': (0, 128, 0),
'NUMBER_3': (255, 0, 0),
'NUMBER_4': (0, 0, 128),
'NUMBER_5': (128, 0, 0),
'NUMBER_6': (0, 128, 128),
'NUMBER_7': (0, 0, 0),
'NUMBER_8': (128, 128, 128),
}
# 按钮设置
self.reset_button = pygame.Rect(
self.screen_width // 2 - 30,
self.header_height // 2 - 15,
60,
30
)
def initialize_board(self, first_x: int, first_y: int) -> None:
"""初始化游戏棋盘,确保第一次点击不是地雷"""
# 放置地雷
mines_placed = 0
while mines_placed < self.mines:
x = random.randint(0, self.width - 1)
y = random.randint(0, self.height - 1)
# 确保不在第一次点击的位置及其相邻位置放置地雷
if (x == first_x and y == first_y) or \
(abs(x - first_x) <= 1 and abs(y - first_y) <= 1):
continue
if not self.board[x][y].is_mine:
self.board[x][y].is_mine = True
mines_placed += 1
# 计算每个格子周围的地雷数
for x in range(self.width):
for y in range(self.height):
if not self.board[x][y].is_mine:
self.board[x][y].adjacent_mines = self.count_adjacent_mines(x, y)
def count_adjacent_mines(self, x: int, y: int) -> int:
"""计算指定格子周围的地雷数量"""
count = 0
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue
nx, ny = x + dx, y + dy
if 0 <= nx < self.width and 0 <= ny < self.height:
if self.board[nx][ny].is_mine:
count += 1
return count
def reveal_empty_cells(self, x: int, y: int) -> None:
"""递归翻开所有相邻的空格子"""
if self.board[x][y].adjacent_mines != 0:
return
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue
nx, ny = x + dx, y + dy
if 0 <= nx < self.width and 0 <= ny < self.height:
cell = self.board[nx][ny]
if not cell.is_revealed and not cell.is_flagged:
cell.reveal()
self.revealed_cells += 1
if cell.adjacent_mines == 0:
self.reveal_empty_cells(nx, ny)
def check_victory(self) -> bool:
"""检查游戏是否胜利"""
# 胜利条件:所有非地雷格子都被翻开
return self.revealed_cells == self.width * self.height - self.mines
def handle_click(self, x: int, y: int, button: int) -> None:
"""处理鼠标点击事件"""
if self.game_over or self.victory:
return
# 转换为格子坐标
grid_x = x // self.cell_size
grid_y = (y - self.header_height) // self.cell_size
if grid_y < 0 or grid_x >= self.width or grid_y >= self.height:
return
cell = self.board[grid_x][grid_y]
if button == 1: # 左键点击
if self.first_click:
self.first_click = False
self.initialize_board(grid_x, grid_y)
self.start_time = pygame.time.get_ticks()
if cell.is_flagged:
return
if cell.reveal():
self.game_over = True
return
self.revealed_cells += 1
if cell.adjacent_mines == 0:
self.reveal_empty_cells(grid_x, grid_y)
self.victory = self.check_victory()
elif button == 3: # 右键点击
if self.first_click:
return
if not cell.is_revealed:
cell.toggle_flag()
if cell.is_flagged:
self.flags_placed += 1
else:
self.flags_placed -= 1
self.victory = self.check_victory()
def draw_cell(self, cell: Cell) -> None:
"""绘制单个格子"""
rect = pygame.Rect(
cell.x * self.cell_size,
self.header_height + cell.y * self.cell_size,
self.cell_size,
self.cell_size
)
# 绘制格子背景
if cell.is_revealed:
pygame.draw.rect(self.screen, self.COLORS['CELL_REVEALED'], rect)
pygame.draw.rect(self.screen, self.COLORS['GRID'], rect, 1)
# 如果是地雷
if cell.is_mine:
pygame.draw.circle(
self.screen,
self.COLORS['MINE'],
(rect.centerx, rect.centery),
self.cell_size // 3
)
# 如果周围有地雷,显示数字
elif cell.adjacent_mines > 0:
number_color = self.COLORS.get(
f'NUMBER_{cell.adjacent_mines}',
self.COLORS['TEXT']
)
text = self.font_small.render(
str(cell.adjacent_mines),
True,
number_color
)
text_rect = text.get_rect(center=rect.center)
self.screen.blit(text, text_rect)
else:
pygame.draw.rect(self.screen, self.COLORS['CELL_HIDDEN'], rect)
pygame.draw.rect(self.screen, self.COLORS['GRID'], rect, 1)
# 如果有旗帜
if cell.is_flagged:
pygame.draw.line(
self.screen,
self.COLORS['FLAG'],
(rect.centerx, rect.top + 5),
(rect.centerx, rect.bottom - 5),
2
)
pygame.draw.polygon(
self.screen,
self.COLORS['FLAG'],
[
(rect.centerx, rect.top + 5),
(rect.right - 5, rect.top + 15),
(rect.centerx, rect.top + 25)
]
)
def draw_header(self) -> None:
"""绘制游戏头部"""
header_rect = pygame.Rect(0, 0, self.screen_width, self.header_height)
pygame.draw.rect(self.screen, self.COLORS['HEADER'], header_rect)
# 绘制剩余地雷数
mines_left = max(0, self.mines - self.flags_placed)
mines_text = self.font_large.render(f"地雷: {mines_left}", True, self.COLORS['TEXT'])
self.screen.blit(mines_text, (20, 30))
# 绘制计时器
if not self.first_click and not self.game_over and not self.victory:
self.elapsed_time = (pygame.time.get_ticks() - self.start_time) // 1000
time_text = self.font_large.render(f"时间: {self.elapsed_time}s", True, self.COLORS['TEXT'])
self.screen.blit(time_text, (self.screen_width - 20 - time_text.get_width(), 30))
# 绘制重置按钮
pygame.draw.rect(self.screen, self.COLORS['CELL_HIDDEN'], self.reset_button)
pygame.draw.rect(self.screen, self.COLORS['GRID'], self.reset_button, 2)
if self.victory:
reset_text = self.font_small.render("胜利!", True, self.COLORS['TEXT'])
elif self.game_over:
reset_text = self.font_small.render("失败!", True, self.COLORS['TEXT'])
else:
reset_text = self.font_small.render("重置", True, self.COLORS['TEXT'])
reset_text_rect = reset_text.get_rect(center=self.reset_button.center)
self.screen.blit(reset_text, reset_text_rect)
def reset_game(self) -> None:
"""重置游戏"""
self.board = [[Cell(x, y) for y in range(self.height)] for x in range(self.width)]
self.game_over = False
self.victory = False
self.first_click = True
self.revealed_cells = 0
self.flags_placed = 0
self.elapsed_time = 0
def run(self) -> None:
"""运行游戏主循环"""
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
if self.reset_button.collidepoint(x, y):
self.reset_game()
else:
self.handle_click(x, y, event.button)
# 绘制游戏界面
self.screen.fill(self.COLORS['BACKGROUND'])
self.draw_header()
for x in range(self.width):
for y in range(self.height):
self.draw_cell(self.board[x][y])
# 如果游戏结束,显示所有地雷
if self.game_over:
for x in range(self.width):
for y in range(self.height):
cell = self.board[x][y]
if cell.is_mine and not cell.is_flagged:
cell.is_revealed = True
elif cell.is_flagged and not cell.is_mine:
cell.is_revealed = True
pygame.display.flip()
self.clock.tick(60)
pygame.quit()
sys.exit()
if name == "main":
# 创建并运行游戏
game = Minesweeper(width=10, height=10, mines=10)
game.run() `

浙公网安备 33010602011771号