贪吃蛇游戏AI自动游玩的强化学习模型

你有没有试过盯着贪吃蛇发呆——看着那条小蛇一圈圈绕着走,明明食物就在眼前,却怎么也吃不到?😅 或者更惨:刚吃完一口,下一秒直接撞自己身上……这不怪你,毕竟人类反应有限。但如果是AI来玩呢?

别急着说“不就是个老掉牙的游戏吗?”——嘿,可别小看它!🐍 这个看似简单的像素小蛇,其实藏着不少门道:路径规划、避障决策、长期收益权衡……换句话说,它是训练AI做“聪明选择”的绝佳 playground!

最近几年,深度强化学习(DRL)火得不行🔥,而贪吃蛇就成了很多研究者手里的“小白鼠”。比起传统靠写死规则的方法(比如A*寻路 or Hamilton回路),DRL让AI从零开始自己学!不需要任何人教它“往哪儿拐”“怎么绕开身子”,只要告诉它啥事该奖励、啥事要惩罚,剩下的交给神经网络去摸索就行。

今天咱们就来干一票大的: 亲手打造一个会自己玩贪吃蛇的AI模型 。不整虚的,直接上硬核内容——状态怎么编码?动作咋设计?奖励函数怎么调才不让AI“摆烂”?DQN和PPO到底谁更适合这个任务?通通安排!


先来点直观感受:想象一下,AI控制的小蛇在屏幕上灵活穿梭,闻着“食物味儿”一路狂奔,遇到墙知道绕,快撞身时果断变向……而且越玩越强,从一开始乱冲乱撞,到后来能预判几步后的局势——是不是有点赛博生命的那味儿了?😎

要实现这一切,核心就在于四个字: 状态、动作、奖励、算法 。我们一个个拆开揉碎讲。

状态表示:让AI“看见”当前局面

首先,AI得知道自己在哪、食物在哪、周围有没有危险。但这可不是让它真去看画面(虽然也能这么做,用CNN处理图像),而是把关键信息提炼成一组数字向量——也就是“状态”。

常见的做法有三种:
- 坐标差值法 :只保留蛇头到食物的相对位置(dx, dy)
- 网格独热编码 :把地图切成N×N格子,每个格标0或1(有没有蛇/食物/墙)
- 视觉输入 :直接喂渲染好的图像给CNN(像Atari游戏那样)

对于贪吃蛇这种结构清晰的小环境,其实没必要上CNN大模型🧠。我推荐一种轻量又高效的方案: 融合相对方向 + 局部感知

什么意思呢?除了告诉AI“食物在左上方”,还让它感知前方、左前方、右前方有没有障碍物——就像蛇长了三只“触角”一样。这样一来,哪怕地图变大,策略也能泛化。

下面是具体实现👇:

import numpy as np

def get_state(snake, food, grid_size):
    head = snake[0]
    # 食物相对于蛇头的方向(归一化为-1, 0, 1)
    food_dir = (np.sign(food[0] - head[0]), np.sign(food[1] - head[1]))

    def is_collision(x, y):
        if x < 0 or y < 0 or x >= grid_size or y >= grid_size:
            return True
        return (x, y) in snake  # 包括身体碰撞

    # 当前移动方向向量
    directions = {
        'up': (0, -1),
        'right': (1, 0),
        'down': (0, 1),
        'left': (-1, 0)
    }
    current_dir = directions[snake.direction]

    # 前方是否危险
    front = is_collision(head[0] + current_dir[0], head[1] + current_dir[1])

    # 左转后的方向(逆时针90度)
    left_dir = (-current_dir[1], current_dir[0])
    left = is_collision(head[0] + left_dir[0], head[1] + left_dir[1])

    # 右转后的方向(顺时针90度)
    right_dir = (current_dir[1], -current_dir[0])
    right = is_collision(head[0] + right_dir[0], head[1] + right_dir[1])

    # 构建12维状态向量
    state = [
        int(front), int(left), int(right),           # 感知三方向障碍
        int(food_dir[0] == 1), int(food_dir[0] == -1),  # 水平方向食物
        int(food_dir[1] == 1), int(food_dir[1] == -1),  # 垂直方向食物
        int(current_dir == (0,-1)),                    # one-hot 编码当前方向
        int(current_dir == (1,0)),
        int(current_dir == (0,1)),
        int(current_dir == (-1,0))
    ]
    return np.array(state)

