# 디버깅과 성능

> 틱이 왜 느린지, 명령이 왜 먹히지 않는지, 게임이 왜 멈춰 있는지. 증상별로 점검하고 내장된 실게임 검증 스크립트로 확인합니다.

출처: https://war3ai.com/ko/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`한 내용은 Bot을 실행하는 터미널에 출력됩니다. 매 틱의 핵심 결정을 출력하고 머리 위 말풍선과 함께 보면 코드를 읽는 것보다 훨씬 빠릅니다.

## 틱이 느릴 때

먼저 다음 경우에 해당하는지 확인하세요.

| 원인 | 해결 방법 |
|---|---|
| 명령을 하나씩 보내 매번 한 프레임씩 기다림 | `with g.batch():`로 감싸면 수십 개도 한 번만 기다림 |
| `g.visible()` / `g.can_do()`를 하나씩 호출(매번 고속 레인으로 한 프레임 대기) | 가시성은 스냅샷의 `u.visible_to()`로, 실행 가능성은 `g.can_do_many([...])`로 한꺼번에 질의 |
| `on_tick` 안에서 `sleep`하거나 대기 | 게임 시간을 기록해 두고 다음 틱에 다시 판단 |
| 비싼 계산(경로 탐색, 전체 맵 스캔)을 매 틱 다시 수행 | 결과를 캐시하고 몇 틱마다 다시 계산. `g.grid()`는 자체 2초 캐시가 있고, `g.stats()`의 기술 레벨은 5초에 한 번 캐시 |

## 게임이 멈춤 / Bot이 게임 진입을 기다리기만 함

| 증상 | 대개 원인 |
|---|---|
| 계속 "게임 진입 대기" | 인스턴스 번호가 틀림. 또는 게임 창이 **최소화**됨 — 최소화하면 게임 시뮬레이션이 멈춤(시계가 흐르지 않음) |
| 게임은 돌아가는데 Bot 명령에 반응이 없음 | 다른 플레이어의 유닛에 명령 중(회신 `not_owner`). 또는 Bot이 observer 역할로 연결됨(`forbidden`) |
| 명령이 `held`됨 | 더 높은 우선순위의 레이어(레퍼런스 브레인의 반사 레이어, 콘솔의 수동 명령)가 이 유닛을 점유하고 있어 전송되지 않음 |
| 일시 정지 후에도 명령이 내려짐 | 정상: 일시 정지 중에는 엔진 시계가 멈추지만, 이벤트 디스패치는 계속 돌고 명령도 그대로 실행됨 |

## 연결해서 상태 보기

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

연결 상태를 출력합니다. 게임 pid, 월드 발행 주기와 수집 1회당 소요 시간, 고속 레인 카운터, 게임 중인지 여부, 유닛 수, 게임 시계.

## 실게임 검증 스크립트

테스트 인스턴스를 하나 띄우고, 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게임초가 지나야 부활할 수 있습니다.
