JNetHackTNG3.4.3を現代へ蘇らせる(AIの力を借りて)

2026年7月23日木曜日

t f B! P L

 



 NetHackというゲームがある。ローグライクの元祖のひとつで、@がダンジョンを潜っていくだけの、見た目だけならファミコン以下のゲームだ。攻略情報を読み込んでからが本当のスタートというタイプのゲームで、一周クリア(アセンション)するまでに数百時間かかる人もざらにいる。

 その日本語ローカライズ版がJNetHackで、さらにそれに拡張パッチを当てたのがJNetHack TNGというやつだ。3.4.3という枯れたバージョンをベースにしていて、開発は完全に止まっている。配布元のページも見ての通りの古めかしさで、文字コードすらまともに表示されない。

 ところがこれをWindows 11 + WSL2で、しかもタイルグラフィック付きで動かそうとすると、なかなかの地獄が待っていた。今回はこの復活作業をAIに手伝ってもらいながら進めた記録である。結論から言うと動いた。動いたが、かなり遠回りをした。

そもそもなぜ今NetHackなのか

 特に理由はない。強いて言うなら、大昔にプレイした記憶がふと蘇っただけだ。オタクの趣味の発火点なんてそんなものである。

 問題は、このJNetHack TNG、ビルド済みバイナリの配布がほぼ無く、ソースからのビルドが前提になっているところだ。しかもソース自体がSubversionという化石みたいなバージョン管理システムでホストされている。令和のこの時代にsvn checkoutをする日が来るとは思わなかった。

環境: WSL2 + Ubuntu

 まずはWindows 11のWSL2上にUbuntuを立てる。これは今どき鼻歌交じりでできる。

powershell
wsl --install -d Ubuntu

 ビルドに必要なパッケージも一気に入れておく。

bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential flex bison libncurses-dev subversion bzip2 git
sudo apt install -y libx11-dev libxaw7-dev libxpm-dev libxext-dev libxmu-dev libxt-dev x11-apps x11-utils

 ソースはSVNから取得する。安定版ブランチを選んだ。トランクは文字通り最新の実験版なので、初回はやめておいた方が無難だろう。

bash
cd ~
svn checkout http://svn.phys98.homeip.net/jnethack/jnethack-tng/branches/jnethack-tng-stable/ jnethack-tng
cd jnethack-tng

ビルド設定:とにかく地道にconfig.hをいじる

 NetHack系のビルドはsys/unix/setup.shを叩いてMakefileを配置するところから始まる。

bash
sh sys/unix/setup.sh

 そのあとinclude/config.hinclude/unixconf.hを編集して、X11グラフィックとタイル表示(USE_XPM)を有効化する。ここらへんはひたすらsedでコメントアウトを外していく地道な作業だ。

bash
sed -i 's|^/\* #define X11_GRAPHICS \*/|#define X11_GRAPHICS|' include/config.h
sed -i 's|^/\* #define DLB \*/|#define DLB|' include/config.h
sed -i '136s|/\*# define USE_XPM\*/|# define USE_XPM|' include/config.h

sed -i 's|^/\* #define LINUX \*/|#define LINUX|' include/unixconf.h
sed -i 's|^/\* #define TIMED_DELAY \*/|#define TIMED_DELAY|' include/unixconf.h

 src/Makefile側もX11用にライブラリとオブジェクトを追加する。特に-lXpmを足し忘れるとタイル画像が読めなくなるので注意。

bash
sed -i 's|^WINSRC = \$(WINTTYSRC)$|WINSRC = $(WINTTYSRC) $(WINX11SRC)|' src/Makefile
sed -i 's|^WINOBJ = \$(WINTTYOBJ)$|WINOBJ = $(WINTTYOBJ) $(WINX11OBJ)|' src/Makefile
sed -i 's|^WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11$|WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11|' src/Makefile
sed -i 's|^WINLIB = \$(WINTTYLIB)$|WINLIB = $(WINTTYLIB) $(WINX11LIB)|' src/Makefile

 最後にトップのMakefileを編集し、rootを介さず自分のホームディレクトリ配下($HOME/games)にインストールするようにする。setuidやchown/chgrpあたりの処理はsudoが無いと普通にコケるので、無害化しておく。

