欢迎光临
我们一直在努力

博弈树与DQN的权衡 2048游戏的实战

GitHub – UNICORN-2887/2048-ai: 2048 AI — Expectimax + DQN on MuMu Android Emulator · GitHub

众所周知,主包的游戏水平堪比三叠纪草履虫,向来是不擅长打游戏的,因此常做逆向。

今天要讲的是2048的逆向。

下面是在像素危城游戏中的一个小游戏2048,你也可以下一个纯净版的2048,上面仓库提供的代码会有一个简单的标定过程的,你只需要把要识别的区域框进去即可。

逆向工程怎么做

通俗且片面地说,逆向工程的目的就是自动化打游戏,无论是基于规则的自动化,还是引入AI来自动化。

当然,实际上是指用CE反向抓基址、判断哪里断call、通过数据结构判断共享代码的双方如何分辨等等,这些都是方法,最终是要运用在自动化打游戏的目的之上的。

所有的逆向工程,都包含三个层次,

观察层:状态获取

这一层需要通过CV来获取游戏的状态。

第一点,是有哪些类型的状态?理论上如何获取?

一般而言,

无数值状态:使用简单的检色即可。(比如血条,只用检测绿色和黑色的比例)

有数值状态:使用OCR,个人而言,EasyOCR非常好用。其次是Tesseract的OCR。都是免费的。当然,对于一些色彩很奇怪的文字,OCR不那么好用的时候,引入yolo对10个数字做训练也是一种办法。

图像状态(比如怪物类型、标志物、物品栏识别):一般使用yolov8训练。选择Object Detection任务进行训练。

位置状态:非联网游戏用CE搜基址。联网游戏分几种情况:

对于有坐标的,那直接用CE的断call法,去尝试各个位置是否触发中断。

对于有小地图的,可以用二值图拼接+特征匹配。

对于都没有的,只能用我独家的光流法定位。这个我会在DeadMaze游戏的逆向中讲解。

第二点,是实战上如何获取?

对于CE能搜索到的,我们不做讨论。

问题是如何获取实时画面?

我们常用的方案会根据设备不同选择。

对于手游,我们会用模拟器,比如MuMu模拟器就会提供API来截图。

但是MuMu模拟器的截图速度是很低的,实测最高仅3fps,对于时间高精度要求的游戏是很糟糕的。

因此我们常用OBS Studio来实时获取游戏的视频流。可以达到60fps,对于所有游戏都是适用的。

电脑游戏一般也可以用OBS Studio来获取实时视频流。

算法层:

这一层就是如何做。

通常有两种模式,一种是基于规则的算法,是固定的。适用于简单,规则容易归纳的游戏(比如消消乐、可推理的扫雷、较为固定的跑酷)。要求动作空间小,观测空间小的游戏。

另一种是智能模式,是随动的,我常用强化学习。使用于较为复杂的,规则难以描述的游戏(大型MOBA、吃鸡、MC)。对于动作空间大、观测空间大的游戏。

操作层:

这一层就是具体如何控制游戏了。

对于手机游戏,一般都能用模拟器进行操作,我常用MuMu模拟器。当然也有一些游戏不支持模拟器,比如《我是市长》这类。之后的一切逆向工程,都会用MuMu的API来操作,你可以阅读

对于电脑游戏,基本都能调用win32的API进行后台控制。

那么根据上述思路,我们就能做出如下架构

二、技术栈分层解读

第1层:物理控制(打通真实游戏)

组件作用关键技术
MuMu 模拟器 运行真实 2048 App Android 模拟器
ADB 与模拟器通信 adb exec-out screencap -p 截图 adb shell input swipe 模拟滑动
校准系统 定位棋盘格子位置 用户手动框选 16 格 → 保存 JSON

通俗理解:用代码代替你的手指,自动截图看棋盘、自动滑动操作。

第2层:状态识别(让 AI "看见"棋盘)

