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.实验结果

完整代码:

MIneSweeper-gitee仓库

演示:



4.参考文章

5.心得体会

从输出一句简单的“Hwllo World1”到实现一个有趣的小游戏,学习python的确带给了我很多的成就感。
好有趣啊扫雷哈哈哈ጿ ኈ ቼ ዽ ጿ ኈቼ ዽ
感谢王志强老师的辛苦付出,您辛苦啦[Rose]

posted @ 2025-06-10 22:55  ;P  阅读(23)  评论(0)    收藏  举报