# 第六部分 · 开悟《王者荣耀》MOBA 强化学习实战

> 目标：把前五部分学到的 **状态 / 动作 / 奖励 / 策略 / 价值 / PPO** 用到一个**真实、复杂、
> 亿级玩家验证过**的 MOBA 环境上——腾讯 [开悟（Kaiwu）](https://aiarena.tencent.com/) 的
> 《王者荣耀》**离线本地仿真**。全程**纯 CPU 起步**、**Docker 一键复现**。

前面几课我们在**「鲁班瞄准」这类王者子任务**里理解了强化学习。
它们简单、直观、纯 CPU 几分钟就能训出来，非常适合学概念。但完整的对局要复杂得多：
**491 维的状态、复合动作、稀疏且多维的奖励、还有一个会反击的对手**。

《王者荣耀》1v1 就是这样一个环境，而开悟把它**封装成了标准的强化学习接口**，
让我们不需要真机、不连线上服务器，就能在本地安全地做实验。

> 🪜 回忆[第一课的子任务阶梯](part0-setup.md#045-大任务打不动那就拆成小任务本课程的关键思路)：
> **鲁班瞄准 → 补刀 → 风筝 → 塔下生存 → 完整 1v1**。前四级你已经能在 CPU 上亲手训通；
> 这一课我们登上最后一级——但方法论完全一样，只是状态更大、动作更复合。

> 📄 **本章的权威依据**：腾讯 AI Lab 与上海交大在 NeurIPS 2022 发表了
> [*Honor of Kings Arena: an Environment for Generalization in Competitive RL*](https://arxiv.org/abs/2209.08483)，
> 正式介绍了这个环境的**观测/动作/奖励规范、API、baseline 结果与泛化挑战**。
> 下面的维度、权重、超参、实验结论都**来自这篇论文**，你可以放心引用到作业与报告里。

---

## 6.1 为什么选「开悟」而不是操控真机？

网上有一类项目（如 `wzry_ai`）用截屏 + adb 触控去操作**真机上的线上游戏**。
它很酷，但**不适合教学**：需要每人一台手机和游戏账号、依赖 GPU 做视觉识别、
而且用 AI 自动操作**线上竞技对局违反游戏条款、有封号风险**。

开悟走的是另一条路——**离线本地仿真**：

| 对比 | 操控真机(如 wzry_ai) | 开悟离线仿真(本课程) |
| --- | --- | --- |
| 是否需要真机/账号 | 需要 | **不需要** |
| 是否连线上服务器 | 是（有封号风险） | **否，完全本地** |
| 状态来源 | 截屏 + 目标检测(需 GPU) | **游戏引擎直接给结构化状态** |
| 合规性 | 灰色地带 | **官方面向教学/科研开放** |
| 可复现性 | 差（依赖设备/画面） | **好（Docker 封装）** |

> 一句话：开悟让我们把精力放在**强化学习本身**，而不是"怎么截屏、怎么点屏幕"。

---

## 6.2 环境架构：gamecore 就是我们的「环境」

回忆第四部分：一个强化学习环境的核心是 `reset() / step(action)`，返回
`观测 / 奖励 / 是否结束`。开悟的结构完全对得上：

```
你的策略(Python, CPU/torch)
      │  动作 action
      ▼
  hok_env SDK  ──(ZMQ 通信)──►  gamecore-server ──► (Wine) 游戏逻辑核心 libgamecore
      ▲                                                        │
      │  观测 observation / 合法动作 legal_action / 奖励 reward / done
      └────────────────────────────────────────────────────────┘
```

- **gamecore = 环境**：运行完整的《王者荣耀》游戏逻辑（英雄、技能、小兵、防御塔……）；
- **hok_env SDK = 接口层**：把 gamecore 的原始帧解析成 `numpy` 观测，并把你的动作发回去；
- 游戏逻辑本身**跑在 CPU**——所以"跑环境"不需要 GPU（只有训练神经网络时 GPU 才加速）。

> 🛠️ 环境怎么装、怎么起，见 [`kaiwu_env/README.md`](kaiwu_env/README.md)。
> 本章聚焦"**装好之后，强化学习怎么做**"。

---

## 6.3 状态（Observation）：491 维的战场快照

在「鲁班瞄准」里状态只有 5 个数；在开悟 1v1 里，论文给出的默认观测是一个 **491 维向量**，
把当前这一帧的战场信息编码了进去。它由**五大组件**构成（论文 Table 3）：

| 组件 | 含义 | 维度 |
| --- | --- | --- |
| `HeroStatePublic` | 英雄公开状态：血量HP、蓝量MP、等级、经验、坐标、技能状态… | 49 × 2 |
| `HeroStatePrivate` | 各英雄专属技能信息（如貂蝉技能位置与 buff、露娜/铠等专属状态） | 49~53 × 2 等 |
| `VecCreeps` | 小兵状态与位置：血量、阵营、攻击范围、绝对/相对坐标 | (12+6) × 4 |
| `VecTurrets` | 防御塔与水晶的状态与位置 | (12+6) × 4 |
| `VecCampsWholeInfo` | 把整局时间划分成的 **5 个时期** | 5 |
| **合计** | 所有特征 | **491** |

> 🔎 论文提示两个关键设计：
> 1. **观测跨英雄统一**（都是 491 维），这才有可能让模型**泛化到不同英雄**（见 6.9）。
> 2. 返回的 `info` 里连**敌方不可见单位的真实状态也带着**——用于"事后学习(hindsight)"研究，
>    但**在真正执行策略时必须 mask 掉**，否则相当于开了透视（违反部分可观测设定）。

```python
obs, reward, done, state = env.reset(camp_config, use_common_ai=[False, True])
print(state[0].keys())
# dict_keys(['observation', 'legal_action', 'reward', 'done',
#            'sub_action_mask', 'frame_no', 'player_id', 'req_pb', ...])
print(state[0]["observation"].shape)   # (491,) 的浮点向量——这就是"状态 s"
```

> 📌 概念对照（第一、三部分）：这里的 491 维 `observation` 就是[第一课词典表](part0-setup.md)里的**状态 \(s\)**、
> 也是[第一部分 1.7](part1-linear-algebra.md) 里那个"局势向量"的**真实版**（我们当时用 6 维做了简化）。
> 状态越高维，越考验函数逼近器（神经网络）的表达能力。

---

## 6.4 动作（Action）：复合动作 + 合法动作掩码

「鲁班瞄准」的动作只是"16 个发射方向"里选一个。完整 MOBA 的动作是**复合动作**，论文把它设计成一个
**层次化的三元组**：①按哪个键 ②怎么走/怎么放 ③指向谁。对应到**多个子动作头**（论文 Table 4）：

| 子动作头 | 取值 | 维度 |
| --- | --- | --- |
| **按钮 Button** | 无 / 移动 / 普攻 / 技能1 / 技能2 / 技能3 / 治疗 / 召唤师技能 / 回城 / 技能4 / 装备技能 | 各 1（共 ~13 类） |
| **移动方向** | X、Z 两轴各离散成 16 个方向 | 16 + 16 |
| **技能方向** | X、Z 两轴各离散成 16 个方向 | 16 + 16 |
| **目标单位 Target** | 无 / 自己 / 敌方英雄 / 最近的 4 个小兵 / 最近的塔 | 1+1+1+4+1 |

> 📌 注意论文的一句话：**不同英雄有不同的"禁用技能偏移"**——因为每个英雄技能不同。
> 这正是[论文标题里的"泛化"难点](#69-泛化挑战论文的核心研究问题)：换个英雄，动作的"含义"都变了。

关键点是**合法动作掩码 `legal_action`**：并非任何时候都能放技能（冷却中、蓝不够、
被控制、没目标都不行）。环境会告诉你**当前哪些动作合法**，策略只能在合法集合里选：

```python
import numpy as np

# 取出各子动作头的长度，把扁平的 legal_action 切成若干段
shapes = env.action_space()
split = np.cumsum(shapes[:-1])
legal_per_head = np.split(state[0]["legal_action"], split)

# 只在"合法"的取值里选择（这里用随机；换成网络输出即为策略）
action = []
for head in legal_per_head:
    legal_ids = [k for k, ok in enumerate(head) if ok == 1]
    action.append(np.random.choice(legal_ids))
```

> 📌 概念对照：合法动作掩码是 RL 落地的常见工程手段——在策略网络输出的 logits 上
> 对非法动作**置 -∞** 再 softmax，保证采样出的动作一定合法（既加速学习又避免无效探索）。
>
> 🧪 **论文消融实验（H.2）**：去掉 legal action mask 后，智能体在如此大的动作空间里
> **很快收敛到糟糕的局部最优**——这从实验上证明了掩码对训练效率至关重要，不是可有可无的技巧。

> 🕒 **决策频率**：论文默认每 **133 毫秒**执行一次动作（约等于高水平业余玩家的反应速度），
> 且该间隔可配置。这也解释了为什么一局会有 2000+ 个决策步。

---

## 6.5 奖励（Reward）：多维、可塑形

在「鲁班瞄准」里奖励只有"命中 +1、未命中 -0.1"。完整 MOBA 的胜负由许多因素累积而成，
所以开悟提供**多维子奖励**，分为**五大类**（论文附录 F）：

1. **发育类 (Farming)**：金币、经验，以及"不作为"的惩罚——**密集**信号；
2. **KDA 类**：击杀 / 阵亡 / 助攻、对敌方单位的最后一击——**稀疏**信号；
3. **伤害类 (Damage)**：血量（密集）、对敌方英雄的伤害量（稀疏）；
4. **推进类 (Pushing)**：对敌方塔与水晶的伤害——**密集**；
5. **胜负类 (Win/Lose)**：推掉敌方水晶——**稀疏**，一局结束才拿到。

论文给出的一组默认权重（Table 5）如下，我们的 `config.json` 直接对齐它：

```json
{
  "reward_hp_point": "2.0",        // 英雄血量（dense）
  "reward_tower_hp_point": "10.0", // 推塔/水晶血量（dense）——权重最高，导向最终胜利
  "reward_money": "0.006",         // 金币（dense）
  "reward_ep_rate": "0.75",        // 蓝量比例（dense）
  "reward_exp": "0.006",           // 经验（dense）
  "reward_dead": "-1.0",           // 阵亡惩罚（sparse，论文 Table 5 记为 death）
  "reward_kill": "-0.6",           // 击杀项（sparse，论文原表即为此值）
  "reward_last_hit": "0.5",        // 补刀（farming 类）
  "log_level": "4"
}
```

> 📌 概念对照（第二、三部分）：最终目标仍是最大化**累计折扣回报** \(G_t=\sum_k \gamma^k r_{t+k}\)。
> 子奖励把"赢"这个**稀疏**目标，拆成"补刀、推塔、掉血"等**密集**信号，缓解稀疏奖励下学不动的问题。
> 论文也坦言：只用密集奖励仍可能"退化成稀疏"，鼓励研究者引入**好奇心/内在奖励**等辅助信号。
>
> ⚠️ 奖励塑形是把双刃剑：注意 `tower_hp_point` 权重(10.0)远大于其它项——这是在告诉 AI
> "**推塔/破水晶才是终极目标**"。权重设不好，智能体会学到"钻空子"的怪异策略。这正是好实验题。

---

## 6.6 最小交互骨架：一局随机智能体

把 6.3~6.5 串起来，就是强化学习最基本的**交互循环**（和第四部分的 Gym 循环同构）：

```python
obs, reward, done, state = env.reset(camp_config, use_common_ai=[False, True])
step = 0
while not (done[0] or done[1]):
    actions = random_legal_action(env, state, common_ai=[False, True])  # 随机合法动作
    obs, reward, done, state = env.step(actions)
    step += 1
env.close_game()
```

完整可运行脚本见 [`kaiwu_env/test_1v1_random.py`](kaiwu_env/test_1v1_random.py)，
在环境里一条命令即可跑通一局：

```bash
docker exec -it kaiwu-rl-cpu python3 /rl_framework/test_1v1_random.py
```

> 这就是 baseline 的"零分选手"。**把 `random_legal_action` 换成一个神经网络策略，
> 就正式进入训练。**

---

## 6.7 从随机到学习：策略网络 + PPO + 自对弈

开悟 1v1 的动作是复合的、状态是高维的，最适合的算法是**策略梯度类**，
尤其是第五部分讲过的 **PPO**（稳定、样本效率尚可、工业界主力）。整体框架：

1. **策略网络 \(\pi_\theta(a\mid s)\)**：输入几百维状态，输出每个子动作头的概率分布
   （对非法动作用掩码屏蔽）；同时输出价值 \(V(s)\) 供 PPO 的优势估计。
2. **采样（Actor）**：用当前策略在 gamecore 里跑很多局，收集 `(s, a, r, s')` 轨迹。
3. **训练（Learner）**：用 PPO 的裁剪目标更新 \(\theta\)。
4. **自对弈（Self-Play）**：让智能体和"过去的自己/内置 AI"对打，逐步变强。

```
   ┌── Actor：用 πθ 在 gamecore 采样轨迹 ──┐   （CPU 多进程并行开多局）
   │                                      ▼
   │                              经验缓冲区 (s,a,r,...)
   │                                      │
   └──── 更新后的 θ ◄── Learner：PPO 更新 πθ、V ◄┘   （神经网络，GPU 可加速）
```

> 📌 开悟官方仓库 [hok_env](https://github.com/tencent-ailab/hok_env) 提供了
> **PPO baseline + actor-learner 分布式框架**；
> [Unakar/AI_Game_KingGlory](https://github.com/Unakar/AI_Game_KingGlory) 是一份 1v1 参考实现。
> 学习路线建议：**先跑通官方 baseline，读懂它的网络/reward/采样，再动手改**。

### 论文给出的关键超参（附录 G，可直接抄）

| 超参 | 值 | 备注（对应前几部分的概念） |
| --- | --- | --- |
| 优化器 / 学习率 | Adam / `1e-4` | 第五部分 5.9：α 最关键 |
| 折扣 `γ` | **0.997** | ≈ 未来奖励**半衰期 46 秒**——非常看重长远（推塔赢局） |
| GAE `λ` | 0.95 | 降低延迟奖励带来的方差 |
| PPO 双裁剪 `ε, c` | 0.2, 3 | 5.7 的裁剪目标 + 大规模训练用的 dual-clip |
| DQN 目标网络 `γ` | 0.98 | 对照组 |

> 🧪 **PPO vs DQN（论文 Figure 4）**：两者都能在 3000M 样本内打败内置 BT，
> 但 **PPO 的最终表现明显优于 DQN**——这印证了第五部分"复合动作+高维状态首选 PPO"的判断。
>
> 🧪 **两个提升技巧的消融**：① **dual-clip PPO**（H.1）比原始 PPO 略好；
> ② **LSTM**（H.3）帮助处理部分可观测与"技能连招"的时序依赖，带记忆的模型更强。

---

## 6.8 纯 CPU 训练：现实与建议

游戏逻辑用**高度优化的 C++** 编写，**跑通环境、单局对战、baseline 推理、小规模训练都能纯 CPU 完成**。
论文给出了很有说服力的实测数据：

- **采样吞吐**：单台 10 核机器跑 10 个并发环境，约 **434 万样本/小时**（≈600 条轨迹/小时）；
  规模化到 200 台机器、2000 个并发环境可达 **8 亿样本/小时**。
- **训练可行性（Table 1）**：用**自对弈**打败 Gold 级内置 BT 所需时间随 CPU 核数下降——

| 采样 CPU 核数 | 打败 BT 所需训练时间 |
| --- | --- |
| 128 | ~6.16 小时 |
| 256 | ~1.67 小时 |
| 512 | ~1.08 小时 |
| 1024 | ~0.90 小时 |
| 2048 | ~0.89 小时 |

> 🔑 论文的关键发现：**瓶颈是 CPU（采样）而不是 GPU（训练）**——多给 GPU 收益有限，
> 多给 CPU 才能更快产出样本。这正好解释了本课程"**纯 CPU 起步**"的合理性：
> 采样这一环本来就靠 CPU，只是核数越多越快而已。

但也要清醒认识：上表是**上千核**的成绩。课堂上只有几核到几十核，
**跑通与做小规模实验完全够，但别指望短时间训出很强的 AI**。我们**务实定位**：

| 目标 | 是否适合纯 CPU | 说明 |
| --- | --- | --- |
| **王者子任务**（鲁班瞄准/补刀/风筝…） | ✅ **非常适合** | 低维、离散动作，几分钟训出来——**新手就从这里开始** |
| 跑通完整 1v1 环境 / 看懂接口 | ✅ 很合适 | 本章重点 |
| 随机/规则 baseline 对局、看回放 | ✅ | 秒级~分钟级 |
| 完整 1v1 上小规模 PPO、改 reward 看行为变化 | ✅ 可行 | 用多核并行采样，耐心等 |
| 从零训出**很强的**完整 1v1 AI | ⚠️ 需大量 CPU | 论文用上千核约 1 小时打败黄金 BT；课堂算力有限，**优先拆成子任务** |

> 🪜 **务实路线（呼应任务拆解）**：不要一上来就死磕完整 1v1。
> 先在[第四部分的子任务阶梯](part4-environments.md#43-王者子任务阶梯我们的训练场清单)上把算法练熟
> （鲁班瞄准 → 补刀 → 风筝 → 塔下生存），每一个都能在几核 CPU 上训出成果、看到明显进步；
> 再把这些能力组合、迁移到完整对局。**大问题打不动，就拆成能打动的小问题**——这本身就是核心技能。

**纯 CPU 提速小技巧**：

- **多进程并行采样**：开多个 gamecore 对局同时采样（瓶颈在采样而非训练）；
- **降采样频率**：不必每帧都推理决策（如每 3 帧决策一次）；
- **缩小网络 / 简化任务**：先固定同一个英雄、镜像对局，降低泛化难度；
- 需要加速再上 GPU（[`kaiwu_env/README.md`](kaiwu_env/README.md) 第 8 节有 GPU 档说明）。

---

## 6.9 泛化挑战：论文的核心研究问题

论文标题里的关键词是 **Generalization（泛化）**。开悟支持 **20 个英雄**，
"我方英雄 × 敌方英雄"就有 **20 × 20 = 400 个任务**。它提出了两类泛化难题：

- **对手泛化 (across opponents)**：固定我方英雄（如貂蝉），换不同的敌方英雄。
  论文实验：只用"貂蝉 vs 貂蝉"训练的模型，打貂蝉能赢 90%，但**换个对手英雄胜率暴跌**。
- **目标泛化 (across targets)**：换我方操控的英雄。由于"每个英雄的技能与动作含义都不同，
  换英雄≈换一个游戏"，同一策略迁到别的英雄上同样**大幅退化**。

> 这说明：**现有 RL 方法在竞争性、多任务设定下泛化能力很弱**——这正是这个环境作为
> benchmark 的价值所在，也是留给你们的开放研究题。

**论文给出的两种缓解办法**（都能提升泛化，供实验参考）：

1. **多任务训练 (Multi-task)**：训练时就混入多个英雄任务（如 5 个英雄），测 20 个任务表现更好；
2. **策略蒸馏 (Distillation)**：把多个单任务专家模型蒸馏成一个学生模型，效果与多任务相当。

> 📌 概念对照：这把[第一课](part0-setup.md)的"AI 学会打王者"具体化为"**学会打任意英雄、对任意对手**"——
> 难度从"解一个 MDP"升级到"解一族相关的 MDP"。这也是当前强化学习最前沿的方向之一。

---

## 6.10 怎么算"变强了"：BT 基线 + Elo 分数

训练出来怎么评估？论文提供了两类对手与一套科学的评分方法：

- **规则基线 BT (Behavior Tree)**：游戏策划手写的行为树 AI，水平对标**黄金段位**（人类入门线），
  是"打败 BT"这个里程碑的参照物；
- **不同强度的已训练模型**：官方还提供多个 level 的模型，方便区分你的 AI 到底有多强。

**为什么不能只看胜率？** 论文用一个漂亮的例子说明（H.4）：模型 B 是专门打"冻结的模型 A"训练出来的，
B **100% 打赢 A**，但 B 打 BT 的胜率反而不如 A——因为 B **过拟合了 A 的弱点**。
这就是**非传递性 (non-transitivity)**：`B>A、A>BT` 不代表 `B>BT`。

> 因此论文推荐用 **Elo 分数**（像国际象棋排名那样，综合与所有对手的胜负来定级）而非单一胜率。
> 上例中 A 的 Elo（2372）远高于 B（1186），更能反映真实实力。
>
> 📌 教学提醒：这也是**自对弈**的一个坑——只和"某一个对手"练，容易练成"专治一人"的偏科生。
> 想练出通用强者，就要和**多样化、不断更新的对手池**对打（呼应 6.9 的多任务思想）。

---

## ✅ 小结

- 开悟把《王者荣耀》做成了**离线、合规、可复现**的强化学习环境，接口与第四部分的 Gym 同构；
- **状态**是 **491 维**战场向量（5 大组件），**动作**是带合法掩码的**三元组复合动作**，**奖励**是分 5 类、可塑形的多维信号；
- 论文关键超参：`γ=0.997`（半衰期 46 秒）、Adam `1e-4`、PPO dual-clip；决策频率 133ms；
- 最小交互骨架 = `reset → (在合法动作里选) → step → 直到 done`；把"随机选"换成策略网络即开始训练；
- 高维复合动作最适合 **PPO + 自对弈**（论文中 PPO > DQN），官方有 baseline 可直接起步；
- **瓶颈在 CPU 采样**：纯 CPU 足以跑通与做小规模实验，上千核约 1 小时打败黄金级 BT；
- 真正的难题是**泛化**（20×20=400 任务）与**科学评估**（用 Elo 而非单一胜率，警惕非传递性）。

## 📝 练习

1. 跑通 [`test_1v1_random.py`](kaiwu_env/test_1v1_random.py)，统计一局的总帧数与随机策略的胜负。
2. 打印 `state[0]["observation"].shape`（应为 491）与 `env.action_space()`，对照 6.3/6.4 的表说说每一维/每个子动作头是什么。
3. 修改 `config.json`：把 `reward_tower_hp_point` 调大、`reward_dead` 惩罚加重，重跑并观察行为差异（可看回放 `.abs`）。
4. 阅读官方 [hok_env](https://github.com/tencent-ailab/hok_env) 的 1v1 PPO baseline，画出它的"采样→训练"数据流，指出哪一步能在 CPU 上并行。
5. 思考题：为什么 MOBA 要用**合法动作掩码**而不是让智能体"自己学会不放非法技能"？（提示：论文 H.2 的消融——没有掩码会怎样？）
6. **论文题**：解释"对手泛化"与"目标泛化"的区别，并说说为什么"换英雄≈换一个游戏"。
7. **论文题**：模型 B 能 100% 打赢 A，但 B 打 BT 不如 A。用"过拟合对手弱点 / 非传递性 / Elo"三个词解释这个现象，并说说它对**自对弈训练**的启示。

---

📗 参考：
- 开悟平台：<https://aiarena.tencent.com/>
- 论文《Honor of Kings Arena》：<https://arxiv.org/abs/2209.08483>
- 官方 SDK / baseline：<https://github.com/tencent-ailab/hok_env>
- 1v1 参考实现：<https://github.com/Unakar/AI_Game_KingGlory>

⬅️ 上一部分：[第五部分 · 各式各样的强化学习算法](part5-algorithms.md)
🏠 返回：[强化学习课程首页](README.md)
