# Pro Playbook Cookbook

> Most of a top player's edge comes from dozens of small habits. This page maps common pro techniques, one by one, onto SDK code — every snippet can be pasted straight into on_tick.

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

Conventions: `g` is the `Game`, `home` is our main base (`g.my_buildings({"htow", "hkee", "hcas"})[0]`), `now = g.clock()`. For API details, see the [API reference](https://war3ai.com/en/api/); complete runnable examples are in [Example Bots](https://war3ai.com/en/docs/examples/).

> **Tip**
>
> When you ask an LLM to add a technique, paste it the relevant recipe along with its code. That works far better than telling it to "play more like a pro".

## I. Economy

### 1. Workers never idle, 5 per mine

```python
for w in g.idle_workers():                                   # only assign idle ones (re-ordering busy workers interrupts harvesting)
    mine = g.nearest([m for m in g.gold_mines() if crew[m.addr] < 5], w)
    g.gather(w, mine) if mine else g.gather(w, g.trees(w.x, w.y, limit=1)[0])
```

Track how many you've sent to each mine yourself (`crew`): workers inside a gold mine aren't in the snapshot. Full example in `hello_bot.py`.

### 2. Queue only 1 at a time, don't lock up gold

```python
for b in g.my_buildings({"hbar"}):
    if not g.queue(b):                                       # only queue the next one when it's empty
        g.train(b, "hfoo")
```

### 3. Never get food blocked

```python
stuck = any(p.blocked for _b, p in g.all_production("me"))  # queued but not started = not enough food
res = g.resources()
if stuck or res["food_cap"] - res["food_used"] <= 6:
    g.build_near(builder, "hhou", home.x, home.y)
```

`blocked` fires one step earlier than "almost full": when you lose a chunk of your army in a fight and the queue stalls as you rebuild, you know immediately.

### 4. Build order + return to the mine when done (Shift-queue back to mining)

```python
spot = g.build_near(w, "hbar", home.x, home.y)
if spot:
    g.gather(w, mine, queue="after")                         # go back to mining when done, no need to find it again next tick
```

One peasant building several in a row: `g.build_queue(w, [("hhou", x1, y1), ("hhou", x2, y2)])`. Gold is only deducted when construction starts.

### 5. Tier-up timing, attack/armor upgrades

```python
if not g.queue(hall) and g.can_do(hall, "hkee") in (0, 220):   # the hall can only upgrade with an empty queue (otherwise 185)
    g.upgrade(hall, "hkee")
p = g.production(hall)                                          # tier-up progress
if p and p.kind == "upgrade":
    print(f"Town hall needs {p.remaining:.0f} more seconds")
for sm in g.my_buildings({"hbla"}):
    if not g.queue(sm):
        ok = [u for u, v in zip(UPS, g.can_do_many([(sm, u) for u in UPS])) if v in (0, 220)]
        if ok:
            g.research(sm, ok[0])
```

### 6. Taking an expansion: pick the closest mine by walking distance

```python
mines = [m for m in g.gold_mines() if g.dist(m, home) > 1500 and not taken(m)]
best = min(mines, key=lambda m: g.path_distance(home, m) or 1e9)   # mines on islands return None -> sorted last
```

## II. Scouting and information

### 7. See what your opponent is doing

```python
for b, p in g.all_production("enemy"):          # what visible enemy buildings are training / researching / upgrading
    print(b.type, p.kind, p.queue, f"{p.progress:.0%}" if p.progress is not None else "stuck")
```

Combine with events: `ev.kind == "production.done" and ev.owner != g.me()` — what the opponent just finished.

### 8. Remember what you've seen (fog of war)

```python
for u, t, age in g.last_seen(max_age=60):       # enemies seen within the last 60 game seconds (last position and HP)
    ...
hero_seen = [r for r in g.last_seen() if r[0].is_hero]   # where the enemy hero was last seen
```

In fair mode this is your only source of information about the opponent, just like a human player.

### 9. Where the computer opponent is going to attack (computer AI only)

```python
plan = g.enemy_ai_plan(some_enemy_soldier)      # where its computer captain is heading
```

The computer picks its target point before it leaves home — bring your army back there ahead of time.

## III. Creeping

### 10. Creeping at night

```python
if g.is_night():                                 # 18:00 ~ 6:00: creeps are asleep (you get the first hit without being surrounded), everyone's vision is shorter
    ...
wait = g.seconds_until(18)                       # game seconds until nightfall (a day is 480 seconds)
```

### 11. Only attack camps you can beat

```python
from openwar3 import combat
mine = [g.stats(u) for u in army]
def ttk(target): return combat.time_to_kill(mine, g.stats(target), target_hp=target.hp) or 1e9
camp = [c for c in g.creeps() if g.dist(c, center) < 600]
ours = max(ttk(c) for c in camp)                                 # how long to clear this camp (rough estimate)
theirs = g.time_to_kill(camp, weakest_of_mine) or 1e9            # how long for them to kill our weakest unit
if ours < theirs and g.reachable(center, camp[0]):
    g.attack_move(army, camp[0].x, camp[0].y)
```

Full example: `_maybe_creep` in `micro_bot.py`.

## IV. Micro

### 12. Focus fire: attack what dies fastest, not what's closest

```python
target = min(visible_enemies, key=lambda e: g.time_to_kill(fighters, e) or 1e9)
g.attack([u for u in fighters if (g.current_target(u) or target).handle != target.handle], target)
```

Only order the units that aren't already attacking it (`current_target`), so you don't interrupt the ones that are.

### 13. Pull back wounded units

```python
for u in army:
    if u.hp < u.hp_max * 0.35:
        g.move(u, *toward(home, u, 500))       # retreat 500 toward home; don't pull the same unit again within 3 seconds
```

Detecting focus fire: in `damage` events, the same unit taking hits from multiple sources in a short window = it's surrounded.

### 14. Keep heroes alive, don't feed XP

```python
for h in g.my_heroes():
    if h.hp < h.hp_max * 0.4:
        g.move(h, home.x, home.y)
        g.use_item(h, slot_of(h, "phea"))       # healing potion: find the slot number with inventory(h)
```

### 15. Counters: the right unit on the right target

```python
s = g.stats(u)
best = max(enemies, key=lambda e: s.dps_vs(g.stats(e)))   # Riflemen on Gryphon Riders (pierce vs light armor ×2), Gryphon Riders on Footmen (magic vs heavy armor ×2)
```

The counter table comes from game data: `combat.damage_multiplier("pierce", "small") == 2.0`.

### 16. Flanks and pathing: route around towers

```python
route = g.walk_path(army_center, target)        # corner points of the shortest ground path
g.path(army, route, attack=True)                # attack-move through each point in order
```

To avoid towers, mark the area around each tower as unwalkable on the pathing grid before computing the route:

```python
grid = g.grid().copy()
for t in towers:
    grid.block_area(t.x, t.y, 800)             # tower range 700 + margin
route = grid.path((army_x, army_y), (target.x, target.y))
```

### 17. Send a tick's commands as one batch

```python
with g.batch():
    g.attack(melee, target_a)
    g.attack(ranged, target_b)
    g.move(wounded, *home_xy)
    g.cast(hero, "thunderclap")
```

Dozens of commands wait on the game thread only once (measured: 8 moves went from 68 ms to 6.5 ms).

### 18. Sieging: artillery attacks the ground

```python
g.attack_ground(mortars, tower.x, tower.y)       # Mortar Teams / Demolishers fire at an area (behind trees, invisible units)
```

## V. Heroes

### 19. Skill build

```python
SKILLS = {"Hamg": ["AHwe", "AHbz", "AHwe", "AHbz", "AHwe", "AHmt"]}   # Water Elemental, Blizzard… level-6 ultimate Mass Teleport
info = g.hero_info(h)
if info and info["skill_points"]:
    g.learn(h, SKILLS[h.type][learned_count])  # if rejected (level too low for the ultimate), wait for the next level
```

### 20. Did the spell actually go off?

```python
r = g.cast(h, "thunderbolt", target=enemy_hero)
# next tick:
if g.cooldown(h, "AHtb"):                         # on cooldown = it really went off; accepted ≠ cast
    ...
```

### 21. Buying potions, teleporting home

```python
g.buy(shop, "phea")                               # the hero is standing next to the shop
g.use_item(hero, slot, x=home.x, y=home.y)        # Scroll of Town Portal (point-targeted item use)
```

## VI. Post-game review

- Log each tick's decisions (`print` goes to the run window), and use `g.say(unit, "Fall back")` to see them in-game;
- `production.done` events include "how many seconds it took" — build your own timeline (when your first hero came out, when you tiered up) and compare it with top players';
- Let the agent review itself: see the game report in [Agent self-iteration](https://war3ai.com/en/docs/agent-loop/).
