曾经有一位武馆馆主,想检验弟子新创的拳法有没有破绽。他没有请外面的高手,而是让弟子左手打右手,自己和自己过招。第一轮,左手发现右手的肘部总是慢半拍;于是右手苦练一晚,第二天左手又发现右手下盘不稳……如此往复,拳法在“发现破绽—弥补破绽”的循环里越来越严密。这个方法,就是AI测试游戏时的“左右互搏”——让一个AI不断与自己的副本对战,自动挖出设计者没看到的策略漏洞、数值失衡和隐藏连招。它不需要几万条人工标注数据,只需要一套清晰的“胜负规则”和一枚“奖励硬币”,就能在格斗场或卡牌桌上自己长出高手策略。本文就用这个“左右互搏”的比喻,带你一步步看懂AI如何训练自己、如何给它发奖励,又如何用胜率和多样性做平衡性测试。

岗位关联

掌握强化学习Self-play训练与奖励设计,你就能用AI自动发现游戏中的策略漏洞和数值失衡,成为游戏开发团队里既懂玩法又懂智能体的稀缺工程师。

目录
  1. 可视化演示
  2. 强化学习基础
  3. Self-play架构
  4. 奖励设计
  5. 平衡性测试
  6. 格斗游戏测试
  7. 卡牌游戏测试
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示Self-play自我对弈PPO训练架构、胜负行为惩罚三维度奖励函数设计、胜率Elo收敛训练曲线及RL Agent自动发现策略漏洞平衡性测试,含粒子场背景动画。

本节配有交互动效(进入视口后自动加载,约 3KB)。

强化学习基础

武馆学徒阿强第一次上擂台,完全不知道该如何出拳。他的师傅站在旁边,每次他出拳打中沙袋,就给他一粒糖;被打倒,就记一个叉。阿强慢慢明白了:在这个沙袋面前,出左拳比出右拳更划算。这里的阿强就是智能体,沙袋和擂台就是环境,师傅的糖和叉就是奖励,而“眼前沙袋的位置”就是状态。

强化学习要解决的问题,正是让一个智能体在没有说明书的情况下,通过试错学到“什么状态该做什么动作”。它不断与环境交互,获得奖励信号,最终学会让累计奖励最大化的策略。为了让智能体记住长期价值,我们引入一个“价值账本”——Q函数。Q(s,a) 记录在状态 s 下执行动作 a,之后一直按最优策略走下去,能拿到的期望总奖励。

Q-learning 是更新这本账本的经典方法。它用当前得到的奖励 r,加上下一步状态的最优估计,来修正对当前动作的估计:

阿强每天训练后都会翻开小本子,把每一个“沙袋位置-出拳选择”的价值都修正一遍。几万次之后,他不再需要想,手自己就知道该往哪打。这就是强化学习训练游戏AI的起点。

