# 偵錯與效能

> 一拍為什麼慢、命令為什麼沒生效、遊戲為什麼不動。依現象排查，再用內建的實機核對腳本確認。

來源: https://war3ai.com/zh-tw/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、世界發布週期與每次擷取耗時、快車道計數、是否在對局中、單位數、遊戲時鐘。

## 實機核對腳本

開一個測試實例，逐項核對 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
```

## 常見的「看起來像 bug」

- **蓋房子的回執接下了，卻一直沒有地基**：樹林裡的點引擎也會當場接下，工人走到了才失敗。改用 `build_near`，它會追蹤結果，並把失敗的點暫時列入黑名單。
- **技能的回執接下了，卻沒放出來**：被打斷了，或者魔力不足。放完後下一拍看 `g.cooldown()` 有沒有進入冷卻。
- **攻擊命令接下了，兵卻去打別人**：要攻擊特定目標，請用 `g.attack(兵, 敵人)`（右鍵語意）。原始的攻擊指令對目標只會更換指令、不會記住目標，於是會去打附近的其他單位。
- **工人數對不上**：進入金礦的工人不在快照裡。
- **陣亡的英雄訓練不出來**：英雄是唯一的，要用 `g.revive(祭壇)`；復活需要人口，陣亡後約 3 遊戲秒才能復活。
