20232414 实验四《Python程序设计》实验报告
20232414 2024-2025-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 2324
姓名: 高泽瑞
学号:20232414
实验教师:王志强
实验日期:2025年5月14日
必修/选修: 公选课
1.实验内容
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
本次实验选择编写小游戏方向,用python程序实现简易的扫雷游戏。
2.实验过程
(1)需要实现的内容
- 第一次点击绝对安全
- 支持旗帜和问号两种标记
- 自动展开空白区域
- 实时计时
- 不同颜色的数字提示
- 游戏结束提示
(2)初始化
- 设置游戏参数
SCREEN_WIDTH = 600 # 游戏窗口宽度
SCREEN_HEIGHT = 700 # 游戏窗口高度
GRID_SIZE = 30 # 每个格子的像素大小
GRID_WIDTH = 16 # 横向格子数量
GRID_HEIGHT = 16 # 纵向格子数量
MINES_COUNT = 40 # 地雷总数
MARGIN_TOP = 100 # 游戏区域顶部边距
- 初始化Pygame
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
- 定义颜色和字体
//颜色定义
BACKGROUND = (220, 220, 220) # 背景色
GRID_LINES = (180, 180, 180) # 网格线颜色
UNREVEALED = (200, 200, 200) # 未翻开格子颜色
REVEALED = (230, 230, 230) # 已翻开格子颜色
MINECOLOR = (255, 0, 0) # 地雷颜色
TEXT_COLOR = (0, 0, 0) # 文本颜色
FLAG_COLOR = (255, 0, 0) # 旗帜标记颜色
QUESTION_COLOR = (0, 0, 255) # 问号标记颜色
//数字颜色 (1-8)
NUM_COLORS = [
(0, 0, 255), # 1 - 蓝色
(0, 128, 0), # 2 - 绿色
(255, 0, 0), # 3 - 红色
(0, 0, 128), # 4 - 深蓝
(128, 0, 0), # 5 - 深红
(0, 128, 128), # 6 - 青色
(0, 0, 0), # 7 - 黑色
(128, 128, 128) # 8 - 灰色
]
//字体设置
font = pygame.font.SysFont('Microsoft YaHei UI', 22) # 主字体
small_font = pygame.font.SysFont('Microsoft YaHei UI', 18) # 小字体
title_font = pygame.font.SysFont('Microsoft YaHei UI', 36) # 标题字体
(3)创建游戏主类 Minesweeper
- 游戏状态初始化
def reset_game(self):
# 游戏板:0表示安全,-1表示地雷,正数表示周围地雷数
self.board = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
# 格子状态: hidden(未翻开), revealed(已翻开), flag(旗帜), question(问号)
self.state = [['hidden' for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
self.mines = set() # 地雷位置集合
self.game_over = False # 游戏结束标志
self.game_won = False # 游戏胜利标志
self.first_click = True # 首次点击标志
self.mines_count = MINES_COUNT # 地雷总数
self.flags = set() # 旗帜标记位置
self.questions = set() # 问号标记位置
self.start_time = pygame.time.get_ticks() # 游戏开始时间
self.elapsed_time = 0 # 已用时间
- 地雷放置
def place_mines(self, first_x, first_y):
# 创建安全区域(第一次点击及其周围8格)
safe_area = set()
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
nx, ny = first_x + dx, first_y + dy
if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT:
safe_area.add((nx, ny))
# 随机放置地雷(避开安全区域)
self.mines = set()
while len(self.mines) < self.mines_count:
x = random.randint(0, GRID_WIDTH - 1)
y = random.randint(0, GRID_HEIGHT - 1)
if (x, y) not in safe_area and (x, y) not in self.mines:
self.mines.add((x, y))
self.board[y][x] = -1 # 标记为地雷
# 计算每个非地雷格子周围的地雷数量
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
if self.board[y][x] != -1:
count = 0
# 检查周围的8个方向
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 < GRID_WIDTH and 0 <= ny < GRID_HEIGHT:
if self.board[ny][nx] == -1:
count += 1
self.board[y][x] = count # 设置周围地雷数
- 递归翻开空白格子
def reveal(self, x, y):
# 检查坐标是否有效
if not (0 <= x < GRID_WIDTH and 0 <= y < GRID_HEIGHT):
return
# 只能翻开隐藏或问号标记的格子
if self.state[y][x] != 'hidden' and self.state[y][x] != 'question':
return
# 如果是第一次点击,放置地雷并开始计时
if self.first_click:
self.first_click = False
self.place_mines(x, y)
self.start_time = pygame.time.get_ticks()
# 如果踩到地雷,游戏结束
if (x, y) in self.mines:
self.state[y][x] = 'revealed'
self.game_over = True
return
# 递归翻开空白区域的辅助函数
def reveal_recursive(x, y):
# 边界检查
if not (0 <= x < GRID_WIDTH and 0 <= y < GRID_HEIGHT):
return
# 已翻开或标记为旗帜的格子不再处理
if self.state[y][x] == 'revealed' or self.state[y][x] == 'flag':
return
# 翻开当前格子
self.state[y][x] = 'revealed'
# 移除可能的标记
if (x, y) in self.flags:
self.flags.remove((x, y))
if (x, y) in self.questions:
self.questions.remove((x, y))
# 如果当前格子是空白(0),递归翻开周围格子
if self.board[y][x] == 0:
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0: # 跳过自身
continue
reveal_recursive(x + dx, y + dy)
# 开始递归翻开过程
reveal_recursive(x, y)
# 检查是否获胜
self.check_win()
- 标记切换
def toggle_mark(self, x, y):
# 已翻开的格子不能标记
if self.state[y][x] == 'revealed':
return
# 标记状态循环: hidden -> flag -> question -> hidden
if self.state[y][x] == 'hidden':
self.state[y][x] = 'flag'
self.flags.add((x, y))
elif self.state[y][x] == 'flag':
self.state[y][x] = 'question'
self.flags.remove((x, y))
self.questions.add((x, y))
elif self.state[y][x] == 'question':
self.state[y][x] = 'hidden'
self.questions.remove((x, y))
# 检查是否获胜
self.check_win()
- 是否胜利检测
def check_win(self):
# 检查所有非地雷格子是否都被翻开
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
if (x, y) not in self.mines and self.state[y][x] != 'revealed':
return # 还有未翻开的非雷格子
# 检查所有地雷是否都被正确标记
if self.mines == self.flags:
self.game_won = True
self.game_over = True
# 记录游戏结束时间
self.elapsed_time = pygame.time.get_ticks() - self.start_time
(4)图形界面
- 游戏标题和状态
# 绘制标题
title = title_font.render("扫雷游戏", True, (0, 0, 128))
screen.blit(title, (SCREEN_WIDTH // 2 - title.get_width() // 2, 20))
# 绘制剩余地雷数
mines_text = font.render(f"剩余地雷: {self.mines_count - len(self.flags)}", True, (0, 0, 0))
screen.blit(mines_text, (50, 60))
# 绘制游戏时间
if not self.game_over and not self.first_click:
self.elapsed_time = pygame.time.get_ticks() - self.start_time
seconds = self.elapsed_time // 1000
time_text = font.render(f"时间: {seconds}秒", True, (0, 0, 0))
screen.blit(time_text, (SCREEN_WIDTH - 150, 60))
- 绘制格子&内容
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
rect = pygame.Rect(x * GRID_SIZE, y * GRID_SIZE + MARGIN_TOP, GRID_SIZE, GRID_SIZE)
# 绘制格子背景
if self.state[y][x] == 'revealed':
pygame.draw.rect(screen, REVEALED, rect)
else:
pygame.draw.rect(screen, UNREVEALED, rect)
pygame.draw.rect(screen, GRID_LINES, rect, 1)
# 绘制格子内容
if self.state[y][x] == 'revealed':
if (x, y) in self.mines: # 地雷
pygame.draw.circle(screen, MINECOLOR,
(x * GRID_SIZE + GRID_SIZE // 2,
y * GRID_SIZE + GRID_SIZE // 2 + MARGIN_TOP),
GRID_SIZE // 3)
elif self.board[y][x] > 0: # 数字
num_text = font.render(str(self.board[y][x]), True, NUM_COLORS[self.board[y][x] - 1])
screen.blit(num_text, (x * GRID_SIZE + GRID_SIZE // 2 - num_text.get_width() // 2,
y * GRID_SIZE + MARGIN_TOP + GRID_SIZE // 2 - num_text.get_height() // 2))
elif self.state[y][x] == 'flag': # 旗帜标记
# 绘制旗帜三角形和旗杆
pygame.draw.polygon(screen, FLAG_COLOR, [...])
pygame.draw.rect(screen, (100, 100, 100), [...])
elif self.state[y][x] == 'question': # 问号标记
q_text = font.render("?", True, QUESTION_COLOR)
screen.blit(q_text, [...])
# 绘制网格线
for x in range(GRID_WIDTH + 1):
pygame.draw.line(screen, GRID_LINES, [...])
for y in range(GRID_HEIGHT + 1):
pygame.draw.line(screen, GRID_LINES, [...])
- 游戏结束画面
if self.game_over:
# 半透明覆盖层
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
overlay.set_alpha(180)
overlay.fill((0, 0, 0))
screen.blit(overlay, (0, 0))
# 根据输赢显示不同文本
if self.game_won:
result_text = font.render("恭喜你赢了!", True, (0, 255, 0))
else:
result_text = font.render("游戏结束!", True, (255, 0, 0))
restart_text = font.render("按R键重新开始", True, (255, 255, 255))
# 居中显示结果文本
screen.blit(result_text, (SCREEN_WIDTH // 2 - result_text.get_width() // 2, SCREEN_HEIGHT // 2 - 50))
screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, SCREEN_HEIGHT // 2 + 10))
(5)事件处理和游戏循环
# 创建游戏实例
game = Minesweeper()
# 设置游戏时钟
clock = pygame.time.Clock()
running = True
# 主游戏循环
while running:
# 处理事件
for event in pygame.event.get():
if event.type == QUIT: # 退出事件
running = False
if event.type == KEYDOWN: # 按键事件
if event.key == K_r: # R键重置游戏
game.reset_game()
if event.type == MOUSEBUTTONDOWN and not game.game_over: # 鼠标点击事件
x, y = event.pos # 获取鼠标位置
# 转换为格子坐标
grid_x = x // GRID_SIZE
grid_y = (y - MARGIN_TOP) // GRID_SIZE
# 检查坐标是否在有效范围内
if 0 <= grid_x < GRID_WIDTH and 0 <= grid_y < GRID_HEIGHT:
if event.button == 1: # 左键 - 翻开格子
game.reveal(grid_x, grid_y)
elif event.button == 3: # 右键 - 切换标记
game.toggle_mark(grid_x, grid_y)
# 绘制背景
screen.fill(BACKGROUND)
# 绘制游戏
game.draw(screen)
# 更新屏幕
pygame.display.flip()
# 控制帧率
clock.tick(30)
3.实验结果
完整代码:
演示:
4.参考文章
5.心得体会
从输出一句简单的“Hwllo World1”到实现一个有趣的小游戏,学习python的确带给了我很多的成就感。
好有趣啊扫雷哈哈哈ጿ ኈ ቼ ዽ ጿ ኈቼ ዽ
感谢王志强老师的辛苦付出,您辛苦啦[Rose]