这套编码方式只有12维,足够简洁,又能覆盖所有必要信息。重点是用了“相对视角”——不管蛇朝哪,它都能判断“左边危险吗?”“食物在我右下方吗?”,这对提升策略泛化能力特别有用。

💡 小贴士:记得把坐标归一化到 [-1,1] [0,1] ,不然训练起来梯度飞得满天跑~


动作空间:别让AI“自杀式转弯”

接下来是动作设计。最 naive 的想法是让AI直接选“上、下、左、右”。听起来合理?错!🚨

因为一旦允许“向上时突然向下”,等于当场自杀。这种无效动作会严重拖慢学习速度。

聪明的做法是采用 相对动作系统
- 0 : 继续前进(不变向)
- 1 : 左转90°
- 2 : 右转90°

这样无论当前朝哪个方向,左转永远是逆时针,右转永远是顺时针。不仅避免了反向自杀,还让学到的策略更具通用性——毕竟AI不再依赖绝对方位,而是学会“看到食物在左边 → 我该左转”。

实验数据也支持这一点:在相同DQN架构下,使用相对动作比绝对方向收敛速度快约40%!⚡️

当然,你也可以加个“后退”选项?算了兄弟,那是自掘坟墓🙃。


奖励函数:别让AI变成“躺平族”

强化学习最怕啥? 稀疏奖励 。如果只有吃到食物才给+10分,其他时候全是0,那AI可能玩了几万步都还没尝过甜头,根本不知道自己在干嘛。

这就像是让你闭着眼投篮,进了才告诉你“对了!”,否则啥也不说……你能学会吗?大概率放弃吧。

所以我们得给AI一点“提示信号”,帮它建立行为与结果之间的联系。这就是所谓的 稠密奖励 (dense reward)。

来看看我的奖励设计方案:

事件 奖励
吃到食物 🍎 +10
更靠近食物 ➕ +0.1
远离食物 ➖ -0.1
撞墙 or 自撞 💥 -10
长时间无进展(防绕圈) -1

代码实现如下:

def calculate_reward(old_state, new_state, done, ate_food):
    if done:
        return -10  # 死亡重罚,别想不开

    # 计算到食物的距离变化
    old_dist = np.linalg.norm(old_state[4:6])  # 假设第5~6位是dx, dy
    new_dist = np.linalg.norm(new_state[4:6])

    distance_reward = (old_dist - new_dist) * 0.1  # 靠近加分,远离扣分

    if ate_food:
        return 10 + distance_reward
    else:
        return distance_reward

这个设计妙在哪?
👉 它给了AI持续的反馈:哪怕没吃到食物,只要你在往正确方向挪,就有正反馈;反之瞎晃悠就会被轻微惩罚。
👉 加上“死亡-10”的严厉处罚,AI很快就能明白:“哦,撞墙这事不能干。”

⚠️ 注意平衡!惩罚太狠会导致AI过度保守,躲在角落不敢动;太轻又容易频繁送命。建议初期用 -10 ,后期可根据表现微调。

还可以额外加个“存活奖励”:每活一步+0.01,鼓励它多撑一会儿,防止早早结束局数影响训练效率。


算法选型:DQN vs PPO,谁才是贪吃蛇之王?

现在轮到最关键的环节:用什么算法来训练?

🟡 DQN(Deep Q-Network)

经典中的经典。适合离散动作空间,贪吃蛇的三动作正好fit。

优点:
- 结构简单,两层全连接搞定
- 收敛快,适合快速验证想法
- 经验回放 + 目标网络,稳定性不错

缺点:
- 对超参敏感(学习率、ε衰减等)
- Q值估计有时震荡,需要耐心调试

🟢 PPO(Proximal Policy Optimization)

