プロの戦術レシピ集
上級者の強さの大半は、数十個の「小さな習慣」から生まれます。このページでは、よくあるプロの立ち回りを 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 による自律反復 の対局レポートを参照してください。