模拟器截图 → 裁剪 16 个格子 → EasyOCR 识别数字 → 构建 4×4 矩阵

EasyOCR 是一个 OCR(光学字符识别)库,能识别图片中的文字/数字。

关键问题:2048 是主游戏里的"小游戏",棋盘位置不固定 → 需要用户先框选一次做校准。


第3层:AI 决策(两种方案)


三、方案一:DQN(深度强化学习)—— 原理拆解

3.1 强化学习基本框架

Agent(AI)── 执行动作 → 环境(2048游戏)
↑ ↓
└── 获得奖励 + 新状态 ←─┘

核心循环:观察 → 决策 → 执行 → 获得反馈 → 学习


3.2 DQN 的 4 个核心组件

组件通俗解释
Q 网络 一个"打分器":输入棋盘,输出 4 个方向的"预期得分"
目标网络 Q 网络的"备份",定期同步,防止训练震荡
经验回放 记日记:把每次经历存下来,随机抽出来复习,打破惯性思维
ε-greedy 90% 时间选最优动作,10% 随机乱走(探索新策略)

3.3 网络结构

16个数字(4×4棋盘展平) ↓ 256 个神经元 ↓ 128 个神经元 ↓ 64 个神经元 ↓ 4个输出(上/下/左/右的Q值)

问题:全连接网络(MLP)无法理解"位置"信息。它把棋盘当成 16 个独立的数字,丢失了"相邻关系"这个关键信息。


3.4 训练流程(伪代码)

for 步数 in range(300000):
动作 = ε-greedy(当前棋盘) # 选动作
执行动作,得到 新棋盘, 得分, 是否结束
存经验 (旧棋盘, 动作, 得分, 新棋盘)

if 步数 % 16 == 0:
随机抽 256 条经验
计算 Q 值损失
反向传播更新网络

ε 逐渐衰减(从 1.0 → 0.02)


3.5 为什么 DQN 失败了?

1. 左图:episode/highest_tile(最高方块数)

  • 纵轴含义:每一局游戏结束时,棋盘上拼出的最大数字方块(例如 64, 128, 256…)。

  • 横轴含义:训练的步伐/时间(Step)。最右边的 2124 代表当前训练进度。

【如何看趋势】

  • 浅色线(原始数据):波动非常大,一会打到 200 多(比如拼出了 256 或 512),一会又掉到几十。这说明游戏前期发挥极其不稳定。

  • 深色线(平滑均值):你看这条平滑线,它并没有呈现上升趋势,而是一直在 100-120 左右徘徊。 这说明你的智能体并没有在学习进步,或者遇到了瓶颈,它的水平一直卡在能拼出 128 这个方块左右。

2. 右图:episode/moves(步数/移动次数)

  • 纵轴含义:每一局游戏进行了多少次操作才结束(GAME OVER)。

  • 横轴含义:同左图,训练进度。

【如何看趋势】

  • 浅色线(原始数据):在前半段(0 – 1000步左右),极不稳定,出现了很多次超过 500 步甚至达到上限(截断)的情况。

  • 深色线(平滑均值):前期的深色线有向上的趋势(甚至涨到了400多步),但随后在 1000 步附近突然发生了“断崖式下跌”,直接掉到了 150 步左右。

  • 不正常现象:如果智能体学会了策略,它的生存时间(步数)应该越来越长。这里步数突然剧烈减少,在强化学习中通常是非常危险的信号,说明策略“崩了”。

原因详细说明
奖励太稀疏 一盘 500 步,大部分移动得 0 分,AI 不知道哪一步做对了
网络太弱 MLP 看不懂棋盘空间结构,应该用 CNN(卷积神经网络)
训练量不足 30 万步 vs 学术界 1000 万步起步
无效移动惩罚不够 AI 反复尝试被堵死的方向,浪费回合

形象比喻:让一个没有触觉的机器人学炒菜。它只能看到"菜糊了"这个最终结果,却不知道是火太大还是油太少。反馈太少,无法学习。


