# 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.

Source: https://war3ai.com/en/docs/rules/

> **Tip**
>
> Give this page to an LLM together with [`api.json`](https://war3ai.com/en/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:

```python
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):

```python
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](https://war3ai.com/en/arena/). See [Fair mode](https://war3ai.com/en/docs/fair-mode/).
