DCSS qw botの中身を探る

この記事は Roguelike Advent Calendar 2016 の8日目の記事です。

■設定ファイル、使っていますか?

DCSSの設定ファイルには様々なオプションが設定でき、知っておくとゲームを円滑に進める助けになります。

配布版ならsettings/init.txtを編集するなり、Webtileなら(edit rc)のリンクを押して編集画面を出すなりしましょう。

よく使うところでは

 # 余分な --more-- をスキップ
 show_more = false
 # スキル割り振りをマニュアルモードで開始
 default_manual_training = true
 # HPが30%以下になると警告(デフォルトは10%)
 hp_warning = 30

でしょうか。

設定ファイル中ではLua言語によるスクリプトも使うことができ、

 : if you.race() == "Ogre" or you.race() == "Troll" then
 # オーガとトロルは大岩を拾う
 autopickup_exceptions ^= <large rock
 : end

のように場合分けで設定をすることもできます。便利!

■DCSS qw bot

本題です。

DCSS開発チームの一人elliptic氏が、DCSSを自動的にプレイしてくれるbotを公開していました。

(やや古いバージョンがDCSS本体のリポジトリツリーにも含まれています。*1 )

はて、いったいどういう仕組みで動くものか? とりあえず中身を見てみました。

すると仰天、qw botの本体は7700行にも及ぶ設定ファイルqw.rcだったのです。

■動かしてみよう

botのお手並み拝見ということでローカルで動かしてみます。READMEにはオンラインサーバでの動かし方も書かれていますがあまり推奨しません(特にトーナメント中は)。

https://github.com/elliptic/qw/ のページの"Clone or download"を押すと"Download ZIP"というボタンが出てくるので、ボタンを押してqw-master.zipをダウンロードします。

zipを展開するとその中にqw.rcというファイルがあるので、それをDCSSのsettingsディレクトリにコピーします。

settings/init.txtを編集し、末尾に

include = qw.rc

追記します。

qw.rcの先頭には各種設定がありますが、以下の項目でキャラクターの初期組み合わせを指定します。

# 闇ドワーフ戦士斧、ガーゴイル狂戦士斧、ミノタウロス狂戦士斧のどれかでスタート
combo = DDFi.waraxe, GrBe.handaxe, MiBe.handaxe

ゲームを開始すると自動的にqwというキャラ名でゲームがスタートしています。Tabを押してbot運行モードに切り替えると、あとはSpaceやその他のキーを押しっぱなしでゲームが進みます。'%'キーでいつもの通りステータスや装備が確認できます(反応しない場合は何度か押してください)。手動操作に戻したい場合は再度Tabを押します。

■どんな感じ?

良い点

けっこう賢いです。敵の群れを狭い通路まで釣ったり、階段昇降して敵を分散させたり、適切に武器防具を交換したりをこなしているようです。店が出たら買い物もします。

(トログ信仰の場合)ここぞというところでバーサークして相手を殲滅したり、トログの御手でMR上昇&回復したり、仲間を召喚したりで適切に進んでくれます。まあ、トログ信仰(とそれを最初から得ている狂戦士)が強いってのもありますが。

デフォルトの組み合わせ(闇ドワーフ戦士斧、ガーゴイル狂戦士斧、ミノタウロス狂戦士斧)での序盤性能は高く、ルーンを拾う所まで進むこともしばしばです。というか私より強いんじゃね?

悪い点

悪手というか機能の限界というか、以下のような動きが見られます。

  • 魔法は使えない。暗殺ビルドもできない様子(短剣スタートでも斧に持ち替えたり)
  • エルフの大広間/Elven Hall、地下墓地/Cryptには寄らない。
  • ポータルへ寄らない(下水道/Sewerの入口を選択するが行かないような動きをする)と思いきや、納骨堂/Ossuaryに入ったことはあった。
  • 指定された神の祭壇があっても入信しないことが多々ある。
  • アビスに飛ばされた際、出口が見つかっても直行しない。
  • オーブを拾った後もZot:5の未探索領域をうろうろする。階段に直行しない。
  • (バーサークが切れた状態でまだ囲まれているなど)危機に陥った際にテレポ巻で逃げる傾向がある。
    • 運が良ければいいが、敵のまっただなかに突っ込んでしまうことがあるため普通は悪手。
  • 沼の特殊マップでルーンの上にflame cloudが湧いている場合、その場でうろうろを続ける。
    • それを避けるようなコードが書かれている形跡はあるが、うまく働いていない様子。
    • まあ、一旦手動に戻せばいいのだけど。
  • アーティファクト片手斧が下賜された/自然生成された時でも無視することがある。

■どんな仕組み?

最近流行りの人工知能……というわけでもなく、基本は「列挙された行動リストの中にある行動を適宜条件判定して実行」という形ですね。

6983行目から行動リストの定義が乗っています。以下抜粋です。

plan_pre_explore = cascade {
  {plan_fly, "fly"},
  {plan_ancestor_life, "ancestor_life"},
  {plan_sacrifice, "sacrifice"},
  {plan_upgrade_weapon, "upgrade_weapon"},
  {plan_maybe_upgrade_armour, "maybe_upgrade_armour"},
  {plan_use_good_consumables, "use_good_consumables"},
} -- hack

-- (中略)

plan_emergency = cascade {
  {plan_cure_starving, "cure_starving"},
  {plan_cure_confusion, "cure_confusion"},
  {plan_coward_step, "coward_step"},
  {plan_remove_terrible_jewellery, "remove_terrible_jewellery"},
  {plan_teleport, "teleport"},
  {plan_dd_recharge_teleport, "dd_recharge_teleport"},
  {plan_cure_bad_poison, "cure_bad_poison"},
  {plan_drain_life, "drain_life"},
  {plan_heal_wounds, "heal_wounds"},
  {plan_cloud_step, "cloud_step"},
  {plan_hand, "hand"},
  {plan_resistance, "resistance"},
  {plan_heroism, "heroism"},
  {plan_bia, "bia"}, -- 注: Brother in Arms
  {plan_sgd, "sgd"}, -- 注: Summon Greater Demon
  {plan_divine_warrior, "divine_warrior"},
  {plan_apocalypse, "try_apocalypse"},
  {plan_slouch, "try_slouch"},
  {plan_hydra_destruction, "try_hydra_destruction"},
  {plan_grand_finale, "grand_finale"},
  {plan_dd_recharge_heal_wounds, "dd_recharge_heal_wounds"},
  {plan_wield_weapon, "wield_weapon"},
  {plan_swap_weapon, "swap_weapon"},
  {plan_water_step, "water_step"},
  {plan_finesse, "finesse"},
  {plan_drac_dig, "try_drac_dig"},
  {plan_berserk, "berserk"},
  {plan_other_step, "other_step"},
} -- hack

ある意味愚直ともいえるのですが、それでもこの方式でいい感じに動き、あまつさえクリア直前まで進行可能というのは驚きです。

Tabを押すとbot運行モードがオンオフされるのは、元々Luaスクリプト(dat/clua/autofight.lua)で組まれていたhit_closest関数(Tabを押したら最寄りの敵を殴る機能)を乗っ取る形で実装されているからですね。なるほど。

■まとめ

  • 設定ファイルは便利
  • というかミノ・ガゴ・闇ドワに狂戦士反則すぎでは?
  • Luaスクリプトで無限の可能性……宇宙……

次の記事はdisさんの「DCSS和訳プロジェクト作業日誌」です。