四、方案二:Expectimax 搜索 —— 原理拆解

4.1 核心思想

"我走一步,想想所有可能发生的随机情况,再想我会怎么应对,再想想随机情况… 最后选最好的那一步。"

这不是"学习",而是"搜索" —— 不需要训练,直接向前模拟推演。


4.2 游戏树的交替结构

┌─────────────────────────────────────────────────┐
│ MAX 层(你的回合) │
│ 你选择:上/下/左/右 → 取期望值最高的 │
│ ↓ │
│ CHANCE 层(系统的回合) │
│ 系统随机在空位放 2(90%) 或 4(10%) │
│ 对所有可能性求"加权平均" │
│ ↓ │
│ MAX 层(你的下一回合) │
│ 你又选最优方向… │
│ ↓ │
│ CHANCE 层… │
│ ↓ │
│ … 到达搜索深度 → 用"启发式函数"打分 │
└─────────────────────────────────────────────────┘

4.3 深度 2 的搜索树规模

第1层:4 个方向
第2层:平均 12 个空位 × 2 种数字(2/4) = 24 种随机情况
第3层:又 4 个方向
第4层:约 6 个空位 × 2 种数字 = 12 种随机情况

总评估次数 = 4 × 24 × 4 × 12 = 4608 次
每次评估就是调用启发式函数打分


4.4 启发式函数 —— 这个最精彩

评估公式(5 个因素加权求和):

总分 = 2.7 × 空格数
+ 1.0 × 单调性(大数靠角,小数靠边)
+ 1.0 × 角落奖励(最大块在角上)
+ 1.0 × 可合并性(相邻相同数字)
– 0.1 × 平滑性(相邻数字差距大则扣分)

举例对比:

棋盘布局空格数单调性角落可合并平滑性总分
大数在角,沿边递减 ✅高 ✅高 ✅高 ✅中 ✅高 高分
大数在中间 ✅高 ❌低 ❌低 ✅中 ❌低 低分

核心策略:2048 高手的直觉被编码成了数学公式,AI 直接"学会"了人类经验。


4.5 Expectimax 的优势

维度解释
零训练 开箱即用,不需要跑几十万步训练
可解释 每个方向的评分可以输出,知道为什么选这个方向
深度可调 depth=2 就够用,depth=3 更强但更慢
自然处理随机 CHANCE 层直接求期望,完美应对"随机放 2 还是 4"

五、两种方案对比(核心总结)

对比维度DQN(强化学习)Expectimax(搜索)
本质 从经验中学习 向前模拟推演
需要训练 ✅ 需要数百万步 ❌ 零训练
人类知识 不需要(自己摸索) 需要(编码启发式函数)
效果 ❌ 最高方块 128 ✅ 最高方块 2048
速度 推理极快(1次前向传播) 较慢(评估数千棋盘)
适用场景 复杂、难编码规则的任务 规则明确、可编码启发式的任务

─────────────────────────────────────────────
Step 446 | Expectimax depth=3
[1] UP: ████ 37.9
[2] DOWN: 28.6
[3] LEFT: █████ 39.2
[4] RIGHT: █████ 40.1 ◀
─────────────────────────────────────────────
→ AI 选择: RIGHT ➡
→ 执行: RIGHT ➡

执行后棋盘 (OCR 识别):
┌──────┬──────┬──────┬──────┐
│ 1024│ 16│ 4│ 2│
├──────┼──────┼──────┼──────┤
│ · │ 4│ 128│ 16│
├──────┼──────┼──────┼──────┤
│ 2│ 256│ 64│ 32│
├──────┼──────┼──────┼──────┤
│ · │ 2│ 1│ 4│
└──────┴──────┴──────┴──────┘

