# Книга рецептов: приёмы профи

> Преимущество сильных игроков по большей части складывается из десятков «мелких привычек». На этой странице типичные приёмы профессиональной игры по одному переложены на код SDK — каждый фрагмент можно сразу скопировать в on_tick.

Источник: https://war3ai.com/ru/docs/cookbook/

Соглашения: `g` — это `Game`, `home` — наша главная база (`g.my_buildings({"htow", "hkee", "hcas"})[0]`), `now = g.clock()`. Подробности об интерфейсах — в [каталоге API](https://war3ai.com/ru/api/), полные рабочие примеры — в разделе [Примеры ботов](https://war3ai.com/ru/docs/examples/).

> **Совет**
>
> Когда просите LLM добавить какой-то приём, вставьте ему соответствующий рецепт вместе с кодом — это работает гораздо лучше, чем просьба «играть более профессионально».

## I. Экономика

### 1. Рабочие никогда не простаивают, по 5 на рудник

```python
for w in g.idle_workers():                                   # отправляем только простаивающих (повторный приказ занятым прервёт добычу)
    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])
```

Сколько рабочих отправлено на каждый рудник, считайте сами (`crew`): рабочие внутри золотого рудника в снимок не попадают. Полный пример — `hello_bot.py`.

### 2. В очереди только 1 юнит — деньги не замораживаются

```python
for b in g.my_buildings({"hbar"}):
    if not g.queue(b):                                       # следующего ставим, только когда очередь пуста
        g.train(b, "hfoo")
```

### 3. Никогда не упираться в лимит пищи

```python
stuck = any(p.blocked for _b, p in g.all_production("me"))  # в очереди есть, но не началось = не хватает пищи
res = g.resources()
if stuck or res["food_cap"] - res["food_used"] <= 6:
    g.build_near(builder, "hhou", home.x, home.y)
```

`blocked` срабатывает на шаг раньше, чем «почти заполнено»: потеряли в бою пачку войск, начали доукомплектовываться — и как только очередь встала, вы сразу об этом узнаёте.

### 4. Порядок постройки + возврат на рудник после стройки (возврат через Shift)

```python
spot = g.build_near(w, "hbar", home.x, home.y)
if spot:
    g.gather(w, mine, queue="after")                         # достроит и вернётся добывать — искать его в следующий тик не нужно
```

Один крестьянин строит несколько зданий подряд: `g.build_queue(w, [("hhou", x1, y1), ("hhou", x2, y2)])`. Деньги списываются только в момент начала стройки.

### 5. Когда улучшать ратушу, улучшения атаки/брони

```python
if not g.queue(hall) and g.can_do(hall, "hkee") in (0, 220):   # улучшать можно, только когда очередь ратуши пуста (иначе 185)
    g.upgrade(hall, "hkee")
p = g.production(hall)                                          # прогресс улучшения ратуши
if p and p.kind == "upgrade":
    print(f"Ратуше осталось {p.remaining:.0f} с")
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. Экспансия: выбираем ближайший рудник по пешему расстоянию

```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)   # для рудника на острове вернётся None -> в конец списка
```

## II. Разведка и информация

### 7. Что делает противник

```python
for b, p in g.all_production("enemy"):          # что нанимают / исследуют / улучшают видимые здания противника
    print(b.type, p.kind, p.queue, f"{p.progress:.0%}" if p.progress is not None else "застряло")
```

Вместе с событиями: `ev.kind == "production.done" and ev.owner != g.me()` — что противник только что получил.

### 8. Помнить всё, что видели (туман войны)

```python
for u, t, age in g.last_seen(max_age=60):       # враги, замеченные за последние 60 игровых секунд (последняя позиция и здоровье)
    ...
hero_seen = [r for r in g.last_seen() if r[0].is_hero]   # где в последний раз был вражеский герой
```

В честном режиме это ваш единственный источник сведений о противнике — как и у живого игрока.

### 9. Куда собирается атаковать компьютер (только против компьютерного AI)

```python
plan = g.enemy_ai_plan(some_enemy_soldier)      # куда направляется его компьютерный капитан
```

Компьютер выбирает точку атаки ещё до выхода из базы — заранее стяните туда войска.

## III. Крипинг

### 10. Крипинг ночью

```python
if g.is_night():                                 # с 18:00 до 6:00 крипы спят (можно ударить первым и не попасть в окружение), обзор у всех короче
    ...
