本专栏的第九篇文章介绍了手把手实现经典的贪吃蛇小游戏,具体内容可以参考以下帖子:

CS课程项目设计9:手把手实现经典的贪吃蛇小游戏-CSDN博客https://blog.csdn.net/weixin_36431280/article/details/149707701?spm=1001.2014.3001.5501

随着人工智能的火热,AI人机对战的引入显得很有必要,本质上就是引入强化学习的概念了,这也是现在大模型领域比较关心的方向了。

因此,和本专栏第六个和第八个CS课程项目一样,我们继续在现有贪吃蛇小游戏基础上,增加AI辅助移动功能:

CS课程项目设计6:基于Canvas支持人机对战的井字棋游戏-CSDN博客https://blog.csdn.net/weixin_36431280/article/details/149668934?spm=1001.2014.3001.5501CS课程项目设计8:基于Canvas支持AI人机对战的五子棋游戏-CSDN博客https://blog.csdn.net/weixin_36431280/article/details/149686915?spm=1001.2014.3001.5501


一、研究背景

随着贪吃蛇是经典的休闲游戏,其核心玩法是通过控制蛇的移动方向,使蛇吃到食物并避免碰撞自身或边界,随着食物的获取,蛇的长度不断增加,游戏难度逐渐提升。传统贪吃蛇游戏依赖玩家的操作反应速度和策略规划能力,而引入 AI 辅助移动功能后,可通过算法自动规划蛇的移动路径,降低操作难度,同时为游戏 AI 路径规划研究提供实践案例。

本代码基于 Pygame 库实现,在保留经典贪吃蛇玩法的基础上,集成了 AI 自动寻路功能,旨在探索基于广度优先搜索(BFS)算法的游戏智能决策机制,提升游戏的可玩性与研究价值。

二、研究目的

  1. 实现一个功能完整的贪吃蛇游戏,支持玩家手动控制与 AI 辅助移动两种模式切换;
  2. 基于 BFS 算法设计蛇头到食物的最短路径规划逻辑,使 AI 能够自主避开障碍物、自身身体及边界;
  3. 验证路径规划算法在动态游戏场景中的有效性(如蛇身长度变化、障碍物随机生成等);
  4. 提供友好的用户交互界面,包括用户名输入、速度设置、游戏状态显示等功能。

三、技术方案

3.1 开发环境与工具

基于 Python 语言,使用 Pygame 库实现游戏窗口渲染、事件处理、图像与音效加载等功能;通过文件操作实现历史最高分的持久化存储。

3.2 核心算法

  • 路径规划:采用 BFS(广度优先搜索)算法,以蛇头为起点、食物为终点,在游戏区域内搜索最短路径。搜索过程中需避开蛇身(除头部外)、障碍物及边界。
  • 碰撞检测:实时检测蛇头与边界、自身身体、障碍物的碰撞,触发游戏结束逻辑。

3.3 功能模块设计

  • 用户交互模块:支持用户名输入、游戏速度调节(1-30 级)、障碍物启用 / 禁用设置;
  • 游戏逻辑模块:蛇的移动控制(手动 / AI 切换)、食物随机生成(含不同分数的食物类型)、分数计算与最高分记录;
  • AI 模块:基于 BFS 算法的路径规划,根据规划结果自动调整蛇的移动方向。

其中,AI模块的可视化界面如下图所示:

四、实现流程

4.1 初始化与配置 

  • 初始化 Pygame 及混音器,定义游戏窗口尺寸(含游戏区域与信息面板)、颜色常量、蛇的移动参数(速度范围、方块大小);

  • 加载游戏资源(蛇头 / 身体 / 食物 / 障碍物图像、音效),若加载失败则使用默认色块与空音效对象替代;

  • 设置支持中文的字体,用于显示文字信息(分数、提示等)。

相较于第九篇帖子,我们主要修改了set_speed()函数,增加了路障启用选项:

  • 游戏开始时没有路障
  • 蛇每吃掉一个食物,会生成一个新路障
  • 路障不会与蛇身或食物重叠
  • 蛇撞到路障会导致游戏结束

路障功能的实现代码如下所示:

# 判断是否撞到路障(如果启用了路障)
if enable_obstacles and obstacles:
    for obstacle in obstacles:
        if x1 == obstacle[0] and y1 == obstacle[1]:
            game_close = True

x1 += x1_change
y1 += y1_change

draw_checkerboard()
dis.blit(current_food["image"], [foodx, foody])

# 绘制路障(如果启用了路障且路障存在)
if enable_obstacles and obstacles:
    for obstacle in obstacles:
        dis.blit(obstacle_img, [obstacle[0], obstacle[1]])