bash
sed -i 's|^PREFIX\t = /usr|PREFIX\t = $(HOME)/games|' Makefile
sed -i 's|^CHOWN = chown|CHOWN = true|' Makefile
sed -i 's|^CHGRP = chgrp|CHGRP = true|' Makefile
sed -i 's|^GAMEPERM = 04755|GAMEPERM = 0755|' Makefile

 ここまで来たらmakeしてmake install。特に難しいことは起きない。この時点でビルドは通り、実行ファイルもできる。ここまでは順調そのものだった。地獄はここから始まる。

第一の地獄:文字化け

 できあがったjnethackを実行すると、まずTTY(普通のターミナル)モードは普通に動く。ところがX11のタイル版で起動すると、メッセージウィンドウは一応日本語が出るのに、職業選択画面のボタンやステータス表示が完全な文字化けを起こす。四角い記号や意味不明な文字列の羅列で、見た目だけならバグった暗号通貨ウォレットみたいになる。

 最初はロケール(LANG=ja_JP.eucjp)を疑った。違った。次にフォント自体を疑い、日本語のビットマップフォント(shinonome)をapt installした。

 ここでもうひとつの地獄に足を突っ込むことになる。WindowsのWSL2には「WSLg」というX11・Waylandの統合機能が標準搭載されていて、追加設定なしでLinux GUIアプリがそのまま動く。便利なのだが、このWSLgのXサーバーは/mnt/wslg/distro/...という特殊な内部パスからしかフォントを読まない仕様で、apt installで普通にUbuntu側へ入れたフォントを一切認識してくれない。フォントパスを追加しようとすると