─────────────────────────────────────────────
Step 447 | Expectimax depth=3
[1] UP: ██████ 33.2
[2] DOWN: 20.2
[3] LEFT: █████████ 39.3 ◀
[4] RIGHT: 无效
─────────────────────────────────────────────
→ AI 选择: LEFT ⬅
→ 执行: LEFT ⬅

执行后棋盘 (OCR 识别):
┌──────┬──────┬──────┬──────┐
│ 1024│ 16│ 4│ 2│
├──────┼──────┼──────┼──────┤
│ 4│ 128│ 16│ 4│
├──────┼──────┼──────┼──────┤
│ 2│ 256│ 64│ 32│
├──────┼──────┼──────┼──────┤
│ 2│ 16│ 4│ · │
└──────┴──────┴──────┴──────┘
合并得分: +19 | 累计: 6972 | 最高: 1024

─────────────────────────────────────────────
Step 448 | Expectimax depth=3
[1] UP: ██████ 37.2 ◀
[2] DOWN: 24.6
[3] LEFT: 无效
[4] RIGHT: █ 27.0
─────────────────────────────────────────────
→ AI 选择: UP ⬆
→ 执行: UP ⬆

执行后棋盘 (OCR 识别):
┌──────┬──────┬──────┬──────┐
│ 1024│ 16│ 4│ 2│
├──────┼──────┼──────┼──────┤
│ 4│ 128│ 16│ 4│
├──────┼──────┼──────┼──────┤
│ 4│ 256│ 64│ 32│
├──────┼──────┼──────┼──────┤
│ 2│ 16│ 4│ · │
└──────┴──────┴──────┴──────┘
合并得分: +2 | 累计: 6974 | 最高: 1024

─────────────────────────────────────────────
Step 449 | Expectimax depth=3
[1] UP: ████ 31.1
[2] DOWN: 22.5
[3] LEFT: 无效
[4] RIGHT: ██████ 34.8 ◀
─────────────────────────────────────────────
→ AI 选择: RIGHT ➡
→ 执行: RIGHT ➡

执行后棋盘 (OCR 识别):
┌──────┬──────┬──────┬──────┐
│ 1024│ 16│ 4│ 2│
├──────┼──────┼──────┼──────┤
│ 4│ 128│ 16│ 4│
├──────┼──────┼──────┼──────┤
│ 4│ 256│ 64│ 32│
├──────┼──────┼──────┼──────┤
│ 2│ 2│ 16│ 4│
└──────┴──────┴──────┴──────┘
合并得分: +2 | 累计: 6976 | 最高: 1024

─────────────────────────────────────────────
Step 450 | Expectimax depth=3
[1] UP: ████████████████████████████████████████████████████████ 25.9
[2] DOWN: -86.5
[3] LEFT: ███████████████████ -47.0
[4] RIGHT: ████████████████████████████████████████████████████████████ 34.4 ◀
─────────────────────────────────────────────
→ AI 选择: RIGHT ➡
→ 执行: RIGHT ➡

执行后棋盘 (OCR 识别):
┌──────┬──────┬──────┬──────┐
│ 1024│ 16│ 4│ 2│
├──────┼──────┼──────┼──────┤
│ 4│ 128│ 16│ 4│
├──────┼──────┼──────┼──────┤
│ 4│ 256│ 64│ 32│
├──────┼──────┼──────┼──────┤
│ 2│ 4│ 16│ 4│
└──────┴──────┴──────┴──────┘
合并得分: +2 | 累计: 6978 | 最高: 1024

─────────────────────────────────────────────
Step 451 | Expectimax depth=3
[1] UP: ████████████████████████████████████████████████████ 8.5 ◀
[2] DOWN: -96.9
[3] LEFT: 无效
[4] RIGHT: 无效
─────────────────────────────────────────────
→ AI 选择: UP ⬆
→ 执行: UP ⬆

经过一段时间的运行,你就能在游戏中断层领先了:

赞(0)
未经允许不得转载:171主机测评 » 博弈树与DQN的权衡 2048游戏的实战
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址