Docs Core concepts

Fifteen rules

Every one was learned the hard way in real games. Check your bot against them and you'll skip most of your debugging.

Give this page to an LLM together with api.json, and the bot it writes will avoid a lot of detours.

Reading state

1. Missing data is None, not 0

resources(), time_of_day(), production() and cooldown() can all return None (while loading, when a unit has no details, when a building isn’t producing…). Check before you use the value:

res = g.resources()
if res is None:
    return

2. Identify units by handle, not by address

Addresses get reused by new units: an old address may point to a freshly spawned unit. To remember a unit across ticks, store u.handle and get it back with g.unit(handle).

3. The event stream is global

production.done and unit.died include events from your opponent and from creeps. Filter by ev.owner (or by the building handle):

if ev.kind == "production.done" and ev.owner == g.me():
    ...

4. Workers inside a gold mine aren’t in the snapshot

The moment a worker enters a gold mine, it disappears from the snapshot (unit.removed; it isn’t dead). To count the workers on each mine, keep your own tally and don’t prune it based on the snapshot, or you’ll send extra workers to a full mine.

Issuing commands

5. A receipt saying “accepted” ≠ done

The engine accepts a build spot in a forest immediately too; it only fails once the worker gets there. Spells can be interrupted. Check the effects in snapshots and events: build with build_near (it tracks whether the foundation appears), and after casting, check whether g.cooldown() has started.

6. You can’t attack what you can’t see

Targeted commands on enemies in the fog are rejected with reason code 1001. To chase an enemy into the fog, attack_move to where it was last seen.

7. Only give orders to idle units

Reissuing the same command to the same unit every tick interrupts it: soldiers twitch in place and peasants’ gather cycle resets to zero. Check whether a unit is idle with g.order_of(u) (which includes what you issued this tick), not the snapshot’s u.order (the snapshot hasn’t caught up yet).

8. Shift can only insert right after the current order

The engine has no “append to the end”: sending B and then C with queue='after' gives you A, C, B. To walk a sequence of points, use g.path(units, points); to have one worker build several structures in a row, use g.build_queue(worker, plan). Both insert in reverse order and handle this for you.

9. Send each tick’s commands as one batch

Sending dozens of commands one by one means waiting on the game thread dozens of times; wrap them in with g.batch(): and you wait only once.

Economy and production

10. At most 5 workers per mine

More doesn’t increase income. Scale your worker target with the number of mines: 5 on gold per mine, plus a few on lumber.

11. Queue only 1 unit at a time

Filling all 7 slots locks your money in the queue (in testing, a town hall queued 4 peasants, tying up 300 gold and slowing the opening considerably). Queue the next one when g.queue(b) is empty.

12. Check the production table for food blocks

g.production(b).blocked means something is queued but hasn’t started, usually because there isn’t enough food. It’s one step ahead of “build when food is nearly capped”: when you lose a chunk of your army in a fight and the queue stalls while you rebuild, you’ll know right away.

13. Heroes are unique, and you can’t tier up while the town hall queue is busy

  • A dead hero can only come back through g.revive(altar); training it again is rejected (221). Reviving also costs food (a hero takes 5).
  • You can’t upgrade the town hall while its queue still has something in it (reason code 185, “building is busy”).

Time and space

14. At 2× speed, don’t wait on the wall clock

To wait 3 game seconds, watch for g.clock() to advance by 3, not sleep(1.5). At higher game speeds, the engine clock runs faster than the wall clock.

15. Don’t use straight-line distance on island or forest maps

Use g.path_distance(a, b) (ground A* that routes around forests, cliffs and buildings) to choose creep camps and expansions; it returns None when the target is unreachable. The spot that’s nearest in a straight line may be across the sea.

One more: write for fair mode

Under --fair, you only see the units, items, production and events within your vision; that’s exactly the Arena’s rule. Write for fair mode now, and you won’t need to change anything when you move to the Arena. See Fair mode.