expectでJNetHackをリセマラする

## はじめに
Expect の manpage を見ると、Expect ができることの例として以下があります。
> ゲーム(例えば rogue)を始める時に、最適なパラメタがもらえなかった場合、最適なパラメタがもらえるまで何度でもリスタートを行ない、その後制御を人間に移す。

昔 JNetHack で遊んでいたとき、主にWizard(魔法使い)を選んでいまして、
初期装備にいい杖やアイテムが出るまでQuit＆再実行を繰り返す所謂リセマラを
地道に手動でやっていた事を思い出しました。~~ヒマ~~贅沢な時間の使い方だなと。

汝、今こそこれを自動化せよ

なるほど、やってみましょう。

## JNetHack のリセマラスクリプト
CentOS7 ＋ nethack-367-src.tgz + jnethack-3.6.7-0.1.diff.gz の tty 環境で実行しました。
ここでは ~/nh/install/games/jnethack にインストールされたものとします。
※デフォルトでのインストール場所は、昔と今では全然違うんですね。

```tcl:jnh.ex
#!/usr/bin/expect

set timeout 1

# お好みの 職業・種族・性別・属性を設定してください
set JOB    w  ; # 職業(w - 魔法使い)
set RACE   h  ; # 種族(h - 人間)
set GENDER *  ; # 性別(* - ランダム)
set ATTR   n  ; # 属性(n - 中立)

# ---------------------------------------------------------------
# お望みのパラメータ・所持品を CONDITION に記載してください
#
# 強早耐知賢魅のうち、強知賢が11以上を希望する例
# set CONDITION ".*強:1\[1-9\].*知:1\[1-9\].*賢:1\[1-9\].*"
#
# 所持品を正規表現で記載する例 (今回はこれを使う)
set CONDITION ".*(回復|識別)の魔法書.*(変化|無力化)の杖.*魔法のマーカ.*"
#
# 組み合わせる場合は、パラメータ->所持品の順序で記述してください
# set CONDITION ".*賢:1\[1-9\].*魔法のマーカ.*"
# ---------------------------------------------------------------

while { 1 } {
    spawn ~/nh/install/games/jnethack
    expect "ynq"
    send "n"             ; # 職業,種族,性別,属性を自動選択しない
    expect {
        "職業を選んでください" {
            send $JOB    ; exp_continue
        }
        "種族を選んでください" {
            send $RACE   ; exp_continue
        }
        "性別を選んでください" {
            send $GENDER ; exp_continue
        }
        "属性を選んでください" {
            send $ATTR   ; exp_continue
        }
        "これでよい" {
            send "y"     ; exp_continue
        }
        "More" {
            # Space2回押す＋所持品を確認する
            send "  i"
            expect {
                -re $CONDITION {
                    interact
                    exit 0
                } default {
                    send "\003 "  ; # Ctrl+c と space 1回
                    expect {
                        "本当にやめる" {
                            send "y" ; exp_continue
                        }
                        "識別しますか" {
                            send "q"
                        }
                     }
                }
            }
        }
    }
    expect eof
    catch wait
}
```
魔法使いは初期装備で強力な杖や魔法のマーカがもらえる(こともある)と
嬉しいので、このスクリプトでは希望の所持品の条件を正規表現で記載して、
出るまでひたすら spawn を繰り返し、出たら interact する、としました。

他の職業だとどういうのがいいんでしょうね。

所持品ではなく、良パラメータを求める場合は、
```tcl
set CONDITION ".*強:1\[1-9\].*知:1\[1-9\].*賢:1\[1-9\].*"
```
のようにすればよいのでしょう。※文法上、[ ] の前にはバックスラッシュが必要です。
当然ですが、絶対出ない値やアイテムを条件にすると、永遠にプレイ開始できません。

それでは、スクリプトを実行しました。
```
                                                                   武器
                                                                   a - 祝福された+1 六尺棒(手にしている)
                                                                   鎧
                                                                   b - 祝福された+0 魔法を防ぐクローク(身につけている)
                                                                   巻物
                                                                   i - 呪われていない地図の巻物
                                                                   j - 呪われていない解呪の巻物
                                                                   k - 呪われていない罰の巻物
                                                                   魔法書
                                                                   l - 祝福された衝撃の魔法書
                                                                   m - 呪われていない識別の魔法書
                                                                   薬
                                                                   f - 呪われていない超回復の薬
                                                                   g - 呪われていない水
                                           --+----------           h - 呪われていない回復の薬
                                           |.......{....           指輪
                                           |....@......|           d - 呪われていない耐毒の指輪
                                           |....f...$..|           e - 祝福された +1 体力の指輪
                                           -------------           杖
                                                                   c - 変化の杖(0:7)
                                                                   道具
                                                                   n - 魔法のマーカ(0:44)
Megadriver 手品師              強:9 早:16 耐:15 知:18 賢:8 魅:9 中 o - 呪われていない目隠し
地下:1 $:0 体:12(12) 魔:7(7) 鎧:9 経験:1                           (end) 
```
しばらく待つと、このように所望の品が含まれた初期状態でスタートできました。

末尾の `wait` はプロセスの終了を待つ処理です。
wait しない場合、以下のようにゾンビプロセス（defunctの行）が残るための対策です。
```terminal:wait なしの場合の ps auxf 抜粋
megadri+ 25188  0.2  0.0 134200  3120 pts/1    Sl+  21:55   0:00      \_ /usr/bin/expect ./jnh.ex
megadri+ 25190  0.0  0.0      0     0 ?        Zs   21:55   0:00          \_ [jnethack] <defunct>
megadri+ 25195  0.0  0.0      0     0 ?        Zs   21:55   0:00          \_ [jnethack] <defunct>
megadri+ 25205  0.0  0.0      0     0 ?        Zs   21:55   0:00          \_ [jnethack] <defunct>
megadri+ 25209  0.0  0.0      0     0 ?        Zs   21:55   0:00          \_ [jnethack] <defunct>
megadri+ 25213  0.0  0.0      0     0 ?        Zs   21:55   0:00          \_ [jnethack] <defunct>
megadri+ 25217  0.0  0.0      0     0 ?        Zs   21:55   0:00          \_ [jnethack] <defunct>
megadri+ 25221  0.0  0.0  17704  2236 pts/2    Ss+  21:55   0:00          \_ /home/megadriver/nh/install/games/lib/jnethackdir/jnethack
```

## さいごに
- 久々にプレイしましたが、懐かしさと新鮮さが入り交じっており、JNetHack の面白さは
昔以上でした。鉄棒(#)ってなんなの？
- スクリプト作成よりも何倍もの時間を、ついつい始めたプレイに費やしてしまいました
- 初期装備が多少よくても結局リトライしまくるのですが、初期アイテムが良いゆえに
止めどきを見失いがちになります（自制心がさがった！）

## 関連サイト
- The NetHack Archive ( [https://www.nethack.org/download/](https://www.nethack.org/download/) )
- JNetHack プロジェクト日本語トップページ ( [https://ja.osdn.net/projects/jnethack/](https://ja.osdn.net/projects/jnethack/) )