# 吃到食物后增加分数、长度并生成新食物和路障
if x1 == foodx and y1 == foody:
    # 播放吃食物音效
    eat_sound.play()

    score += current_food["score"]  # 增加对应分数
    Length_of_snake += 1  # 增加蛇长度

    # 更新历史最高得分
    if score > high_score:
        high_score = score
        save_high_score(high_score)

    # 生成新食物
    current_food = random.choices(food_types, weights=[ft["probability"] for ft in food_types])[0]
    foodx, foody = generate_valid_position(snake_List, None, None)

    # 如果启用了路障,生成新路障(替换旧路障)
    if enable_obstacles:
        obstacles = [generate_valid_position(snake_List, foodx, foody)]

路障功能的可视化界面如下图所示:

路障功能禁用的可视化界面如下图所示:

4.2 用户输入与设置

  • 通过输入框获取用户名(默认 “玩家 1”);
  • 提供速度调节界面(方向键 ± 速度)、障碍物开关(空格键切换),按回车确认进入游戏。

4.3  游戏主循环

1)手动模式:通过方向键控制蛇的移动方向(限制反向移动,避免自撞);

2)AI 模式(按 A 键切换)

  • 通过输入框获取用户名(默认 “玩家 1”);

  • 提供速度调节界面(方向键 ± 速度)、障碍物开关(空格键切换),按回车确认进入游戏。
  • 调用bfs_shortest_path函数,传入蛇头位置、食物位置、蛇身列表、障碍物等参数;
  • BFS 算法遍历四个方向(上下左右),生成从蛇头到食物的最短路径;
  • 根据路径的下一步位置,自动设置蛇的移动方向(x/y 轴变化量);

3)碰撞检测:实时判断蛇头是否撞墙、撞自身或障碍物,触发游戏结束界面;

4)食物与分数逻辑:蛇吃到食物后,增加对应分数(苹果 + 1、橘子 + 3、西瓜 + 5)、增长蛇身、生成新食物与障碍物(若启用),并更新最高分记录。

其中,核心的bfs_shortest_path函数代码如下所示:

def bfs_shortest_path(snake_head, food_pos, snake_body, obstacles, snake_block, dis_width, dis_height,
                      score_bar_height):
    """使用BFS算法寻找从蛇头到食物的最短路径"""
    # 定义四个方向的移动向量
    directions = [
        (snake_block, 0),  # 右
        (-snake_block, 0),  # 左
        (0, snake_block),  # 下
        (0, -snake_block)  # 上
    ]

    # 转换位置为网格坐标以便于计算
    start = (snake_head[0], snake_head[1])
    target = (food_pos[0], food_pos[1])

    # 如果已经在食物位置,返回空路径
    if start == target:
        return []

    # 初始化队列和已访问集合
    queue = deque()
    queue.append([start])
    visited = set()
    visited.add(start)

    # 将蛇身和障碍物添加到障碍集合
    obstacles_set = set(obstacles) if obstacles else set()
    snake_body_set = set(tuple(segment) for segment in snake_body[:-1])  # 排除蛇头
    obstacles_combined = obstacles_set.union(snake_body_set)

    # BFS主循环
    while queue:
        path = queue.popleft()
        current_pos = path[-1]

        # 尝试四个方向
        for dx, dy in directions:
            next_x = current_pos[0] + dx
            next_y = current_pos[1] + dy
            next_pos = (next_x, next_y)

            # 检查是否到达目标
            if next_pos == target:
                return path[1:] + [next_pos]  # 返回路径(排除起点)

            # 检查是否在有效范围内且未访问过且不是障碍
            if (0 <= next_x < game_area_width and  # 限制在游戏区域内
                    score_bar_height <= next_y < dis_height and
                    next_pos not in visited and
                    next_pos not in obstacles_combined):
                visited.add(next_pos)
                new_path = list(path)
                new_path.append(next_pos)
                queue.append(new_path)

    # 如果找不到路径,返回空列表
    return []

贪吃蛇有三种食物:苹果、橘子、西瓜,分数分别对应:1分、3分、5分,三种食物出现的概率分别为50%、30%、20%。障碍物为地雷图片,实时出现,可视化界面如下所示:

蛇头如果在移动时碰墙或者踩到地雷障碍物,就会结束游戏,并计算分数,可视化界面如下所示:

4.4 界面渲染

  • 绘制棋盘格背景(游戏区域)、蛇身(头部与身体图像)、食物、障碍物;

  • 右侧信息面板显示用户名、当前分数、速度、最高分及 AI 状态,底部显示操作提示。

五、总结

本代码实现了一个兼具娱乐性与研究性的贪吃蛇游戏,核心亮点在于集成了基于 BFS 算法的 AI 辅助移动功能。通过手动与 AI 模式的切换,既保留了传统游戏的操作乐趣,又展示了路径规划算法在动态场景中的应用。

后续可进一步优化:

  1. 增强 AI 算法鲁棒性(如处理 BFS 无解场景的避障策略);
  2. 增加难度递增机制(如随分数提升自动加快速度);
  3. 扩展食物与障碍物类型,丰富游戏策略性。

最后,还上传个该项目的简要演示视频,供大家了解。

手把手实现支持AI辅助移动的贪吃蛇小游戏

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