# 회신과 사유 코드

> 모든 명령의 회신에는 상태 코드와 사유 코드가 담깁니다. Bot과 Agent가 스스로 오류를 고치는 근거로, "왜 안 됐는가"를 기계가 읽을 수 있는 숫자로 바꿔 줍니다.

출처: https://war3ai.com/ko/docs/reason-codes/

```python
r = g.train(barracks, "hfoo")
bool(r)        # False
r.status       # 1              -> rejected
r.verdict      # 3              -> 식량 부족
r.reason       # 'rejected（人口不够）'   = 식량 부족
r.exec_us      # 이 명령이 게임 스레드에서 실행된 시간(마이크로초)
```

`if r:`은 `r.status == 0`(엔진이 수락함)과 같습니다.

## 상태 코드 `status`

| 코드 | 이름 | 의미 | 흔한 원인 |
|---|---|---|---|
| 0 | `accepted` | 엔진이 수락함 | —(단, 수락 ≠ 완료, 아래 참고) |
| 1 | `rejected` | 엔진이 거부함 | `verdict` 확인 |
| 2 | `bad_unit` | 유닛이 없거나 핸들이 맞지 않음 | 유닛이 이미 죽음, 만료된 유닛 객체를 사용함 |
| 3 | `not_owner` | 내 유닛이 아님 | `player` 역할로 다른 플레이어의 유닛을 지휘함 |
| 4 | `fault` | 실행 중 예외(런타임이 이미 막아 두어 게임을 무너뜨리지 않음) | 재현 단계와 함께 제보해 주세요 |
| 5 | `bad_args` | 인수 오류 | 좌표, 칸 번호, 4자리 코드를 잘못 씀 |
| 6 | `unsupported` | 지원하지 않음 | 이 버전의 런타임에 해당 기능이 없음 |
| 7 | `bad_target` | 잘못된 대상 | 대상이 이미 사라짐, 대상 유형이 맞지 않음 |
| 8 | `forbidden` | 레인 역할상 허용되지 않음 | `observer` 역할로 명령함 |
| 97 | `cancelled` | 배치 블록에서 예외가 발생해 배치 전체가 전송되지 않음 | `with g.batch():` 블록 안의 코드에서 오류 발생 |
| 98 | `held` | 더 높은 우선순위의 레이어가 유닛을 점유하고 있어 전송되지 않음 | 레퍼런스 브레인의 반사 레이어나 콘솔의 수동 명령이 이 유닛을 점유 중 |
| 99 | `timeout` | 시간 초과 | 게임 일시 정지 / 랙으로 마감 시간을 넘김(만료된 명령은 다시 실행되지 않음) |

## 사유 코드 `verdict`

거부되면 런타임이 엔진 자체의 실행 가능성 검사로 이유를 알려 줍니다. 명령을 내리지 않고 먼저 물어볼 수도 있습니다. `g.can_do(유닛, 4자리코드)`는 같은 코드를 반환합니다.

| 코드 | 의미 | 대처 |
|---|---|---|
| 0 / 220 | 가능 | — |
| 3 | 식량 부족 | 식량 건물 건설. `g.production(b).blocked`로 미리 감지 |
| 8 | 금 부족 | 돈이 모일 때까지 대기. 명령 전에 `g.can_afford(code)` 사용 |
| 9 | 목재 부족 | 벌목 인원 추가 |
| 32 | 훈련 대기열 가득 참(7칸) | 대기열에는 1개만: `g.queue(b)`가 비면 다음 것을 넣기 |
| 183 | 선행 기술 / 건물 부족 | 선행 건물을 먼저 짓거나 티어 업 |
| 185 | 건물이 사용 중 | 제단에서 영웅 부활 중. 본진 건물 대기열이 비어 있지 않으면 업그레이드 불가 |
| 221 | 해당 항목 없음 / 건설 중 / 업그레이드 중 / 이미 존재 | 영웅이 이미 있음(죽었으면 `revive`). 이 상점은 해당 품목을 팔지 않음 |
| 89 | 상점에 아직 입고되지 않음 | 게임 시작 후 아이템 테이블의 입고 시간이 지나야 재고가 생김. 새로 지은 상점은 완공 시점부터 계산 |
| 1001 | 대상이 보이지 않음 | 대상이 전장의 안개나 검은 마스크 안에 있음. 그 위치로 `attack_move` |

## 수락 ≠ 완료

회신은 "엔진이 이 명령을 받아들였다"는 것만 알려 주며, 같은 프레임 안에서 읽어 온 결과입니다. 그 뒤에 일어날 수 있는 일까지는 알 수 없습니다.

| 명령 | 회신이 수락된 뒤에도 실패할 수 있는 경우 | 확인 방법 |
|---|---|---|
| 건설 | 숲속 지점도 그 자리에서는 수락되고, 일꾼이 도착해서야 실패 | `build_near` 사용(건설 부지가 나타나는지 추적), 또는 `production.done` 대기 |
| 시전 | 끊김, 마나 부족 | 다음 틱에 `g.cooldown(u, 스킬)`이 쿨다운에 들어갔는지 확인 |
| 훈련 | 대기열에 들어갔지만 식량이 부족해 계속 시작되지 않음 | `g.production(b).blocked` |
| 이동 / 공격 | 다른 로직(또는 더 높은 우선순위의 레이어)이 덮어씀 | `g.current_target(u)`, `g.order_of(u)` |

## 조회 API

아래 API는 명령을 내리지 않고 엔진에 묻기만 합니다. 결과는 회신의 `value`에도 담깁니다(SDK는 값을 바로 반환합니다).

| API | 반환 |
|---|---|
| `g.can_do(u, code)` / `g.can_do_many([(u, code), ...])` | 위 표의 사유 코드 |
| `g.tech(code, player=None)` / `g.tech_many([...])` | 연구 레벨 / 완성된 건물 수(업그레이드 체인 포함) |
| `g.visible(x, y)` | 이 지점이 아군에게 보이는지 |
| `g.gold_left(mine)` | 금광에 남은 금 |
| `g.enemy_ai_plan(적 유닛)` | 컴퓨터 상대의 부대장이 병력을 이끌고 어디로 가려는지(컴퓨터 AI에만 유효) |
