# Отладка и производительность

> Почему тик медленный, почему команда не сработала, почему игра стоит. Ищите по симптомам, а затем подтверждайте встроенными скриптами проверки в реальной игре.

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

## Смотрите на квитанцию

Квитанция каждой команды — информация из первых рук:

```python
r = g.cast(hero, "blizzard", x=tx, y=ty)
if not r:
    print(r.reason, r.verdict)     # rejected（…） и код причины
print(r.exec_us, r.engine_us)      # сколько микросекунд команда выполнялась в игровом потоке / сколько из них заняла сама функция приказа движка
```

Обычно команда занимает в игровом потоке от нескольких до нескольких сотен микросекунд. После завершения блока пачки в `g.last_receipts` лежат квитанции всех команд этой пачки.

## Смотрите прямо в игре

```python
g.say(unit, "Отходим")          # над юнитом появляется облачко чата (на игру не влияет)
g.message("Начинаем крипинг")   # строка в области сообщений внизу слева (видна только на этом компьютере)
```

Вывод `print` появляется в терминале, где запущен бот. Печатайте ключевые решения каждого тика и добавьте облачка над юнитами — так разобраться гораздо быстрее, чем читая код.

## Тик слишком медленный

Сначала проверьте, не ваш ли это случай:

| Причина | Решение |
|---|---|
| Команды отправляются по одной, и каждая ждёт кадр | Оберните их в `with g.batch():` — десятки команд ждут только один раз |
| `g.visible()` / `g.can_do()` вызываются по одному (каждый вызов идёт через быструю полосу и ждёт кадр) | Видимость берите из снимка через `u.visible_to()`; выполнимость спрашивайте пачкой через `g.can_do_many([...])` |
| `sleep` или ожидание внутри `on_tick` | Запомните игровое время и проверьте условие на следующем тике |
| Дорогие вычисления каждый тик (поиск пути, сканирование всей карты) | Кэшируйте результат и пересчитывайте раз в несколько тиков. У `g.grid()` встроенный кэш на 2 секунды, уровни технологий в `g.stats()` кэшируются на 5 секунд |

## Игра стоит / бот не дожидается начала матча

| Симптом | Скорее всего |
|---|---|
| Бесконечное «ожидание матча» | Неверный номер экземпляра; или окно игры **свёрнуто** — пока оно свёрнуто, симуляция стоит (часы не идут) |
| Игра идёт, а на приказы бота нет реакции | Приказы идут чужим юнитам (квитанция `not_owner`); или бот подключён как observer (`forbidden`) |
| Команды возвращают `held` | Юнита держит слой с более высоким приоритетом (рефлекторный слой эталонного мозга, ручной приказ из консоли), команда не отправлена |
| На паузе команды всё равно проходят | Это нормально: на паузе часы движка стоят, но диспетчеризация событий работает и команды выполняются как обычно |

## Подключиться и посмотреть статус

```bash
python -m openwar3 status --inst 5
```

Выводит состояние подключения: pid игры, период публикации мира и время каждого сбора, счётчики быстрой полосы, идёт ли матч, число юнитов, игровые часы.

## Скрипты проверки в реальной игре

Запустите тестовый экземпляр и по пунктам проверьте, что возможности SDK работают на вашей машине:

```bash
python tools/sdk_live_check.py --inst 20                 # всё
python tools/sdk_live_check.py --inst 20 --only prod     # только один раздел
```

Разделы: пачки, время, производство, очередь приказов, боевые характеристики, поиск пути, честный режим. Каждый раздел отдаёт команды в настоящем матче, считывает результат и печатает число пройденных проверок.

Для офлайн-тестов игра не нужна:

```bash
python tools/run_tests.py
```

## Частые «вроде бы баги»

- **Квитанция на строительство — «принята», а фундамента всё нет**: точку в лесу движок тоже сразу принимает, а срывается всё, когда рабочий дойдёт до места. Используйте `build_near`: он следит за результатом и на время заносит неудачные точки в чёрный список.
- **Квитанция на способность — «принята», а способность не применилась**: её прервали или не хватило маны. На следующем тике после применения проверьте по `g.cooldown()`, началась ли перезарядка.
- **Приказ атаки принят, а бойцы бьют кого-то другого**: для атаки конкретной цели используйте `g.attack(боец, враг)` (семантика правого клика). Сырой приказ атаки на цель меняет только приказ, но не запоминает цель — и юнит пойдёт бить кого-то рядом.
- **Не сходится число рабочих**: рабочих внутри рудника нет в снимке.
- **Погибшего героя не удаётся натренировать**: герой уникален, нужен `g.revive(алтарь)`; воскрешение требует пищи и доступно лишь примерно через 3 игровые секунды после гибели.
