ドキュメント はじめに

最初の Bot

10 行の最小 Bot から始めて、農民の生産、人口の確保、兵の生産、ヒーロー、出撃を加え、最後にレシートの読み方を理解します。

Bot とは openwar3.Bot を継承したクラスのことです。必要なフックだけをオーバーライドすれば、「見る」と「やる」は g(Game)が担当します。

最小の Bot

from openwar3 import Bot

class MyBot(Bot):
    def on_start(self, g):          # 試合に入った後に 1 回呼ばれる
        g.message("参戦します")

    def on_tick(self, g):           # 毎秒約 5 回
        for w in g.idle_workers():
            g.gather(w, g.nearest(g.gold_mines(), w))
python tools/play.py --bot my_bot.py

手の空いた農民が最寄りの金鉱へ向かいます。フックは 4 つです。

フック呼ばれるタイミング
on_start(g)試合に入った後、最初のティックの前に 1 回
on_tick(g)毎ティック(デフォルトで毎秒 5 回)。1 ティックが時間を超えると自動的に後ろにずれ、どんどん溜まっていくことはありません
on_event(g, ev)毎ティックの on_tick の前に、前回のティック以降のイベントを 1 件ずつ渡します
on_end(g, reason)試合が終わったとき(ゲームプロセスがなくなった / 自軍のユニットがいなくなった / 手動で停止した)に 1 回

on_tick で例外が発生しても試合全体は中断されません。ランナーがスタックトレースを表示し、次のティックから続行します。20 ティック連続でエラーになったときだけ停止します。

経済を加える:農民を作り、人口を確保する

from openwar3 import Bot

class Economy(Bot):
    def on_tick(self, g):
        res = g.resources()                       # 読み取れないときは 0 ではなく None
        halls = g.my_buildings({"htow", "hkee", "hcas"})
        if res is None or not halls:
            return
        home = halls[0]

        # 1. 手の空いた農民はゴールドを採掘
        for w in g.idle_workers():
            mine = g.nearest(g.gold_mines(), w)
            if mine:
                g.gather(w, mine)

        # 2. 農民を作る:キューには 1 つだけ入れる(いっぱいにするとゴールドがキューにロックされる)
        if len(g.my_workers()) < 15 and not g.queue(home):
            g.train(home, "hpea")

        # 3. 人口がもうすぐ上限:建設中でない農民を見つけ、本拠地の近くに Farm を建てる
        if res["food_cap"] - res["food_used"] <= 6:
            builder = next((w for w in g.my_workers() if not g.is_constructing(w)), None)
            if builder:
                g.build_near(builder, "hhou", home.x, home.y)

注目すべき書き方が 3 つあります。

  • g.queue(home) が空のときだけ訓練する。 毎ティック訓練命令を出すと 7 スロットのキューが埋まり、ゴールドがロックされます(実測:本拠地のキューに農民が 4 人入り、300 ゴールドがロックされて、序盤が大きく遅れました)。
  • 座標を決め打ちせずに build_near を使う。 近いところから順に置ける場所を探し、ティックをまたいで結果を追跡します。ゴールドが足りないときは何もしません。決め打ちの座標は、ちょうど木立の中かもしれません。
  • 建設中の農民は選ばない。 ヒューマンの Farm は建設に 35 秒かかり、途中でワーカーを別の場所へ送ると基礎の工事が止まります。

4 種族すべてで動く完全版は brains/examples/hello_bot.py です:1 鉱山 5 人、鉱山がいっぱいなら伐採、工事が止まった基礎の建設再開まで行います。

Barracks、ヒーロー、出撃を加える

from openwar3 import Bot

WAVE = 8

class Rush(Bot):
    def on_start(self, g):
        self.attacking = False

    def on_tick(self, g):
        halls = g.my_buildings({"htow", "hkee", "hcas"})
        if not halls:
            return
        home = halls[0]

        # ヒーロー:祭壇があってヒーローがいない -> まず復活を試み、復活できなければ訓練する(ヒーローは唯一なので、死んだ後に訓練し直そうとすると拒否される)
        altars = g.my_buildings({"halt"})
        if altars and not g.my_heroes():
            if not g.revive(altars[0]):
                g.train(altars[0], "Hpal")
        for h in g.my_heroes():
            info = g.hero_info(h)
            if info and info["skill_points"]:
                g.learn(h, "AHhb")                # Holy Light

        # Barracks から Footman を出し続ける(キューには 1 つだけ)
        for b in g.my_buildings({"hbar"}):
            if not g.queue(b):
                g.train(b, "hfoo")

        # 1 波分たまったら出撃し、壊滅したら本拠地に戻る
        army = g.my_army()
        if len(army) >= WAVE:
            self.attacking = True
        elif len(army) < WAVE // 2:
            self.attacking = False
        if self.attacking:
            target = g.nearest([e for e in g.enemies() if g.is_building(e)], home)
            if target:
                idle = [u for u in army if not g.order_of(u)]      # 手の空いたユニットにだけ命令する
                g.attack_move(idle, target.x, target.y)

完全版は brains/examples/rush_bot.py を参照してください(hello_bot を継承し、Barracks / 祭壇がなければ建てます)。

レシートを読む

すべてのコマンドはレシートを返します。if r: が「エンジンが受理した」という意味で、受理されなかった場合は r.reason に理由が入っています。

r = g.train(barracks, "hfoo")
if not r:
    print(r.reason)        # rejected(人口不够)(= 人口不足)
    print(r.verdict)       # 3

よく見る理由コード:3 人口不足、8 ゴールド不足、9 木材不足、32 キューがいっぱい、183 前提条件が足りない、221 その項目がない / 建設中 / ヒーローがすでにいる、1001 ターゲットが見えない。全一覧は レシートと理由コード を参照してください。

受理 ≠ 成功

レシートが示すのは「エンジンがこのコマンドを受け付けた」ということだけです。木立の中の建設地点もエンジンはその場で受理し、ワーカーが到着してから失敗します。スキルは中断されることもあります。結果はスナップショットとイベントで確認してください:建物は build_near で建て(基礎が現れたかを追跡してくれます)、スキルは g.cooldown() がクールダウンに入ったかを見ます。

次のステップ