# 職業打法食譜

> 高手的優勢大多來自幾十個「小習慣」。這一頁把常見的職業打法逐條落實到 SDK 程式碼上，每段都能直接貼進 on_tick。

來源: https://war3ai.com/zh-tw/docs/cookbook/

約定：`g` 是 `Game`，`home` 是我方主基地（`g.my_buildings({"htow", "hkee", "hcas"})[0]`），`now = g.clock()`。API 細節見 [API 目錄](https://war3ai.com/zh-tw/api/)，完整可執行的範例在 [範例 Bot](https://war3ai.com/zh-tw/docs/examples/)。

> **提示**
>
> 要讓 LLM 加入某種打法時，把對應的那一招連同程式碼一起貼給它，比描述「打得職業一點」有效得多。

## 一、運營

### 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 -> 排到最後
```

## 二、偵察與資訊

### 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)      # 它的電腦隊長要去的地方
```

電腦出門前就決定好了目標點 —— 提前把兵帶回那裡。

## 三、打野

### 10. 夜裡打野

```python
if g.is_night():                                 # 18 點 ~ 6 點：野怪睡覺（先手不被包圍）、所有人視野變短
    ...
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)
```

完整範例：`micro_bot.py` 的 `_maybe_creep`。

## 四、微操

### 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)       # 迫擊砲 / 投石車對一塊地方開火（樹林後面、隱形單位）
```

## 五、英雄

### 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)        # 回城卷軸（對點使用物品）
```

## 六、復盤

- 每拍把決策寫進日誌（`print` 會輸出到執行視窗），搭配 `g.say(單位, "撤")` 在遊戲裡查看；
- `production.done` 事件帶有「花了多少秒」—— 統計自己的建造時間軸（第幾秒出第一個英雄、第幾秒升級主城），和高手的比較；
- 讓 Agent 自己復盤：見 [Agent 自主迭代](https://war3ai.com/zh-tw/docs/agent-loop/) 裡的對局報告。
