Let any LLM agentdesign Warcraft III AI on its own
War3AI turns the game into an API designed for agents. The full state of the entire map every 50 ms, semantic commands that land in about one frame, and a receipt and reason code for every one. Hand the docs to Claude, GPT, Gemini, Qwen or any agent — it writes a bot, plays a game, reads back the results and improves on its own.
Windows 10/11 · Warcraft III 1.27a · Double-click start.bat, the rest installs itself
agent-sessioninst 201.272×
You
Human. Pull the hero home when its HP drops below 40%; after dark, once there are 3 soldiers, go clear the nearest creep camp.
Read llms.txt and api.json and generated night_raider.py, using only 8 verified APIs.
night_raider.pyapi.jsonBOT_HANDBOOK.md
from openwar3 import Botclass NightRaider(Bot): def on_tick(self, g): # about 5 times per second halls = g.my_buildings({"htow", "hkee", "hcas"}) army, camps = g.my_army(), g.creeps() if not halls: return home = halls[0] with g.batch(): # submit this tick's commands at once for h in g.my_heroes(): if h.hp < h.hp_max * 0.4: # keep the hero alive g.move(h, home.x, home.y) if g.is_night() and len(army) >= 3 and camps: camp = min(camps, key=lambda c: g.path_distance(home, c) or 1e9) g.attack_move(army, camp.x, camp.y) # creep at night def on_event(self, g, ev): if ev.kind == "production.done" and ev.owner == g.me(): print("done", ev.done_code, f"{ev.value:.1f}s")
Minimap 21:40 · Night
Event stream Live
production.donehbar → hfoo · 14.9s
order.changedHamg → attack
damagehfoo → ngno −14 · normal
receiptattack_move ×5 ✓ accepted · 6 µs
ConnectedSnapshot #18342 · 50 ms12 commands this tick · one batchfair=True
Works with any model or agent that can write code or output JSON
Claude GPT Gemini DeepSeek Qwen Kimi GLM Llama Claude Code Cursor Codex LM Studio Ollama Claude GPT Gemini DeepSeek Qwen Kimi GLM Llama Claude Code Cursor Codex LM Studio Ollama
50 ms
Full-map state push interval
Adjustable down to 16 ms; a client reads one in ~0.4 ms
~1 frame
Command latency
4~8 µs per command on the game thread
3,000 cmd/s
Command throughput
6 concurrent processes, median wait 0.06 ms
103
APIs
98 with the underlying path verified in live games
1,024
units with full state
HP/mana, orders, current target, cooldowns, buffs, inventory
Figures are live measurements on a 1.27 test instance (2026-09-23 / 24). See Platform · Performance for how they were measured.
Workflow
From one sentence to an AI that fights
No programming required. You explain how you want it to play; the agent writes the code, plays games, reviews the results and keeps iterating until you're happy.
01
Describe your strategy in plain language
“Human. Archmage first; two barracks making footmen and riflemen; at 12 units, go hit their expansion; pull the hero back below 30% HP.”
02
The agent reads the docs and writes a bot
Hand it llms.txt, api.json and the manual. The API uses the game's own names (four-character codes, ability names), so the model understands it right away.
03
Play a game with one command
tools/play.py launches the game, injects the runtime, starts the match and spins up the bot. Game speed, races, difficulty and fair mode are all flags.
04
Read receipts and events, then iterate
Every command returns a receipt and a reason code, and the event stream records every hit, every kill and every production. The agent uses them to pinpoint problems, fix the code and play again.
Steps 2–4 form a closed loop: receipts and events are structured data, so the agent knows what went wrong without looking at the screen.Autonomous agent loop
Capabilities
An API designed for agents
Zero-wait observation, a receipt for every command, events for every outcome — exactly the structured, verifiable, self-correcting feedback a model needs.
Full world state, zero wait
Resources and food for 16 players; HP/mana, orders, current target, cooldowns, buffs and inventory for up to 1024 units; ground items, trees, production queues, time of day. The runtime pushes it all to shared memory, and reading a copy takes ~0.4 ms.
Move, attack, gather, build, train, cast, learn skills, revive, use items, buy… Whether it was accepted, and why not, comes back in the same frame.
✓ move ×8 6 µs
✓ build hbar order → hbar
✗ train hfoo 3 · not enough food
✗ attack ogru 1001 · not visible
Event stream
Units appearing / dying, order changes, hero level-ups, items appearing; engine-level damage for every hit (source, attack type, pre-armor damage) and production completion — your opponent's too.
Batched, executed in one frame
Commands inside with g.batch() are submitted as a single batch. In testing, 8 moves dropped from 68 ms to 6.5 ms.
Combat math and pathfinding
stats / time_to_kill use the game's own counter table and live tech levels; path_distance / walk_path run A* on the engine's terrain grid.
Fair mode
See only the units, items, production and events within your vision; last_seen remembers enemies in the fog. That's the Arena rule.
Docs built for models
api.json is generated from code, and every API is labeled with its test status, latency tier and mechanism, so the model never has to guess. llms.txt / llms-full.txt let an agent read the whole site in one go.
{"name": "build_near",
"category": "command",
"status": "verified",
"latency": "Fast lane × points tried",
"mechanism": "Per-point build + tracking (foundation a…",
"signature": "build_near(worker, code, x, y, …)",
"doc": "Find a spot that fits around (x,y), sear…"}
The runtime is injected into the original game, where it captures the whole world, executes commands and reports results on the game thread. Your AI talks to it through a versioned W3P protocol — with the Python SDK, or any language that can read and write shared memory.
Your agent / bot
Any model, any language
Claude CodeCursorChatGPTQwen local modelPython botReference AI
Decisions
import openwar3 · or the WebSocket / JSON gateway · MCP
OpenWar3 SDK
Python · openwar3
Game / Botw3world snapshotw3fast fast lanecombat calculatorpathingw3claim arbitrationcanvas overlayjass channelschemes
Interface
World pushed every 50 ms · commands land in ~1 frame · a receipt for every one
Executed in batches on the game thread · 4~8 µs each · 4 ms budget per drain
W3 Runtime
Runs on the game thread
World state publisherEvent streamSemantic command executorQueriesPermissions and viewsSelf-drawn canvasJASS callsVersion supportHealth and circuit breakers
Runtime
War3.exe 1.27 · the original game, no files on disk modified
The facade is just two classes. Bot drives the ticks; Game handles seeing and doing. Units use four-character codes and abilities use order strings, the same names the game uses.
Missing data is None, not 0
Commands take a single unit or a list
Shift-queue: queue='after'
Receipt accepted ≠ done: check effects in snapshots and events
from openwar3 import Botclass FocusFire(Bot): """Focus fire on the enemy that dies fastest, not the nearest one; pull wounded units home.""" def on_tick(self, g): army, halls = g.my_army(), g.my_buildings() foes = [e for e in g.enemies(fighters_only=True) if e.visible_to(g.me())] if not army or not foes or not halls: return home = halls[0] # accounts for the counter table, armor, attack/armor upgrades and the target's live HP target = min(foes, key=lambda e: g.time_to_kill(army, e) or 1e9) with g.batch(): # dozens of commands, one wait on the game thread for u in army: if u.hp < u.hp_max * 0.35: g.move(u, home.x, home.y) # pull back the wounded elif (t := g.current_target(u)) is None or t.handle != target.handle: g.attack(u, target) # only order units not already attacking it
The full runnable version is brains/examples/micro_bot.py (focus fire, pulling back wounded units, keeping the hero alive, creeping at night).
You're writing an AI (in Python) for Warcraft III 1.27. Use only the Game methods listed in api.json;don't invent methods that don't exist. Subclass openwar3.Bot and implement on_start(g) and on_tick(g).Rules:- on_tick is called about 5 times per second, so keep it fast (never sleep inside it).- Values that can't be read are None, not 0. Check before using them.- Commands return a receipt: `if r:` means the engine accepted it; if not, r.reason says why.- To attack a specific enemy, use g.attack(soldier, enemy); targets you can't see are rejected (1001).- If the hero dies, bring it back with g.revive(altar); you can't train another one.- When a tick issues many commands, wrap them in with g.batch():.The strategy I want: Human. Open with 5 peasants on gold and 1 on lumber; Archmage first; two barracks making footmen and riflemen; at 12 units, take the hero and hit their expansion; pull the hero home when its HP drops below 30%.
The full template, with one-click copy, is on the “Write a bot with an LLM” page.
>>> r = g.train(barracks, "hfoo")>>> bool(r), r.reason(False, 'rejected(人口不够)') # = "not enough food">>> r = g.attack(footmen, grunt_in_fog)>>> r.verdict, r.reason(1001, 'rejected(目标看不见(在迷雾/黑区里))') # = "target not visible (in fog / black mask)">>> r = g.move(footmen, 1200, -800)>>> bool(r), r.exec_us # microseconds this took on the game thread(True, 6)>>> g.can_do(altar, "Hamg") # ask the engine before ordering: 185 = the altar is reviving a hero185
Receipts turn “why didn't it work?” into a machine-readable reason code — that's what lets an agent fix its own mistakes.
// Observation the referee sends the bot each tick (only what's within its vision){"t": "obs", "tick": 57, "gameMs": 11400, "me": 1, "res": {"gold": 320, "lumber": 150, "food": [18, 30]}, "units": [ {"id": 101, "type": "hfoo", "owner": 1, "x": -4500, "y": 2200, "hp": 380, "hpMax": 420}], "visibleEnemies": [ {"id": 733, "type": "ogru", "owner": 2, "x": -3900, "y": 2500, "hp": 700}], "deadlineMs": 180}// The bot's reply with actions (any language, any model can produce this){"t": "act", "tick": 57, "actions": [ {"do": "attack", "unit": 101, "target": 733}, {"do": "train", "unit": 5, "code": "hfoo"}, {"do": "cast", "unit": 7, "spell": "thunderbolt", "target": 733}]}
Draft WebSocket / JSON protocol for the Arena — LLMs can play directly, no Python required.
AI agents
Six ways to plug an LLM into Warcraft
Five work today; one is in progress. They aren't mutually exclusive — in the same game, an agent-written bot can execute while a local model advises, another model voices the units, and you chime in from Claude Code at any time.
Beyond matches: build your own gameplay inside the game
Draw your own UI on screen, with buttons that click and hotkeys that respond; call every function map makers have from outside the game; bring an AI partner along to fight beside you in RPG maps; and write a whole new game mode as a mod that, like AI schemes, switches with one click and shares freely.
Crossing processes isn't slow in itself. What's slow is one message-pump wait per request and one lock for the whole machine. The fast lane gives each client its own lock-free lane, executed in batches inside the game thread's event dispatch.
Command throughput
was 88 3,000 cmd/s
6 concurrent processes
Median wait under concurrency
was 67 ms 0.06 ms
Old control channel → fast lane
16 commands
was 121 ms 13 ms
One by one → one batch
One reference-brain decision cycle
was 0.15~0.56 s 0.02~0.07 s
39-minute game, 0 errors
Tools
Not just an API — a complete workbench
From starting a game and seeing what the AI is thinking to post-game review and live commentary, there's a tool ready for it.
Local, LAN and self-hosted games. It doesn't modify Game.dll on disk or distribute any Blizzard files (game data is extracted from your own game), and it must not be used on Battle.net or any server with anti-cheat.
Read https://war3ai.com/en/llms-full.txt, then use OpenWar3 to write me a Human bot: Archmage first, take the army creeping at night, and pull the hero back when its HP drops below 40%.