wait = g.seconds_until(18)                       # сколько игровых секунд до темноты (сутки = 480 с)
```

### 11. Нападать только на лагеря, которые вам по силам

```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)                                 # сколько времени уйдёт на весь лагерь (грубо)
theirs = g.time_to_kill(camp, weakest_of_mine) or 1e9            # за сколько они убьют нашего самого слабого
if ours < theirs and g.reachable(center, camp[0]):
    g.attack_move(army, camp[0].x, camp[0].y)
```

Полный пример: `_maybe_creep` в `micro_bot.py`.

## IV. Микроконтроль

### 12. Фокус огня: бить того, кто умрёт быстрее всех, а не ближайшего

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

Приказ отдаётся только тем, кто «ещё не бьёт эту цель» (`current_target`) — не прерывайте тех, кто уже атакует.

### 13. Отводить раненых

```python
for u in army:
    if u.hp < u.hp_max * 0.35:
        g.move(u, *toward(home, u, 500))       # отойти на 500 в сторону базы; не отводить повторно в течение 3 секунд
```

Как понять, что юнит под фокусом: в событиях `damage` один и тот же юнит за короткое время получает урон от нескольких источников = его окружили.

### 14. Берегите героев, не дарите опыт

```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"))       # зелье лечения: номер ячейки ищите через inventory(h)
```

### 15. Контры: нужные войска — по нужным целям

```python
s = g.stats(u)
best = max(enemies, key=lambda e: s.dps_vs(g.stats(e)))   # Стрелки по всадникам на грифонах (колющий по лёгкой броне ×2), грифоны по пехоте (магический по тяжёлой броне ×2)
```

Таблица контр берётся из игровых данных: `combat.damage_multiplier("pierce", "small") == 2.0`.

### 16. Окружение и маршруты: обходим башни

```python
route = g.walk_path(army_center, target)        # точки поворота кратчайшего наземного пути
g.path(army, route, attack=True)                # атака с движением через каждую точку по порядку
```

Чтобы обойти башни, пометьте область вокруг них в сетке поиска пути как непроходимую и посчитайте маршрут заново:

```python
grid = g.grid().copy()
for t in towers:
    grid.block_area(t.x, t.y, 800)             # дальность башни 700 + запас
route = grid.path((army_x, army_y), (target.x, target.y))
```

### 17. Команды одного тика — одной пачкой

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

Десятки команд ждут игровой поток всего один раз (измерено: 8 команд движения — 68 ms → 6.5 ms).

### 18. Осада: артиллерия бьёт по земле

```python
g.attack_ground(mortars, tower.x, tower.y)       # мортиры / катапульты стреляют по участку земли (за лесом, по невидимым юнитам)
```

## V. Герои

### 19. Порядок прокачки

```python
SKILLS = {"Hamg": ["AHwe", "AHbz", "AHwe", "AHbz", "AHwe", "AHmt"]}   # Элементаль воды, Снежная буря… на 6-м уровне ульта — Массовая телепортация
info = g.hero_info(h)
if info and info["skill_points"]:
    g.learn(h, SKILLS[h.type][learned_count])  # отказ (уровня не хватает для ульты) — ждём следующего уровня
```

### 20. Сработало ли заклинание

```python
r = g.cast(h, "thunderbolt", target=enemy_hero)
# в следующий тик:
if g.cooldown(h, "AHtb"):                         # ушло на перезарядку = действительно применено; «принята» ≠ «применено»
    ...
```

### 21. Покупка зелий, возврат на базу

```python
g.buy(shop, "phea")                               # герой стоит рядом с магазином
g.use_item(hero, slot, x=home.x, y=home.y)        # свиток телепортации на базу (предмет, применяемый в точку)
```

## VI. Разбор партии

- Каждый тик пишите решения в лог (`print` попадает в окно запуска), а вместе с `g.say(юнит, "Отходим")` смотрите их прямо в игре;
- событие `production.done` содержит «сколько секунд заняло» — соберите свою хронологию построек (на какой секунде первый герой, на какой секунде улучшение ратуши) и сравните с сильными игроками;
- пусть агент сам разбирает партии: см. отчёт о партии в разделе [Автономные итерации агента](https://war3ai.com/ru/docs/agent-loop/).