Q(s,a)←Q(s,a)+α[r+γmax⁡a′Q(s′,a′)−Q(s,a)]\displaystyle Q(s,a) \leftarrow Q(s,a) + \alpha \left[ r + \gamma \max_{a'} Q(s',a') - Q(s,a) \right]
这条公式的意思是:把旧估计向“实际收到的奖励+未来最优估计”挪一小步,α 是挪动步长。
  1. 当前记的账小本子上记录的“在状态 s 做动作 a 的长期价值”。
    Q(s,a)Q(s,a)
  2. 本次赚到的糖执行动作后环境立刻给的真实奖励。
    rr
  3. 未来最好的预期转移到的下一个状态 s′ 里,所有动作中价值最高的那个,再乘一个折扣系数 γ,表示未来的糖不如现在值钱。
    γmax⁡a′Q(s′,a′)\gamma \max_{a'} Q(s',a')
  4. 学习步长α 控制这次要更新多少,太大容易学歪,太小学得太慢。
    α[r+γmax⁡a′Q(s′,a′)−Q(s,a)]\alpha \left[ r + \gamma \max_{a'} Q(s',a') - Q(s,a) \right]

Self-play架构

周伯通闭关时练左右互搏,左手和右手打架。但他后来发现,如果只用同一个姿势打,左右手会形成默契,谁也不用变招。于是他每次收功都会把今天的“右手”备份存档,明天让新的“左手”轮流挑战过去的“右手们”。这份存档,就是AI世界的对手库。

Self-play训练架构的核心,是让智能体与自己的副本而不是固定脚本对打。当前策略 π_current 不断产生新打法,同时要面对一组历史策略组成的对手库。这样它既不会被旧套路欺负,也不会忘了怎么应对各种风格。为了评估进步,还可以定期举办锦标赛:让当前策略和对手库里的所有版本各打几百局,看胜率是否有真实提升。

一个常用的混合对手策略,是按概率在当前策略和历史平均策略之间切换:

就这样,AI在“新招—旧招—更旧的招”之间反复横跳,无论对方用什么套路,它都能找出破绽。这也是为什么Self-play能自动发现游戏策略漏洞,而不需要设计师预先告诉它漏洞在哪。

πmix=(1−ε)πcurrent+επavg\displaystyle \pi_{\mathrm{mix}} = (1-\varepsilon)\pi_{\mathrm{current}} + \varepsilon \pi_{\mathrm{avg}}
当前策略负责探索新招式,平均策略负责稳定地“回忆”旧招,ε 是切换到回忆模式的比例。
  1. 当前策略今天闭关练出来的新拳路,包含最新发现的连招。
    πcurrent\pi_{\mathrm{current}}
  2. 历史平均策略把过去所有版本的拳路平均起来,代表一个“老练但没有极端的对手”。
    πavg\pi_{\mathrm{avg}}
  3. 混合比例ε 越小,越专注用新招数战斗;ε 越大,越常和“老自己”过招。
    (1−ε)πcurrent+επavg(1-\varepsilon)\pi_{\mathrm{current}} + \varepsilon \pi_{\mathrm{avg}}

奖励设计

师傅给阿强发糖也是门学问。如果只在KO对手时给一颗大糖,阿强前期完全不知道怎么做,只能瞎试,这叫稀疏奖励;如果每出一拳都发糖,他可能学会刷出拳数来骗糖,这叫奖励滥用。聪明的师傅会在关键节点给即时反馈,又不改变最终目标。

强化学习中的奖励设计决定了AI学得快不快、学得好不好。为了让AI在中途有方向又不偏离终局目标,可以用一种“势函数塑形”的方法。它给每一步增加一项额外的“方向感”奖励:当新状态比旧状态更接近目标时,多给一点糖;退步时就扣一点。

阿强每走一步,师傅会悄悄念一句“这一步走得对”或“别后退”,最后KO时还是照样发大糖。这样一来,阿强既不迷茫,又不会为了骗小糖而丧失大局观。奖励设计,就是这位师傅恰到好处的耳语。

R′(s,a,s′)=r(s,a,s′)+Φ(s′)−Φ(s)\displaystyle R'(s,a,s') = r(s,a,s') + \Phi(s') - \Phi(s)
原始奖励 r 加上进入新状态的势函数变化量,用来引导AI朝“局面更好”的方向走,但不改变最优策略。
  1. 原始奖励环境原本给出的糖,比如这次攻击是否掉血。
    r(s,a,s′)r(s,a,s')
  2. 新状态的势对新状态好坏的打分,比如距离胜利还有多远。
    Φ(s′)\Phi(s')
  3. 旧状态的势动作之前状态的打分,两者相减得到“变得更好了多少”。
    Φ(s′)−Φ(s)\Phi(s') - \Phi(s)
  4. 塑形后的奖励把这份变化量加进原始奖励,就是喂给学习的新糖。
    R′(s,a,s′)R'(s,a,s')

平衡性测试

武林大会结束,裁判要判断各门派强弱。他先看两个门派对战的胜率,再看有多少人愿意学这两个门派。如果一个门派胜率高达80%,大家全挤过去拜师,那武学显然失衡。游戏平衡性测试也相似。

用Self-play训练的AI可以在几小时内模拟海量对局,这就是测试的“武林大会”。最基础的指标是双方胜率,理想情况下接近50%。但胜率不能只算一场,还要统计大量随机策略和对局组合,避免偶然性。

只盯胜率还不够。如果两个AI只会用同一招对打,胜率也能稳定在50%,但游戏变得枯燥。平衡性测试还要看“使用率”和“策略多样性”——就像武林大会除了看胜负,还会统计有多少门派愿意上场、每套拳法有多少变化。AI就像一面照妖镜,把数值配平的边边角角都照出来。

B=∣pA−0.5∣\displaystyle B = \left| p_A - 0.5 \right|
B 越接近0,说明A和B的整体强度越接近;B越接近0.5,说明一方碾压另一方。
  1. A方胜率所有对局里A赢下的比例,接近1表示快赢,接近0表示快输。
    pAp_A
  2. 理想平衡点双方均势,胜率应为一半。
    0.50.5
  3. 偏离程度取绝对值,只关心差多远,正负无所谓。
    ∣pA−0.5∣\left| p_A - 0.5 \right|

格斗游戏测试

左右互搏最壮观的应用是格斗游戏。传统测试人员手动测试每个招式,很难覆盖两个角色成千上万个出招组合。AI则可以一天打十万场,像两个不知疲倦的拳手,把对方每一个帧的破绽都逼出来。

在格斗游戏中,Self-play Agent 会自己摸索出连招:先用轻拳试探,再乘胜追击重拳。测试者从录像和帧数据中,不仅能发现AI发明的连招,还能发现设计者没想到的“金矿”——比如某个角色在特定帧可以打断防御。计算连招的期望伤害时,要把每段命中的概率乘上伤害再累加:

AI发现连招后,测试工程师把帧数据放到回放里,就像武馆师傅看弟子对打的录像,总结“这一拳慢了5帧所以被破防”。格斗游戏里的每一帧都是攻防博弈,Self-play就是那个闲不下来的陪练,把隐藏帧漏洞一一翻出来。

Dcombo=∑i=1npi×di\displaystyle D_{\mathrm{combo}} = \sum_{i=1}^{n} p_i \times d_i
连招总期望伤害等于每一段伤害乘以该段能打中的概率之和。
  1. 每一段招式连招第 i 下,比如轻拳、重拳、升龙。
    ii
  2. 命中概率这一下能不能落在对方身上的概率,防御可以挡住。
    pip_i
  3. 单段伤害这一下打实了的伤害数值。
    did_i
  4. 累加求和把 n 段加起来,就是整套连招的平均收益。
    ∑i=1npi×di\sum_{i=1}^{n} p_i \times d_i

卡牌游戏测试

卡牌游戏像一场大型的“左右互搏棋盘”。每个玩家带一套卡组上场,环境会随着版本更新不断演化。AI的自我对弈可以模拟一整季天梯:让不同卡组互相碰撞,看哪些卡牌被过度使用,哪些卡组趋于消失。

卡牌游戏的随机性比格斗更大——抽牌顺序、起手手牌都会影响胜负。Self-play Agent 通过大量对局统计某张卡的关键程度。简单的方法是计算“单卡参与胜率”:这张卡在手牌时赢的局数与它出现在手牌的总局数之比。

左右互搏让卡牌环境动起来:一张卡被AI发现太强,大家就换对策卡,然后环境又变。测试工程师看着这副“演化图”,就能在玩家流失前调整数值。这就像周伯通和无数个自己的影子在牌桌上打了一整个赛季,把环境火山一早就摸清了。

S(c)=Nwin(c)Ntotal(c)\displaystyle S(c) = \frac{N_{\mathrm{win}}(c)}{N_{\mathrm{total}}(c)}
某张卡被抽到时获胜的比例,衡量这张卡对胜负的贡献。
  1. 分子当局玩家抽到过这张卡并且获胜的场次。
    Nwin(c)N_{\mathrm{win}}(c)
  2. 分母所有抽到过这张卡的局数。
    Ntotal(c)N_{\mathrm{total}}(c)
  3. 比例比值越高说明这张卡在手时胜率越高,强度可能超标。
    Nwin(c)Ntotal(c)\frac{N_{\mathrm{win}}(c)}{N_{\mathrm{total}}(c)}

AI 应用场景

格斗游戏连招漏洞挖掘

阶段:测试执行与策略挖掘

解决的问题:传统人工测试逐一搓招,很难覆盖成千上万种出招组合,尤其是只有几帧差异的目押连招和防御穿透漏洞。RL Agent 通过Self-play自动对战,在大量对局回放中定位异常帧,把人工测试从“碰运气”变成“广撒网”。

具体案例:某格斗项目部署Self-play Agent,每日模拟10万局,发现角色“寸拳”在特定帧可以穿过防御判定。修复该帧数据后,该角色对主流角色的胜率从63%回落至51%。

卡牌游戏版本环境平衡监控

阶段:数据生成与平衡分析

解决的问题:版本更新后主流卡组往往一周后才被玩家摸清,官方只能等天梯数据回传。Self-play Agent 可以在数小时内模拟新环境下的卡组碰撞,提前暴露某张卡过强或某流派绝迹的风险。

具体案例:某卡牌游戏中“冰霜新星”上线后使用率异常攀升,Agent 用新构筑击败旧主流套牌,统计胜率超过55%。测试组据此回调数值,避免了版本生态固化。

玩法上限探索

阶段:玩法空间探索

解决的问题:人工测试偏向已知技巧,很难跳出设计者的思维定式。RL Agent 在自我对弈中会尝试极端的策略组合,经常发现人类意想不到的连段、卡牌配合或操作技巧。

具体案例:在某动作游戏中,Agent 自动组合出“3帧目押轻拳接重拳”的连段,测试团队据此评估是否需要保留该操作深度,甚至将其加入新手引导。

Python 演示

python56 行
import random ACTIONS = ['攻击', '防御', '蓄力'] def referee(x, y):    # 0攻击 1防御 2蓄力; 攻击胜蓄力, 蓄力胜防御, 防御胜攻击    if x == y:        return 0    if (x - y) % 3 == 1:        return 1    return -1 def normalize(p):    s = sum(p)    return [v / s for v in p] def sample(p):    r = random.random()    acc = 0    for i, v in enumerate(p):        acc += v        if r < acc:            return i    return len(p) - 1 def battle(p1, p2, rounds=50):    score = 0    for _ in range(rounds):        a1 = sample(p1)        a2 = sample(p2)        score += referee(a1, a2)    return score / rounds def train(epochs=100, lr=0.1):    cur = [1/3, 1/3, 1/3]    avg = [1/3, 1/3, 1/3]    wlr = []    for _ in range(epochs):        perf = battle(cur, avg, 50)        if perf < 0:            for j in range(3):                cur[j] += lr * (avg[j] - cur[j])        else:            for j in range(3):                avg[j] += lr * (cur[j] - avg[j])        cur = normalize(cur)        avg = normalize(avg)        wlr.append(battle(cur, avg, 100))    return cur, avg, wlr if __name__ == '__main__':    random.seed(42)    cur, avg, wlr = train(epochs=200, lr=0.1)    print('当前策略:', dict(zip(ACTIONS, [round(p, 3) for p in cur])))    print('对手库策略:', dict(zip(ACTIONS, [round(p, 3) for p in avg])))    print('最终互评胜率:', round(wlr[-1], 3))
目的:用简化版的Self-play训练,让“当前策略”和“历史平均策略”互相博弈,像左右手互搏一样自动调整招数比例,最终输出双方策略和互评胜率。
  1. def referee(x, y):裁判函数,判定两个动作的胜负关系,这是游戏环境的最小核心。
  2. score += referee(a1, a2)累计一局中多次对打的净胜分,用来评估策略强弱。
  3. perf = battle(cur, avg, 50)当前策略和历史策略先打50轮,看谁占上风。
  4. cur[j] += lr * (avg[j] - cur[j])如果当前策略打不过对手,就向对手的策略方向挪一步,模拟“学习”。
  5. avg[j] += lr * (cur[j] - avg[j])如果当前策略赢了,就让历史策略库吸收新打法,保持对手库不过时。
  6. cur = normalize(cur)将概率向量归一化,保证所有招数概率相加为1。
  7. random.seed(42)固定随机种子,让训练结果可复现,方便调试和回归测试。

常见误区

误区AI自己和自己打,直接拿最新版本当对手就行,这样进步最快,还能省内存。

正解只用最新版本会让AI很快“记住”当前招数,陷入石头剪刀布式的循环:今天破解了左拳,明天又被右拳破解,忘了怎么应对旧招。正确做法是维护一个历史对手库,让当前策略轮流挑战过去的版本,再配合锦标赛评估,才能真正进步。就像周伯通把所有过去的“右手”都请回来当陪练。

为何常见:直觉上最强的对手最有训练价值,但忽略了策略遗忘和循环问题。

误区每打中一拳都加分,AI会更早学会连招,所以奖励密集体现在每步都发糖。

正解过密的奖励会诱导AI“奖励黑客”——它会刷出拳次数、卡墙角等无效行为来骗分,最后学到的并不是真正的胜利策略。奖励设计需要稀疏与密集结合,势函数塑形只提供方向感,不能改变最优策略。这就像师傅每步都喂糖,弟子就会盯着糖而不是盯着怎么打赢。

为何常见:人们容易把奖励类比成考试分数,以为分数越多激励越强,忽视了游戏目标可能被“刷分”破坏。

误区只要两个AI互相打,胜率接近50%,角色就是平衡的,可以直接上线。

正解胜率只是底线指标。如果两个AI从头到尾只用同一种打法,胜率也能恰好50%,但玩家会觉得单调。真正的平衡要看策略多样性——多个角色、多套卡组都有登场空间,且都有克制途径。就像武林大会不仅看胜负,还要看各门派有没有人愿意上场。

为何常见:数值测试者习惯用单一量化指标做判断,忽略了游戏体验是多维的。

误区AI一天能打几百万局,以后游戏测试只要跑AI就行,人工测试可以取消。

正解AI擅长发现数值失衡和策略漏洞,但它无法感受“手感”“乐趣”“挫败感”等主观体验。强化学习测试应该作为人工测试的放大器:AI负责挖掘数据和异常,人工负责判断是不是合理设计、要不要保留。两者结合才是成熟的测试流程。

为何常见:看到AI强大的自动化和高产出的胜率报表,容易高估它的能力边界。

面试题

Q1. Self-play(自我对弈)训练游戏AI的核心思想是什么?

单选题 · easy

  • A.让AI与自己的新版本对打← 正确答案
  • B.让AI与固定脚本机器人对打
  • C.让AI学习大量人类录像
  • D.让AI进行暴力穷举搜索

答案:A。Self-play 的核心就是AI与自身副本左右互搏,让新旧策略互相攻防。固定脚本会被AI背下来,人类录像无法覆盖新策略,暴力搜索则没有学习过程。只有不断“自己打自己”才能自动逼出盲区。

Q2. 在Self-play训练中加入“历史对手库”的主要目的是什么?

单选题 · mid

  • A.防止AI陷入策略循环← 正确答案
  • B.降低训练所需的计算量
  • C.减少游戏内存的占用
  • D.提高画面渲染的帧率

答案:A。历史对手库存储过去版本的策略,让当前AI轮流挑战旧版,避免只针对最新策略导致招数循环。它不会减少计算量,也不关心内存和渲染。这就像周伯通保存每一天的“右手招式”,防止左右手打熟了变成互相放水。

Q3. 势函数塑形奖励增加的额外项是什么?

单选题 · mid

  • A.新状态势函数减旧状态势函数← 正确答案
  • B.新状态势函数乘旧状态势函数
  • C.新状态势函数取对数后的值
  • D.新状态势函数取指数后的值

答案:A。势函数塑形在原始奖励上增加 Φ(s′) − Φ(s),表示“变得更接近目标多少”。乘法、对数、指数都不具备这种可加性的方向引导,也更容易扭曲最优策略。这就像师傅判断“这一步比上一步更靠近KO”才给糖。

Q4. 平衡性测试中,为什么要同时看“策略多样性”?

单选题 · mid

  • A.避免只靠一套打法造成的假平衡← 正确答案
  • B.为了让所有角色胜率绝对相等
  • C.为了确保AI每局都能获胜
  • D.为了减少测试对局的盘数

答案:A。如果双方只用同一招,AI互殴也可能打出50%胜率,但这是假平衡。策略多样性要求多个角色、多套卡组都能上场且有克制关系。胜率绝对相等既不现实也不需要;AI每局都赢反而说明异常。减少盘数更不是多样性的目的。

Q5. 格斗游戏Self-play测试发现“帧漏洞”主要依赖什么?

单选题 · hard

  • A.大量对局录像与帧数据分析← 正确答案
  • B.随机生成角色皮肤颜色
  • C.人工阅读游戏源代码
  • D.直接修改游戏的物理引擎

答案:A。帧漏洞藏在出招与受击判定的几帧差异里,只有通过大量Self-play对局让AI主动试探,再结合帧数据回放才能定位。皮肤颜色、阅读源码、修改引擎都不是测试层该做的事。这就像从对打录像里一帧一帧找“哪一拳迟了5帧”。

Q6. 卡牌游戏版本更新后某卡使用率异常,正确的测试做法是什么?

单选题 · hard

  • A.用AI模拟新环境重估主流卡组← 正确答案
  • B.立刻删除该卡牌所有效果
  • C.把所有卡组的胜率强制设为零
  • D.停止更新版本并重新开服

答案:A。正确的做法是先用Self-play Agent模拟新版本环境,重估主流卡组胜率和环境演化,再决定是否调整数值。直接删卡、清零胜率、停止更新都是粗暴修复,会破坏游戏生态。AI模拟能给出数据支撑,让调整有据可依。

Q7. 以下哪些属于强化学习训练AI的基本要素?

多选题 · easy

  • A.一个做决策的智能体← 正确答案
  • B.提供反馈的交互环境← 正确答案
  • C.来自环境的奖励信号← 正确答案
  • D.用于存储状态的数据库

答案:ABC。强化学习必须有智能体、环境和奖励信号三要素:智能体做动作,环境返回新状态和奖励,奖励指导学习方向。数据库索引是工程工具,不是强化学习的组成部分。就像练拳必须有弟子、擂台和师傅的糖,缺一样都学不成。

Q8. 以下哪些手段可以防止Self-play训练中的策略循环?

多选题 · hard

  • A.使用多种历史对手的混合池← 正确答案
  • B.定期用锦标赛评估胜率← 正确答案
  • C.引入随机初始化的新策略← 正确答案
  • D.将每局奖励值全部归零

答案:ABC。历史对手混合池可以让策略面对多样化风格,锦标赛评估能分辨是真进步还是只针对某个对手,随机初始化新策略能跳出局部循环。奖励归零会完全失去学习信号,策略只能原地踏步,无法防止循环。

Q9. 关于强化学习奖励设计,下列说法正确的是?

多选题 · mid

  • A.密集奖励能加快前期探索← 正确答案
  • B.势函数塑形不改变最优策略← 正确答案
  • C.稀疏奖励通常难以直接学习← 正确答案
  • D.奖励数值越大训练效果越好

答案:ABC。密集奖励在前期提供明确反馈,能加速探索;势函数塑形只改变过程向导,不改变最优策略;稀疏奖励目标太远,智能体往往需要很长时间才能“摸到”奖励。奖励数值过大会导致梯度震荡甚至不收敛,所以“越大越好”是错的。

Q10. 卡牌游戏Self-play测试的平衡性评估指标包括哪些?

多选题 · mid

  • A.卡组和角色的胜率← 正确答案
  • B.卡牌和流派的使用率← 正确答案
  • C.卡组之间的克制关系← 正确答案
  • D.卡牌的闪卡稀有度

答案:ABC。平衡性评估看胜率、使用率和克制关系:胜率反映强度,使用率反映受欢迎度,克制关系反映环境多样性。闪卡稀有度只是外观收藏属性,与数值平衡无关,所以不纳入评估。