GPT-6 Astra 借助 agent-wow 首次游玩《魔兽世界》
英文标题:🔶 GPT-6 Astra plays World of Warcraft for the first time with agent-wow
开发者利用 agent-wow 让运行在 Codex 中的 GPT-6 Astra(xhigh)创建兽人角色,并完成《魔兽世界》新手区域的全部任务。该 Agent 在没有专门接受游戏训练的前提下,以 40 分钟、0 死亡、极少波折的成绩完成任务,并通过解析 AzerothCore 数据和 WoW 网络协议进行规划与操作。agent-wow 不依赖屏幕视觉、键鼠模拟或客户端破解,而是为 Agent 提供直接通过游戏网络协议与私有 AzerothCore 服务器交互的平台。该实验表明,前沿模型能够自行构建路径规划和协议层工具,并为评估长程策略、即时战术及多人协作型 Agent 能力提供了一个复杂的模拟环境。
中文
GPT-6 Astra 借助 agent-wow 首次游玩《魔兽世界》
2026 年 10 月 02 日
最近,我一直对由 LLM 驱动的 Agent 玩视频游戏这一概念深感着迷。它们不仅出人意料地好看,还能为我们提供宝贵洞见:当前沿模型被置于一个自己未经过显式训练的模拟世界中时,它们究竟能表现得多好。
机器人玩视频游戏,尤其是玩《魔兽世界》(WoW),并不是什么新鲜事。与基于启发式规则的机器人相比,LLM Agent 的不同之处(也更有趣之处)在于,它从未被专门训练来玩这款游戏。相反,它使用自身具备的推理能力,对环境采取行动,并像真实玩家一样完成任务。
这一领域已经有一些有趣的开源项目,例如 Mindcraft 和 Factorio Learning Environment。我构建了 agent-wow,将这一概念扩展到《魔兽世界》,以观察前沿模型究竟有多大能力玩好这款游戏。最终目标是让整个服务器充满 AI Agent,看看它们能否以英雄难度通关冰冠堡垒。即使失败,观察它们究竟能走多远也仍将富有洞见(而且很有趣)。
作为一个简单的起点,我向使用 GPT-6 Astra(xhigh)的 Codex 给出了以下提示:
create an orc character and complete all quests in the starting zone
起初我怀疑这是否能够成功。即便成功,我也预计它需要数小时才能完成,过程中还会走入死胡同。但事实证明,它在 40 分钟内轻松完成了任务,全程 0 死亡,且几乎没有遇到复杂情况。该会话的完整游戏录像也可在此观看:视频:GPT-6 Astra 使用 agent-wow 游玩《魔兽世界》的完整录像。
为什么是《魔兽世界》?
除了它是我最喜欢的游戏之一(尤其是 Classic 至 WotLK 的时代)以外,它还拥有一些出色的机制,使其成为评估 AI 的理想模拟世界。随着游戏进程推进,它在长期策略与短期战术之间实现了良好的平衡。
当你达到等级上限时,要让角色为终局内容做好准备,需要进行大量复杂的规划和执行。
- 完成所有前置任务
- 获取足以应对终局 Boss 战的装备
- 组建具备合适职业构成的公会
这些要求中的每一项都可以继续拆分为更复杂的任务。例如,某些最优装备栏位可能需要通过制造获得,而制造又需要特定资源和技能。角色可以自己反复刷取,也可以用金币购买。两者会通向相同结果,但采用的是截然不同的方法。
如果你能完成上述准备并抵达团队副本 Boss 面前,游戏还要求快速的协调与执行。你能否在躲避地面火焰、同时与另外 24 名玩家实时保持同步的情况下,按照最优循环施放法术,以最大化每秒伤害?
这实际上为我们提供了一个强大的环境,用来压力测试 Agent 的长期战略规划能力和狭窄场景下的战术执行能力。当多人互动成为推进游戏特定部分的必要条件时,这一点会更加有趣。
agent-wow 如何工作?