现代主流选手,属于策略梯度方法。

优点:
- 更新稳定,不容易崩
- 样本利用率高,适合复杂任务
- 支持连续动作(虽然后者在这用不上)

缺点:
- 实现稍复杂
- 初期训练慢一点

📌 实测建议:
- 如果你是新手 👉 先上 DQN ,跑通流程,理解整个pipeline。
- 如果你想追求更高性能、更稳的表现 👉 上 PPO ,尤其是当你打算扩展到更大地图或多蛇对抗时。

下面是DQN的核心训练片段(PyTorch版):

import torch
import torch.nn as nn
import torch.optim as optim

class DQN(nn.Module):
    def __init__(self, input_dim, n_actions):
        super().__init__()
        self.network = nn.Sequential(
            nn.Linear(input_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 128),
            nn.ReLU(),
            nn.Linear(128, n_actions)
        )

    def forward(self, x):
        return self.network(x)

# 初始化
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
policy_net = DQN(state_dim, 3).to(device)
target_net = DQN(state_dim, 3).to(device)
optimizer = optim.Adam(policy_net.parameters(), lr=1e-4)
criterion = nn.MSELoss()

# 训练循环(简化)
for batch in replay_buffer.sample(32):
    states, actions, rewards, next_states, dones = batch.to(device)

    q_values = policy_net(states).gather(1, actions.unsqueeze(1)).squeeze()

    with torch.no_grad():
        max_next_q = target_net(next_states).max(1)[0]
        target_q = rewards + 0.99 * max_next_q * (1 - dones)

    loss = criterion(q_values, target_q)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    # 定期更新目标网络
    if step % 1000 == 0:
        target_net.load_state_dict(policy_net.state_dict())

看到没?并不复杂!一个标准MLP,加上经验回放和目标网络,就能让AI从零开始学会吃豆人式的生存之道。


实际应用中那些坑,我都替你踩过了 🛠️

你以为写完代码就能坐等AI称霸贪吃蛇?Too young too simple 😏

真实训练过程中,你会遇到各种诡异行为:

🌀 无限绕圈 :AI发现原地打转不会死,还能偶尔蹭点距离奖励,干脆就不动了。
✅ 解法:加入“生存时间奖励”(+0.01/step),并检测连续状态重复,超过阈值就惩罚。

📍 贴边游走 :沿着墙走确实安全,但容易错过食物。
✅ 解法:增加“中心吸引力”奖励,或者动态调整探索率。

🎯 贪近忘远 :AI只顾眼前一口,不顾长远路径,导致把自己围死。
✅ 解法:引入更复杂的奖励 shaping,比如预测未来几步可达性,或使用分层RL。

🔧 我的最终配置建议:

项目 推荐设置
状态表示 相对方向 + 三向感知 + one-hot方向
动作空间 相对动作(前行/左转/右转)
网络结构 MLP(128→128)即可,无需CNN
探索策略 ε-greedy,从1.0线性衰减至0.05
训练地图 多尺寸混合(10×10 ~ 20×20),增强鲁棒性
评估指标 平均得分、最长存活步数、成功率

最后说点掏心窝的话 ❤️

这个项目看起来只是让AI玩个小游戏,但它背后的意义远不止于此。

你想想, 一个从未见过贪吃蛇的神经网络,仅凭试错和奖励信号,就能学会觅食、避障、规划路径 ——这不是魔法是什么?

更重要的是,这套框架完全可以迁移到真实世界的问题上:
- 机器人导航 ✅
- 自动驾驶决策 ✅
- 物流路径优化 ✅

贪吃蛇就像是一块“AI启蒙积木”,简单却不失深刻。你可以在这上面试验各种新想法:加注意力机制看看能不能更好关注食物,试试模仿学习让AI学高手操作,甚至搞个多智能体竞争版本……

所以啊,别嫌弃它老派。有时候, 最经典的,反而最能照亮未来的路 。✨

“Every expert was once a beginner.”
—— 而你的第一步,也许就从这条小蛇开始 🐍

更多推荐