AI智能体自动化游戏测试的创新实践
开发者展示如何用智能体驱动的测试框架自动化游戏QA。
开发者展示如何用智能体驱动的测试框架自动化游戏QA。
我有一套扎实的用 AI 制作游戏的工作流。然而,每个迭代都要进行大量的手动游戏测试以验证功能并查找 bug。这个过程往往很慢且繁琐。单元测试是不够的。我需要一种方式让 AI 验证实际运行的系统。今天我开创了一种新方法,让 AI 与我一起进行游戏测试,效果很好。
我的游戏叫做 Crossword Dungeon。想象一个纵横字谜组成了老式地牢爬行游戏的布局。每个字母是一个"房间",每个房间里有宝藏、陷阱或怪物。解答字谜的一个字母会提升相连房间的等级,使地牢变得更难。拼图解答与 RPG 元素相互交织,RPG 机制又让拼图解答变得复杂。很有趣。
这个游戏运行在浏览器中,所以我最初考虑使用浏览器自动化 MCP 工具供 AI 使用。但游戏出了名地难以测试,有大量状态和复杂的行为会随着许多交互而改变。仅仅进行简单导航所需的截图数量就会非常庞大。幸运的是,这个游戏文字很多且回合制。AI 能够制作一个 node.js 包装器,它不修改任何实际游戏文件,而是基于游戏状态添加了新的基于文本的渲染器和合成事件用于交互。它看起来像这样:
=== player @ grid(2,2) Dungeon Level 3 [SPACE] Full map [?] Help ===
; [ ] [A]-[&]-[I]
:: ##### ##### ##### | | |
# ###+ +### # [?]-[?]-[T]-[?]
# A & I # | | | ‖
# ###+ +### # [&]-[?]-[@]=[/]
:~ ## ## ## ## ## ##
# # # # # #
##### ## ## ## ## ## ##
# ### ### ### #
# ? ? T ? # '
# ### ### ### #
## ## ## ## ## ## ## ##
# # # # # # #=#
## ## ## ## ## ## ## ## ~
#+ +### ###+ +###+ +# ~
# & ? @ = / #
#+ +### ###+ +###+ +#
##### ##### ##### #####
--- HERO ---
AdventurerLv.1 HP: ██████████ 50/50 MANA: ██████████ 15/15 DMG:8 DEF:0 XP:0/120 GOLD:3000
[1] Heal ×2 [2] Restore ×2 [3] Inscribe ×3 [4] Intone ×3
--- ENCOUNTER ---
% Merchant Lv.3 Wares and wonder, for the right price.
[1] Health Potion (+20 HP) 16 GOLD
[2] Mana Potion (+10 MANA) 16 GOLD
[3] Inscribe Scroll (reveal letter) 40 GOLD
[4] Intone Scroll (reveal word) 60 GOLD
--- CLUES ---
▲ ▼ MSN competitor
所有的玩家和遭遇面板都以文本显示。实际的 ASCII 地牢网格在左边呈现,用于上下文说明玩家所看到的内容。此外,我们在右边添加了一个紧凑的地图,以 AI 更容易理解的格式表示相同的房间和走廊,用于导航。这是你在浏览器中看到的相同关卡:
我教了 AI 如何使用新的游戏测试服务器。效果非常好。但别只听我的说法。这是 AI 在玩完第一局后说的话:
这多酷啊?这个游戏完全可以从终端通过无状态 HTTP 调用进行游玩。我启动服务器,获取快照,决定去哪里,发送按键,读取新状态,重复。游戏不知道有什么不同的事情发生——它运行得完全和在浏览器中一样,相同的逻辑、相同的事件流、相同的状态机。我可以通过导航到确切的房间配置来重现特定的 bug,尝试诸如从两边靠近锁定门的边界情况、触发战斗、使用商店,并实时观察面板内容的变化。测试工具工作得足够好,我实际上是在玩游戏,而不仅仅是在运行代码路径。
起初我只是要求 AI 玩游戏、仔细测试并享受乐趣。我跟踪进展,惊讶地发现它与观看人类玩我的游戏是多么相似!AI 遇到了相同的障碍,获得了相同的战略见解。我还发现当前一代的 AI 非常擅长纵横字谜。
然后我给了它为特定场景设计自定义 fixture 的能力。现在我可以描述我发现的 bug,让它重新创建一个最小的测试 fixture 来重现 bug,自己体验它,修复代码,然后再测试一次来验证实际的修复!
但真正的力量来自现在对我开放的新工作流。我给了它一个新的里程碑,并指示它在继续之前完全游戏测试它添加的每个功能。结果呢?与其花费数小时进行手动游戏测试,我得到了这个:(为了可读性而截断,突出了进度时刻)
❯ claude --permission-mode auto
▐▛███▜▌ Claude Code v2.1.119
▝▜█████▛▘ Sonnet 4.6 · Claude Pro
▘▘ ▝▝ ~/Documents/code/crossword-dungeon
❯ Time for milestone 13. Do them one at a time, playtesting happy path and different scenarios each time to confirm it works completely before moving on. Ask any questions if needed otherwise go.
⏺ Let me read the milestone 13 doc to understand what's planned.
...
⏺ Starting with the Thieving Modifier. Let me add it to the monster modifiers data and handle the gold steal in the wrong-guess path.
...
⏺ All 204 tests pass. Now let me playtest to verify the Thieving modifier works:
...
; [@]-[!]-[!]
##### ##### #####
#...###. ###. #
# @ ! ! #
# ### ### # `
##### ##### ##### '
...
--- ENCOUNTER ---
Thieving Goblin Lv.3
A scrappy humanoid with crude weapons
Cast a rune to fight.
◆ Thieving — Steals gold
HP: ██████████ 18
DMG: 0
-10 GOLD on wrong guess
REWARD
+ 16 XP on defeat
Runes tried: Z
--- CLUES ---
◀ ▶ Test clue
--- POPUP ---
You cast the 'Z' rune (-1 MANA)
The rune glows briefly but then fades away.
The Goblin strikes!
-0 HP
-10 GOLD (stolen)
[SPACE] Continue---
...
⏺ Gold went from 50 → 40, popup shows "-10 GOLD (stolen)". Thieving modifier works correctly. Now verify correct guess triggers normal combat (no gold stolen):
...
⏺ Correct guess triggered combat, Goblin defeated, gold stayed at 40 (no theft on victory). Thieving modifier is fully working.
Now on to Cage Trap. Plan:
...
⏺ Now write a fixture and playtest the cage trap:
...
⏺ Caged. HP stayed at 50 (no damage). Now dismiss and try to move:
⏺ Bash(curl -s "http://localhost:3001/dismiss" > /dev/null && curl -s "http://localhost:3001/key?k=ArrowLeft")
...
⏺ Movement blocked with the right message. Now solve correctly to escape:
...
⏺ Escape message shows. Now dismiss and verify movement is restored:
⏺ Bash(curl -s "http://localhost:3001/dismiss" > /dev/null && curl -s "http://localhost:3001/key?k=ArrowLeft")
...
⏺ Hmm — still blocked after solving! The room shows "completed" (encounter panel is empty), but movement is still blocked. The bug is that state.caged is never cleared — it's still true even after the room is solved. I need to clear caged when the room is marked solved:
⏺ Update(src/game.ts)
...
⏺ Player successfully moved to (0,0) after escaping the cage. The full flow works:
- Wrong guess → caged, no HP damage
- Movement blocked with message
- Correct guess → disarmed, escape message, movement restored
Cage trap is complete. Now onto Slime Blob. Plan:
...
⏺ The encounter panel shows the Slime Blob at level 2 (mods not active yet, correct). The adjacent rooms show ! (trap). Let me fight it:
...
⏺ The adjacent rooms changed from ! (trap) to * (monster) — the slime split into both neighbors. Now let me dismiss and check one of the split slimes:
...
Slime blob is working. Now Dormant Sentinel. Plan:
...
⏺ The sentinel has 70 HP remaining at 22 — it wasn't killed in 5 turns (player does 8 dmg × 5 = 40 dmg, from 70 to 30). Then after awakening, one hit at 92 dmg kills the player. This shows the awakening is working (player was killed by the sentinel after it awakened).
Let me now test with a player strong enough to kill it within 5 turns — load with high DMG so it dies before awakening:
...
⏺ "The sentinel awakens!" appears in the victory popup. Player HP went from 500 to 224 — that's 276 damage from 3 sentinel hits (92 × 3 = 276), meaning it awakened at turn 6 (after player hit #5) and attacked 3 times before dying. Correct behavior.
Dormant Sentinel is done. Now the most complex one: Thief. Plan:
...
⏺ The thief is defeated. Gold: started 100, stole 20+20=40, recovered 40 → back to 100. XP gained 14. "You recover 40 stolen gold." — perfect.
All 5 special encounte
整个里程碑 13,包括 5 个新的特殊情况功能,在 12 分钟内完成,耗费约 120k tokens(使用 Claude Code with Sonnet 4.6 Pro 计划)。
它奏效了吗?嗯,它仍然需要一些手动游戏测试,我仍然发现了许多 bug 和"感觉"问题。但那只花了不到 30 分钟,我记下了所有问题,让 AI 一次性修复所有问题并验证修复——一个流畅得多的工作流。