Example bots
Four examples, from simple to complex. Each one runs as-is, and every piece of logic maps to an SDK capability. Plus a complete reference brain.
The examples live in brains/examples/. Each one builds on the previous one and only adds what’s new. Read them in order:
| Example | What it teaches | Run it |
|---|---|---|
hello_bot.py | Gathering (5 per mine; when a mine is full, chop lumber), training workers (only 1 in the queue), building food structures, resuming stalled foundations; works for all four races | python tools/play.py --bot brains/examples/hello_bot.py |
rush_bot.py | Barracks and altar (built with build_near if missing), hero first (revived when it dies), learning skills whenever points are available, gathering a wave and attack-moving | … --bot brains/examples/rush_bot.py |
macro_bot.py | Build order + returning to the mine after building (Shift), adding food the moment production is blocked, 1 unit in the barracks queue, attack/armor upgrades, tiering up and advanced units, picking targets by actual ground distance and following the path | … --bot brains/examples/macro_bot.py --speed 200 |
micro_bot.py | Takes over combat on top of the macro: focus fire on whatever dies fastest, pull back wounded units, keep the hero alive, pick creep camps it can win at night, defend when enemies reach the base; sends each tick’s commands as one batch | … --bot brains/examples/micro_bot.py --fair |
The comments in hello_bot and rush_bot record pitfalls hit in live games, such as “it always picked the first worker to build, so all 3 farms ended up as half-built foundations” and “the hardcoded barracks coordinates were right in a forest, and not a single barracks got built in 3 minutes.” The comments teach more than the code.
hello_bot: economy
# race -> (worker, town halls, food building)
RACES = {
"h": ("hpea", {"htow", "hkee", "hcas"}, "hhou"),
"o": ("opeo", {"ogre", "ostr", "ofrt"}, "otrb"),
"u": ("uaco", {"unpl", "unp1", "unp2"}, "uzig"),
"e": ("ewsp", {"etol", "etoa", "etoe"}, "emow"),
}
MINE_CAP = 5 # at most 5 peasants per mine (more doesn't raise income)
LUMBER_CREW = 5 # lumber crew size: 5 on gold per mine + this many on lumber = worker target
Three things: idle workers go mine gold (it keeps its own count of workers per mine; when a mine is full, they chop lumber); train workers when short (only 1 in the queue); when food is nearly capped, find a worker that isn’t already building and put up a food building next to the town hall (for Human and Orc, it also sends workers to resume stalled foundations).
rush_bot: army and attacks
Adds three things on top of hello_bot: build a barracks and an altar if missing; train a hero at the altar (revive it first if it’s dead, since heroes are unique) and learn skills whenever points are available; once 8 soldiers are gathered, attack-move them all to the enemy town hall, and come home to regroup when they’re badly beaten. Only idle soldiers get orders, so fights aren’t interrupted tick after tick.
macro_bot: macro fundamentals
TECH = {
"h": dict(order=["halt", "hbar", "hbla", "hlum"], altar="halt", hero="Hamg", skills=["AHwe", "AHbz", "AHab"],
barracks="hbar", soldiers=["hfoo", "hrif", "hkni"], smith="hbla", upgrades=["Rhme", "Rhar", "Rhra", "Rhla"],
tiers=["hkee", "hcas"]),
...
}
The things pro players do every game, each mapped to an SDK capability: a build-order table + gather(..., queue="after") to return to the mine after building; production().blocked to spot food-blocked production; g.queue to keep only 1 unit in the barracks queue; can_do to ask the engine whether the next attack/armor level can be researched; tiering up and advanced units (a lesson from a live game: it stayed at tier 1 the whole time and got steamrolled at 23 minutes by tier-3 knights and gryphons); picking targets by path_distance and following waypoints with path().
micro_bot: once the fighting starts
def _fight(self, g, army, foes, home, now):
...
visible = [e for e in foes if e.visible_to(me)] # targets you can't see get rejected (1001)
atk = [s for s in (g.stats(u) for u in fighters) if s]
target = min(visible, key=lambda e: _ttk(g, atk, e)) # the one that dies fastest, not the nearest
idle_or_other = [u for u in fighters if g.current_target(u) is None
or g.current_target(u).handle != target.handle]
if idle_or_other:
g.attack(idle_or_other, target)
In a live game: 1497 ticks, 3023 commands and 0 errors over 5 minutes.
Reference brain: a complete AI
brains/xwar3/ is a complete AI that expands, creeps and attacks, organized in three layers:
| Layer | Location | Cadence | What it does |
|---|---|---|---|
| Strategy layer | strategy/ | Seconds | AMAI-style selection and switching among multiple strategies, build tables, counter units, hero picks; optional LLM strategy coach |
| Reflex layer | reflex/ (4 separate processes) | ~100 ms | Staying alive, casting, focus fire, picking up items |
| Win-rate model | worldmodel/ | — | Can we win this fight (inference subset) |
Multiple processes share units through a claim table, and priority decides who’s in charge: manual 95 > survival 90 > dodging spells 85 > casting 80 > item pickup 70 > … > strategy 50 > worker assignment 45. Your own bot appears in the table as bot, with a default priority of 50.
The reference brain uses the SDK’s low-level layer directly (w3cmd / act) and relies heavily on full-map information. Treat it as a source of ideas; don’t have an LLM copy it directly. It needs AMAI data: the first time start.bat deploys, it pulls it from AMAI’s public repository and generates it (AMAI has a custom license, so the generated files are not committed to git; if that fails, retry with start.bat setup).
The easiest way to start the reference brain is the Farsight console: on the Instances & Setup page, check the instance numbers and click Start test.