ドキュメント ガイド

プロの戦術レシピ集

上級者の強さの大半は、数十個の「小さな習慣」から生まれます。このページでは、よくあるプロの立ち回りを 1 つずつ SDK のコードに落とし込みます。どれもそのまま on_tick に貼り付けられます。

前提:g は Game、home は自軍の本拠地(g.my_buildings({"htow", "hkee", "hcas"})[0])、now = g.clock() とします。API の詳細は API カタログ、そのまま動く完全な例は サンプル Bot にあります。

LLM にある戦術を追加させるときは、「もっとプロっぽく戦って」と説明するより、該当するレシピをコードごと貼り付けるほうがずっと効果的です。

一、運営

1. 農民を遊ばせない、1 鉱山 5 人

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 つだけ、ゴールドを寝かせない

for b in g.my_buildings({"hbar"}):
    if not g.queue(b):                                       # 空になってから次を入れる
        g.train(b, "hfoo")

3. 人口で詰まらない

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 で戻す)

spot = g.build_near(w, "hbar", home.x, home.y)
if spot:
    g.gather(w, mine, queue="after")                         # 建て終わったら採掘に戻る。次のティックで探し直す必要がない

1 人の農民に続けて何棟も建てさせるには:g.build_queue(w, [("hhou", x1, y1), ("hhou", x2, y2)])。ゴールドは着工時に引かれます。

5. ティアアップのタイミング、攻防アップグレード

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"Keep 完成まであと {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. 拡張:歩行距離で最も近い鉱山を選ぶ

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. 相手が何をしているかを見る

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. 見たものを覚えておく(戦場の霧)

for u, t, age in g.last_seen(max_age=60):       # 60 ゲーム秒以内に見た敵(最後の位置と HP)
    ...
hero_seen = [r for r in g.last_seen() if r[0].is_hero]   # 相手のヒーローを最後に見た場所

フェアモードでは、これが相手の情報を得る唯一の手段です。人間のプレイヤーと同じです。

9. コンピューターの相手がどこを攻めるか(コンピューター AI にのみ有効)

plan = g.enemy_ai_plan(some_enemy_soldier)      # コンピューターのキャプテンが向かう先

コンピューターは出撃前に目標地点を決めています —— 先回りしてそこへ兵を戻しておきましょう。

三、クリープ狩り

10. 夜にクリープ狩り

if g.is_night():                                 # 18 時 ~ 6 時:クリープは眠っている(先手を取れて囲まれない)、全員の視界が短くなる
    ...
wait = g.seconds_until(18)                       # 日没まであと何ゲーム秒か(1 日 480 秒)

11. 勝てるキャンプだけを狙う

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. 集中攻撃:一番近い敵ではなく「最も早く倒せる敵」を狙う

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. 瀕死のユニットを下げる

for u in army:
    if u.hp < u.hp_max * 0.35:
        g.move(u, *toward(home, u, 500))       # 本拠地の方向へ 500 下がる。3 秒以内は繰り返し下げない

集中攻撃されているかの判定:damage イベントで、同じユニットが短時間に複数のダメージ元から攻撃されている = 囲まれている。

14. ヒーローを守る、経験値を献上しない

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. 相性:適切なユニットに適切な相手を攻撃させる

s = g.stats(u)
best = max(enemies, key=lambda e: s.dps_vs(g.stats(e)))   # Rifleman は Gryphon Rider を撃つ(Pierce 対 Small ×2)、Gryphon Rider は Footman を撃つ(Magic 対 Large ×2)

相性表はゲームデータから来ています:combat.damage_multiplier("pierce", "small") == 2.0。

16. 挟み撃ちと経路:タワーを迂回する

route = g.walk_path(army_center, target)        # 地上の最短経路の折れ点
g.path(army, route, attack=True)                # 各地点を順番にアタックムーブで通過

タワーを避けるには、経路探索グリッド上でタワーの周囲を通行不可にしてから計算します。

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. 1 ティック分のコマンドは 1 バッチで送る

with g.batch():
    g.attack(melee, target_a)
    g.attack(ranged, target_b)
    g.move(wounded, *home_xy)
    g.cast(hero, "thunderclap")

数十件のコマンドでも、ゲームスレッドを待つのは 1 回だけです(実測:8 件の移動が 68 ms → 6.5 ms)。

18. 攻城:砲で地面を撃つ

g.attack_ground(mortars, tower.x, tower.y)       # Mortar Team / Demolisher で一帯を砲撃(木立の裏、透明ユニット)

五、ヒーロー

19. スキル習得表

SKILLS = {"Hamg": ["AHwe", "AHbz", "AHwe", "AHbz", "AHwe", "AHmt"]}   # Water Elemental、Blizzard……レベル 6 の究極技は Mass Teleport
info = g.hero_info(h)
if info and info["skill_points"]:
    g.learn(h, SKILLS[h.type][learned_count])  # 拒否された(レベル不足で究極技を習得できない)ら次のレベルまで待つ

20. スキルが本当に発動したか

r = g.cast(h, "thunderbolt", target=enemy_hero)
# 次のティック:
if g.cooldown(h, "AHtb"):                         # クールダウンに入った = 本当に発動した。受理 ≠ 発動
    ...

21. ポーションを買う、帰還する

g.buy(shop, "phea")                               # ヒーローはショップの隣に立っていること
g.use_item(hero, slot, x=home.x, y=home.y)        # Scroll of Town Portal(地点指定でアイテムを使う)

六、振り返り

  • 毎ティックの判断をログに書き出し(print は実行ウィンドウに出力されます)、g.say(unit, "撤退") と組み合わせてゲーム内で確認します。
  • production.done イベントには「何秒かかったか」が含まれます —— 自分の建設タイムライン(何秒で最初のヒーローが出たか、何秒でティアアップしたか)を集計し、上級者と比較しましょう。
  • Agent 自身に振り返らせる:Agent による自律反復 の対局レポートを参照してください。