xset: bad font path element (#2), possible causes are:
    Directory does not exist or has wrong permissions

と怒られ続ける。これはWSLg側の制約であって、こちらの設定ミスではなかった。

 結論として、WSLgを諦めてWindows側で動く独立したXサーバー、VcXsrvに乗り換えることにした。

powershell
# Windows側でVcXsrvをインストール後、XLaunchを起動
# Multiple windows → Start no client → Disable access control にチェック
bash
# WSL側 ~/.bashrc
export DISPLAY=$(ip route show default | awk '{print $3}'):0.0
export LIBGL_ALWAYS_INDIRECT=1

 これでxeyesが表示されることを確認し、フォントパスの追加も試みた。しかしまたも壁にぶつかる。VcXsrvはWindows側で動くプロセスなので、WSL(Linux)側のファイルパスをそもそも認識できないのだ。仕方がないのでフォントファイル一式をWindowsから見える/mnt/c/xfontsにコピーし、Windows形式のパス(C:\xfonts)で登録した。

bash
mkdir -p /mnt/c/xfonts
cp -r /usr/share/fonts/X11/misc/* /mnt/c/xfonts/
xset fp+ "C:\xfonts"
xset fp rehash

 これでようやく日本語フォント自体はXサーバーに認識された。だが文字化けは直らなかった。

 ここでAIとソースコードを読み進めていくと、決定的な一文に行き当たる。JNetHackのX11コードには、こういう条件分岐が仕込まれていた。

c
#if defined(X11R6) && defined(XI18N)
    XtSetArg(args[num_args], XtNinternational, True);   num_args++;
#endif

 XI18Nconfig.h側で定義されていたのだが、肝心のX11R6というマクロがどこにも定義されていなかった。つまり日本語フォントセットを使う国際化コード自体が、ビルド時に一行もコンパイルされていなかったのである。半日以上溶かした文字化けの正体は、たったひとつのコンパイラフラグの欠落だった。

bash
sed -i 's|^CFLAGS = -W -g -O -I../include|CFLAGS = -W -g -O -I../include -DX11R6|' src/Makefile

 これを足して再ビルドしたところ、あっさり文字化けが直った。技術的な教訓としては「動いてるように見えて実は分岐ごと死んでいる」パターンが一番厄介ということに尽きる。

やらかし:touchコマンドで自爆する

 ちなみにこの再ビルドの過程で自分で自分の首を絞める事故も起こした。「特定のファイルだけ再コンパイルさせたい」と思い、存在しないパスに向かってtouchを打ってしまったのだ。

bash
# やってはいけない例
touch winX.c winmenu.c dialogs.c  # ← srcディレクトリにこれらの実体は無い

 NetHack系のビルドはwin/X11/のソースをVPATH経由で参照する仕組みになっており、src/直下には実体のファイルが存在しない。存在しないファイルにtouchを打つと、当然ながら空の新規ファイルが生成されてしまう。結果、ビルドはこの空ファイルを優先して読み込み、リンクは通るのに中身が空っぽの関数だらけになるという地味に厄介な壊れ方をした。

 さらに輪をかけて、原因調査中にターミナル自体が一時的にフリーズし、lsを打っても何も返ってこないという状況に陥った。「ファイルが全部消えた」と青ざめたが、これは単にターミナルの表示がバグっていただけで、新しいターミナルを開いたら何事もなくファイルは残っていた。心臓に悪い数分間だった。

 教訓:存在するかわからないファイルにtouchは打つな。オブジェクトファイルを消してクリーンビルドする方がよっぽど安全である。

第二の地獄:タイル画像が存在しない

 文字化けを退治した後、次はタイルグラフィックである。設定ファイル(JNetHack.ad)にはt32-1024.xpmというタイル画像ファイル名が指定されているのだが、これがSVNリポジトリのどこにも存在しない。よくある話で、ライセンスやサイズの都合で画像アセットだけ配布から外されているパターンだ。

 幸い、画像そのものは無くても「タイルを生成するためのソース」は残っていた。モンスターやアイテムをアスキーアートで記述したmonsters.txtobjects.txtother.txtと、それをXPM画像に変換するtile2x11というツール一式である。

bash
cd ~/jnethack-tng/util
make tile2x11

cd ~/jnethack-tng/dat
make x11tiles
# → 1153タイル、16x16ピクセルのXPM画像が生成される

 テキストファイルから絵を「コンパイル」するという発想がなかなか渋くて良い。生成された画像を、設定ファイルが期待するファイル名にリネームしてコピーする。

bash
cp x11tiles $HOME/games/games/lib/jnethackdir/t32-1024.xpm

第三の地獄:設定ファイルが読まれない

 タイルサイズの設定(32x32になっていたが実際は16x16だった)を直そうと/usr/share/X11/app-defaults/JNetHackを編集したのだが、何度直しても反映されない。appresコマンドで実際に読み込まれているリソースを確認すると、設定した値がまるごと空だった。つまりそもそもこのapp-defaultsファイルがXサーバーの検索パスに含まれておらず、静かに無視され続けていたのである。

 最終的には~/.Xdefaultsに直接設定を書き込み、xrdb -mergeで強制的に反映させる力技で解決した。

bash
cat ~/jnethack-tng/win/X11/JNetHack.ad >> ~/.Xdefaults
xrdb -merge ~/.Xdefaults

 こうしてやっと、タイル絵付き・日本語表示の職業選択画面と、ジャッカルに噛みつかれる自キャラの姿を拝むことができた。感無量である。

完成:ワンクリック起動化

 ここまで来ると、もう「毎回Ubuntuのターミナルを開いてコマンドを打つ」のが面倒になってくる。人間は一度快適さを覚えると後戻りできない生き物だ。というわけでWSL側に起動スクリプトを、Windows側にバッチファイルを用意して、ダブルクリックひとつでVcXsrvの起動からゲームの立ち上げまで完結するようにした。

bash
# ~/start_jnethack.sh
#!/bin/bash
export DISPLAY=$(ip route show default | awk '{print $3}'):0.0
export LIBGL_ALWAYS_INDIRECT=1
cd ~/games/games
./jnethack -window x11
batch
:: jnethack.bat (Windows側)
@echo off
tasklist /FI "IMAGENAME eq vcxsrv.exe" 2>NUL | find /I /N "vcxsrv.exe">NUL
if "%ERRORLEVEL%"=="1" (
    start "" "C:\Program Files\VcXsrv\vcxsrv.exe" -multiwindow -clipboard -wgl -ac
    timeout /t 3 /nobreak >NUL
)
wsl -d Ubuntu -- bash -lc "~/start_jnethack.sh"

つまずきポイント早見表

症状原因対処
メッセージ・メニューが全部文字化けX11R6マクロ未定義でfontSet国際化コードが丸ごとコンパイルされていないCFLAGS-DX11R6を追加して再ビルド
フォントパスをXサーバーに追加できないWSLg・VcXsrvともにWSL側のLinuxパスをそのままでは読めないVcXsrvに乗り換え、フォントを/mnt/c/経由・Windowsパスで登録
設定ファイル(.ad)を配置しても反映されないapp-defaultsの検索パスに含まれていなかった~/.Xdefaults + xrdb -mergeで直接反映
タイルが表示されずテキスト文字のままx11tilesの実体がSVNに同梱されていないtile2x11でテキスト定義から自前ビルド
ビルドが謎の空ファイルで壊れる存在しないsrc/*.cにtouchして空ファイルを新規作成してしまったwin/X11/*.c側を触る。事故った空ファイルは削除してクリーンビルド

まとめ:AIとの共同デバッグについて

 今回の作業、正直ひとりでやっていたら心が折れていたと思う。文字化けの原因ひとつとっても、ソースコードの条件分岐の奥深くに埋まっていたマクロの欠落にたどり着くまで、地道な切り分け作業の連続だった。ロケールを疑い、フォントを疑い、Xサーバーの実装差異を疑い、最終的にソースコードそのものを読みに行く。この過程をAIと一緒に、出力ログを貼っては次の仮説を立て、また貼っては絞り込む、というやり取りで進めていったわけだが、これがなかなか快適だった。

 特に良かったのは、こちらが出したエラーメッセージやターミナルのスクリーンショットひとつひとつから、次に確認すべきコマンドを的確に返してくる点だ。人間相手のペアプログラミングだと「とりあえず一晩寝かせよう」となりがちな類の地道な切り分け作業を、根気強く付き合ってくれる。もちろんこちらのtouchコマンドの事故を防げなかった点はご愛嬌としても、その後の復旧までちゃんと導いてくれたので差し引きプラスだったと思う。

 20年以上前のソースコードを、令和のWindows機の上で、AIの力を借りて日本語タイル表示付きで動かす。趣味の物好き具合としてはSeikoの中華ムーブメントを弄るのと同じ種類の遊びだが、こちらは工具の代わりにターミナルとにらめっこするタイプの遊びだ。同じようにJNetHack TNGを蘇らせたい酔狂な人がいれば、この記事がそのまま手順書として使えるはずである。あとは各自の環境差分(フォントの有無やVcXsrvのインストール先パスなど)と気合で乗り切ってほしい。

 さて、蘇らせたのはいいものの、まだアセンションはおろかMinesの入り口にすら到達していない。この記事を書いている場合ではなかったかもしれない。


最後に この記事はAIが作成してくれました。

実際に我々が辿った道をなぞって、私の文体を真似して書いて、と指示しただけでコレが出てくる。強すぎて草。

自己紹介

自分の写真
弱いオタク.日々苦しむ.

人気の投稿

QooQ