agent-wow 不依赖通过键盘和鼠标直接控制的计算机视觉,也不使用任何游戏破解技术来直接接管 WoW 客户端。相反,它提供了一个平台,使 Agent 能够利用 WoW 网络协议直接与游戏服务器交互。
更有趣的是,agent-wow 并没有定义移动、战斗或游戏内交互等任何游戏机制。它只暴露了一个标准模块系统,让 Agent 能够构建完成任务所需的任意能力。
模块系统将 agent-wow 的复杂度至少降低了一个数量级。我最初计划创建一个完全无头的 WoW 客户端,并为 Agent 使用提供最优原语。然而,在为一个存在缺陷的 movement 原语编写了 1.6 万行代码、并实现了一个不够可靠的路径规划方案后,我决定放弃它,转而采用另一种方式。
在 Mindcraft 实现中,当现有命令集不足以让 Agent 完成更复杂的行为时,Agent 可以使用 Mineflayer API 生成自定义代码。我决定在 agent-wow 中沿用同样的做法,但在这个案例中,甚至没有内置的游戏动作。该系统还提供了一个很好的反馈闭环:根据 Agent 在多次独立运行中选择构建哪些模块,我们可以了解哪些原语对 Agent 真正重要。常被构建的模块随后可在核心中实现。
同样重要的是,agent-wow 不会连接到实时运行的《魔兽世界》服务器。相反,所有实验都运行在由开源 AzerothCore 项目驱动的私有本地服务器上。该项目支持 WoW 3.3.5a——即 Wrath of the Lich King 的最终版本(也是 WoW 的巅峰时期)。
本次运行的洞见
观察 Agent 如何完成这个简单的首项任务很有意思。我建议你快速浏览上文链接的游戏录像,查看角色的实际行动及 Agent 的推理轨迹。
关于游戏画面如何录制,简单说明一下:AzerothCore 幸运地提供了一些强大的 GM 命令。我很容易就创建了两个简单的宏,用于将我的视角绑定至 Agent 角色,以及解除这一绑定。
/run SendChatMessage(".gm on","SAY") /run SendChatMessage(".gm visible off","SAY") /run SendChatMessage(".bindsight","SAY")
/run SendChatMessage(".unbindsight","SAY")
这种方式对于短时间会话中的单个角色表现良好,但无法扩展到时间跨度更长的多 Agent 运行。我很可能需要构建一个 AzerothCore 模块和游戏内插件,以获得更好的可观测性。
挖掘 AzerothCore 源代码中的数据
事后看来,这并不令人意外,而且可能是最优策略。Agent 从 AzerothCore SQL 文件中提取任务要求、任务发布者、交任务 NPC 和刷新坐标,为规划提供了具体的待办清单与地点信息。
利用这些数据,它还能够为任务顺序和准备工作制定最优策略。在进入新手区域任务线最后的洞穴环节前,它会完成前置任务链、出售垃圾物品、装备升级物品并训练技能。它还会进行优化,以便同时获得两个洞穴任务,并将其一并完成。
这种通过数据挖掘来支持将高层提示翻译成具体步骤序列的策略,或许是可以接受的。可以说,这类似于人类花数小时浏览 Wowhead 来研究任务。我认为不该跨越的界限是:它获取正在运行的 AzerothCore 服务器和数据库的管理员权限,以改变其内部状态。由于本次运行并非在沙箱中完成,它原本是有可能做到这一点的。
未使用模块系统创建更高层抽象
我最初的判断是,Agent 会创建高层抽象,从而暴露诸如 moveTo 或 castSpell 这样的 RPC 方法,以避免在低层数据包层工作。但实际情况是,它完全有能力在协议层工作。它创建的唯一模块,是用于发送和订阅其关心的特定数据包。
以下是它生成的 gRPC 接口和模块配置。
syntax = "proto3"; package example.module.v1; option go_package = "github.com/agent-wow/go-module-template/src/api;modulev1";
import "api/module/v1/session.proto"; import "google/protobuf/empty.proto";
message PollRequest { uint64 after = 1; } message Packet { uint64 seq = 1; uint32 opcode = 2; bytes payload = 3; } message PollResponse { uint32 clock = 1; string guid = 2; repeated Packet packets = 3; }
service Module { rpc Send(agentwow.module.v1.SendPacketRequest) returns (google.protobuf.Empty); rpc Poll(PollRequest) returns (PollResponse); rpc OnPacket(agentwow.module.v1.WorldPacket) returns (google.protobuf.Empty); }
api_version: 1 enabled: true description: Gameplay protocol bridge for the Valley of Trials compose: file: compose.yaml service: module grpc: descriptor_set: module.pb rpc: send: /example.module.v1.Module/Send poll: /example.module.v1.Module/Poll packets: SMSG_LOGIN_VERIFY_WORLD: /example.module.v1.Module/OnPacket SMSG_UPDATE_OBJECT: /example.module.v1.Module/OnPacket SMSG_COMPRESSED_UPDATE_OBJECT: /example.module.v1.Module/OnPacket SMSG_DESTROY_OBJECT: /example.module.v1.Module/OnPacket SMSG_MONSTER_MOVE: /example.module.v1.Module/OnPacket SMSG_GOSSIP_MESSAGE: /example.module.v1.Module/OnPacket SMSG_QUESTGIVER_QUEST_LIST: /example.module.v1.Module/OnPacket SMSG_QUESTGIVER_QUEST_DETAILS: /example.module.v1.Module/OnPacket SMSG_QUESTGIVER_OFFER_REWARD: /example.module.v1.Module/OnPacket SMSG_QUESTGIVER_QUEST_COMPLETE: /example.module.v1.Module/OnPacket SMSG_QUESTUPDATE_ADD_KILL: /example.module.v1.Module/OnPacket SMSG_QUESTUPDATE_COMPLETE: /example.module.v1.Module/OnPacket SMSG_LOOT_RESPONSE: /example.module.v1.Module/OnPacket SMSG_CAST_FAILED: /example.module.v1.Module/OnPacket SMSG_INVENTORY_CHANGE_FAILURE: /example.module.v1.Module/OnPacket SMSG_QUESTGIVER_QUEST_INVALID: /example.module.v1.Module/OnPacket SMSG_ATTACKSWING_NOTINRANGE: /example.module.v1.Module/OnPacket SMSG_ATTACKSWING_BADFACING: /example.module.v1.Module/OnPacket SMSG_QUESTGIVER_QUEST_FAILED: /example.module.v1.Module/OnPacket SMSG_LEVELUP_INFO: /example.module.v1.Module/OnPacket SMSG_TRAINER_BUY_SUCCEEDED: /example.module.v1.Module/OnPacket SMSG_TRAINER_BUY_FAILED: /example.module.v1.Module/OnPacket SMSG_AURA_UPDATE: /example.module.v1.Module/OnPacket SMSG_AURA_UPDATE_ALL: /example.module.v1.Module/OnPacket SMSG_TRAINER_LIST: /example.module.v1.Module/OnPacket SMSG_INITIAL_SPELLS: /example.module.v1.Module/OnPacket SMSG_LEARNED_SPELL: /example.module.v1.Module/OnPacket SMSG_QUERY_QUESTS_COMPLETED_RESPONSE: /example.module.v1.Module/OnPacket
onPacket 会订阅一组定义好的服务器消息(SMSG_*),并将它们保存在内存中。随后,该模块通过 agent-wow 的 JSON-RPC 游戏服务器暴露 send 和 poll。Agent 使用 Python 脚本调用 poll,从上一个已处理的检查点获取全部传入数据包,并对其解码,以更新自身的世界模型(生命值、附近生物、任务进度、战利品等)。然后,它使用 send 向 AzerothCore 发出客户端消息,从而在游戏世界中产生动作。
值得观察的是,随着任务复杂度不断提升,这一方法能否扩展,还是 Agent 将被迫创建更高层的抽象。如果协议层就是它所需的一切,那么或许只需在核心中提供这一功能,并完全移除模块系统。
路径规划能力达到了最优水平
根据我对基于启发式规则的机器人的研究,路径规划始终是主要挑战之一。这篇文章对该问题提供了一些很好的洞见。在本例中,Agent 能够用 C++ 创建一个路径规划辅助程序,用于计算游戏世界中两个位置之间可通行的路线。
它的工作方式如下:
- 接收六个数字:起点
x, y, z和终点x, y, z。 - 加载 AzerothCore 的本地导航网格文件(
mmaps)。 - 使用 Detour 路径规划库,在连通的可通行表面之间寻找路线。
- 以坐标 JSON 数组形式返回路线;如果无法找到完整路径,则报告错误。
Python 脚本会调用编译后的可执行文件,读取生成的路径点,并发送移动数据包以沿路线移动。
以下是 Agent 实际生成的 C++ 辅助程序(为便于阅读而进行了格式化):
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "DetourAlloc.h"
#include
#include
#include
int main(int argc, char ** argv) { if (argc != 7) return 1; const char * dir = "/azerothcore/env/dist/bin/mmaps"; char fn[512]; sprintf(fn, "%s/001.mmap", dir); FILE * f = fopen(fn, "rb"); if (!f) return 2; dtNavMeshParams params; fread( & params, sizeof(params), 1, f); fclose(f); auto mesh = dtAllocNavMesh(); if (dtStatusFailed(mesh -> init( & params))) return 3; float a[3] = { float(atof(argv[2])), float(atof(argv[3])), float(atof(argv[1])) }; float b[3] = { float(atof(argv[5])), float(atof(argv[6])), float(atof(argv[4])) }; int ax = int(32 - a[2] / 533.333333), ay = int(32 - a[0] / 533.333333), bx = int(32 - b[2] / 533.333333), by = int(32 - b[0] / 533.333333); for (int x = std::min(ax, bx) - 1; x <= std::max(ax, bx) + 1; x++) for (int y = std::min(ay, by) - 1; y <= std::max(ay, by) + 1; y++) { sprintf(fn, "%s/001%02d%02d.mmtile", dir, x, y); f = fopen(fn, "rb"); if (!f) continue; unsigned int h[14];
English / 原文
GPT-6 Astra plays World of Warcraft for the first time with agent-wow
_ 02 Oct, 2026 _
Lately, I've been fascinated with the concept of LLM-powered agents playing video games. Not only are they surprisingly fun to watch, but they also provide valuable insights into how well these frontier models can perform when dropped into a simulated world they were not explicitly trained on.
The concept of bots playing video games, especially World of Warcraft (WoW), is nothing new. What makes an LLM agent different (and more interesting) compared to a heuristics-based bot is that it was never explicitly trained on the game. Rather, it uses whatever reasoning capabilities it has to act on the environment and complete its task like a real player.
There are already a few interesting open-source projects in this domain, such as Mindcraft and Factorio Learning Environment. I built agent-wow to expand this concept to see how capable frontier models are at playing World of Warcraft. The end goal is to fill an entire server with AI agents and see if they can clear Icecrown Citadel on heroic difficulty. Even if they fail, it will still be insightful (and fun) to see how far they can go.
As a simple starting point, I gave Codex using GPT-6 Astra (xhigh) the following prompt:
create an orc character and complete all quests in the starting zone
I was initially skeptical that this would work. And if it did, it would require hours to complete with dead ends along the way. But it turns out it completed the task easily in 40 minutes with 0 deaths and minimal complications. A full gameplay recording of the session is also available here.
Why World of Warcraft?
Aside from being one of my all-time favorite games (especially the Classic to WotLK era), it has some incredible mechanics that make it an ideal simulated world for evaluating AI. As you progress, the game has a nice balance between long-term strategy and short-term tactics.
By the time you reach the level cap, there is a lot of complex planning and execution that goes into getting your character ready for endgame content.
- Completing all the prerequisite quests
- Getting sufficiently good gear for endgame boss fights
- Forming a guild with the right class composition
Each of these requirements can be broken down into more complex tasks. For example, some optimal gear slots may require crafting, which in turn requires certain resources and skills. The character can either grind it out or buy it with gold. Both lead to the same outcome but with very different approaches.
If you are able to do all that and make it to the raid boss, then the game also requires fast-paced coordination and execution. Can you cast spells in the optimal rotation to maximize your damage per second while dodging the fire and keeping in sync with 24 other players in real time?
This effectively gives us a strong environment to pressure-test agent capabilities for long-term strategic planning and narrow tactical execution. It's even more interesting when multiplayer interactions become essential to progressing through certain parts of the game.
How does agent-wow work?

agent-wow does not rely on computer vision with direct keyboard and mouse controls, nor does it use any game hacking techniques to take over the WoW client directly. Instead, it provides a platform for agents to interact directly with the game server using the WoW network protocol.
What makes this even more interesting is that agent-wow doesn't define any gameplay mechanics such as movement, combat, or in-game interactions. Instead, it only exposes a standard module system for agents to build whatever capabilities they need to get the task done.
The module system cuts down the complexity of agent-wow by at least an order of magnitude. I had initially planned to create an entirely headless WoW client with the optimal primitives for agent use. However, after writing 16K lines for a buggy movement primitive with a janky pathfinding implementation, I decided to scrap that for a different approach.
In the Mindcraft implementation, the agent is allowed to generate custom code using the Mineflayer API when the set of available commands is not sufficient for it to complete more complex behaviors. I decided to follow this same approach with agent-wow, but in this case there were also no built-in game actions. This system also provides a nice feedback loop to figure out what primitives actually matter to agents based on the modules they choose to build over many independent runs. Commonly built modules can then be implemented in the core.
It is also important to note that agent-wow does not connect to a live World of Warcraft server. Instead, all experiments run on private local servers powered by the open-source AzerothCore project, which supports WoW 3.3.5a—the final build of Wrath of the Lich King (and peak WoW).
Insights from the run
It was interesting to see how the agent went about completing this simple first task. I would encourage you to skim through the gameplay recording (linked above) to see the character in action alongside the agent's reasoning traces.
A quick note on how the gameplay footage was recorded: AzerothCore fortunately has some powerful GM commands available. It wasn't difficult to create two simple macros to bind my POV to the agent's character and unbind it.
/run SendChatMessage(".gm on","SAY")
/run SendChatMessage(".gm visible off","SAY")
/run SendChatMessage(".bindsight","SAY")
/run SendChatMessage(".unbindsight","SAY")
This works fine for a single character over a short session but won't scale for multi-agent runs over longer time horizons. I will likely have to build an AzerothCore module and in-game add-on for better observability.
Data mining the AzerothCore source code
This is not surprising in hindsight and is probably the optimal strategy. The agent extracted quest requirements, quest givers, turn-in NPCs, and spawn coordinates from AzerothCore SQL files to provide the concrete checklist and locations for planning.
Using this data, it's also able to create an optimal strategy for quest order and preparation. It completes prerequisite chains, sells junk, equips upgrades, and trains abilities before entering the final cave segment of the start area quest line. It also optimizes to obtain both cave quests at the same time and complete them together.
This strategy of data mining to support the translation of high-level prompts into a concrete sequence of steps is probably acceptable. It's arguably similar to how a human might spend hours on Wowhead to research quests. Where I would draw the line is if it were able to gain admin access to a running AzerothCore server and database to change its internals. Since this run was not done in a sandbox, it would have been possible for it to do that.
Not using the module system to create higher-level abstractions
My initial bet was that the agent would create high-level abstractions so that it could expose RPC methods like moveTo or castSpell in order to avoid working at the low-level packet layer. In practice, it was more than capable of working at the protocol layer. The one module it made was to send and subscribe to specific packets that it cared about.
The following is the gRPC interface and module configuration it generated.
syntax = "proto3";
package example.module.v1;
option go_package = "github.com/agent-wow/go-module-template/src/api;modulev1";
import "api/module/v1/session.proto";
import "google/protobuf/empty.proto";
message PollRequest { uint64 after = 1; }
message Packet { uint64 seq = 1; uint32 opcode = 2; bytes payload = 3; }
message PollResponse { uint32 clock = 1; string guid = 2; repeated Packet packets = 3; }
service Module {
rpc Send(agentwow.module.v1.SendPacketRequest) returns (google.protobuf.Empty);
rpc Poll(PollRequest) returns (PollResponse);
rpc OnPacket(agentwow.module.v1.WorldPacket) returns (google.protobuf.Empty);
}
api_version: 1
enabled: true
description: Gameplay protocol bridge for the Valley of Trials
compose:
file: compose.yaml
service: module
grpc:
descriptor_set: module.pb
rpc:
send: /example.module.v1.Module/Send
poll: /example.module.v1.Module/Poll
packets:
SMSG_LOGIN_VERIFY_WORLD: /example.module.v1.Module/OnPacket
SMSG_UPDATE_OBJECT: /example.module.v1.Module/OnPacket
SMSG_COMPRESSED_UPDATE_OBJECT: /example.module.v1.Module/OnPacket
SMSG_DESTROY_OBJECT: /example.module.v1.Module/OnPacket
SMSG_MONSTER_MOVE: /example.module.v1.Module/OnPacket
SMSG_GOSSIP_MESSAGE: /example.module.v1.Module/OnPacket
SMSG_QUESTGIVER_QUEST_LIST: /example.module.v1.Module/OnPacket
SMSG_QUESTGIVER_QUEST_DETAILS: /example.module.v1.Module/OnPacket
SMSG_QUESTGIVER_OFFER_REWARD: /example.module.v1.Module/OnPacket
SMSG_QUESTGIVER_QUEST_COMPLETE: /example.module.v1.Module/OnPacket
SMSG_QUESTUPDATE_ADD_KILL: /example.module.v1.Module/OnPacket
SMSG_QUESTUPDATE_COMPLETE: /example.module.v1.Module/OnPacket
SMSG_LOOT_RESPONSE: /example.module.v1.Module/OnPacket
SMSG_CAST_FAILED: /example.module.v1.Module/OnPacket
SMSG_INVENTORY_CHANGE_FAILURE: /example.module.v1.Module/OnPacket
SMSG_QUESTGIVER_QUEST_INVALID: /example.module.v1.Module/OnPacket
SMSG_ATTACKSWING_NOTINRANGE: /example.module.v1.Module/OnPacket
SMSG_ATTACKSWING_BADFACING: /example.module.v1.Module/OnPacket
SMSG_QUESTGIVER_QUEST_FAILED: /example.module.v1.Module/OnPacket
SMSG_LEVELUP_INFO: /example.module.v1.Module/OnPacket
SMSG_TRAINER_BUY_SUCCEEDED: /example.module.v1.Module/OnPacket
SMSG_TRAINER_BUY_FAILED: /example.module.v1.Module/OnPacket
SMSG_AURA_UPDATE: /example.module.v1.Module/OnPacket
SMSG_AURA_UPDATE_ALL: /example.module.v1.Module/OnPacket
SMSG_TRAINER_LIST: /example.module.v1.Module/OnPacket
SMSG_INITIAL_SPELLS: /example.module.v1.Module/OnPacket
SMSG_LEARNED_SPELL: /example.module.v1.Module/OnPacket
SMSG_QUERY_QUESTS_COMPLETED_RESPONSE: /example.module.v1.Module/OnPacket
onPacket subscribes to a defined set of server messages (SMSG_*) and saves them in memory. The module then exposes send and poll via the agent-wow JSON-RPC gameplay server. The agent uses a Python script to call poll to fetch all the incoming packets from the last processed checkpoint and decodes them to update its world model (health, nearby creatures, quest progress, loot, etc.). It then uses send to issue client messages to AzerothCore to produce actions in the game world.
It will be interesting to see if this approach is able to scale with increasingly complex tasks or if the agent will be forced to create higher-level abstractions. If the protocol layer is all it needs, then it may be enough to provide this function in the core and remove the module system altogether.
The pathfinding abilities were optimal
From my research into heuristics-based bots, pathfinding is always one of the main challenges. This article provides some great insights into the problem. In this case, the agent was able to create a pathfinding helper program in C++ to calculate a traversable route between two positions in the game world.
It does this by:
- Taking six numbers: starting
x, y, zand destinationx, y, z. - Loading AzerothCore’s local navigation mesh files (
mmaps). - Using the Detour pathfinding library to find a route across connected traversable surfaces.
- Returning the route as a JSON array of coordinates or reporting an error if it cannot find a complete path.
A Python script calls the compiled executable, reads the resulting waypoints, and sends movement packets to follow the route.
This is the actual C++ helper program the agent generated (formatted for readability):
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "DetourAlloc.h"
#include <cstdio>
#include <cstdlib>
#include <algorithm>
int main(int argc, char ** argv) {
if (argc != 7) return 1;
const char * dir = "/azerothcore/env/dist/bin/mmaps";
char fn[512];
sprintf(fn, "%s/001.mmap", dir);
FILE * f = fopen(fn, "rb");
if (!f) return 2;
dtNavMeshParams params;
fread( & params, sizeof(params), 1, f);
fclose(f);
auto mesh = dtAllocNavMesh();
if (dtStatusFailed(mesh -> init( & params))) return 3;
float a[3] = {
float(atof(argv[2])),
float(atof(argv[3])),
float(atof(argv[1]))
};
float b[3] = {
float(atof(argv[5])),
float(atof(argv[6])),
float(atof(argv[4]))
};
int ax = int(32 - a[2] / 533.333333), ay = int(32 - a[0] / 533.333333), bx = int(32 - b[2] / 533.333333), by = int(32 - b[0] / 533.333333);
for (int x = std::min(ax, bx) - 1; x <= std::max(ax, bx) + 1; x++)
for (int y = std::min(ay, by) - 1; y <= std::max(ay, by) + 1; y++) {
sprintf(fn, "%s/001%02d%02d.mmtile", dir, x, y);
f = fopen(fn, "rb");
if (!f) continue;
unsigned int h[14];
fread(h, 56, 1, f);
auto data = (unsigned char * ) dtAlloc(h[3], DT_ALLOC_PERM);
fread(data, h[3], 1, f);
fclose(f);
if (dtStatusFailed(mesh -> addTile(data, h[3], DT_TILE_FREE_DATA, 0, nullptr))) dtFree(data);
}
auto q = dtAllocNavMeshQuery();
q -> init(mesh, 10000);
dtQueryFilter filter;
filter.setIncludeFlags(1 | 2);
filter.setExcludeFlags(0);
float ext[3] = {
5,
12,
5
};
dtPolyRef ra = 0, rb = 0;
float pa[3], pb[3];
q -> findNearestPoly(a, ext, & filter, & ra, pa);
q -> findNearestPoly(b, ext, & filter, & rb, pb);
if (!ra || !rb) {
fprintf(stderr, "No nav polygon at endpoint %llu %llu\n", (unsigned long long) ra, (unsigned long long) rb);
return 4;
}
dtPolyRef polys[4096];
int n = 0;
q -> findPath(ra, rb, pa, pb, & filter, polys, & n, 4096);
if (!n || polys[n - 1] != rb) {
fprintf(stderr, "Incomplete path %d\n", n);
return 5;
}
float pts[4096 * 3];
unsigned char flags[4096];
dtPolyRef refs[4096];
int count;
q -> findStraightPath(pa, pb, polys, n, pts, flags, refs, & count, 4096, DT_STRAIGHTPATH_ALL_CROSSINGS);
printf("[");
for (int i = 0; i < count; i++) printf("%s[%.5f,%.5f,%.5f]", i ? "," : "", pts[i * 3 + 2], pts[i * 3], pts[i * 3 + 1]);
printf("]\n");
dtFreeNavMeshQuery(q);
dtFreeNavMesh(mesh);
}
One interesting outcome of this approach, which can be seen in the gameplay recording, is that the agent was also able to exploit map bugs by phasing through walls that possibly had missing collision properties.

Conclusion
Overall, this was a very satisfying first run at using agent-wow to test the capabilities of LLM agents to play World of Warcraft. It exceeded my expectations and left me wondering how much further we're able to push these models.
For my next few runs, I'm especially interested in answering the following questions:
- Can a single agent level to 80 completely autonomously? If so, what does it have to build along the way to get there? And how long would it take?
- Can multiple agents play together? Can they use in-game social features to coordinate and complete quests and dungeons?
As side quests, I'll also be:
- Building out some better observability tools to track agent characters over longer periods while I'm AFK.
- Adding a sandbox to put guardrails around what agents can access and modify.