ローグライク攻略&開発雑記

コンシューマー/PCに限らず、適当に綴っていくブログ 無駄にあれこれ遊んだので要らん知識だけは豊富

2020年12月

変愚蛮怒 3.0.0 進捗状況 特別編 その3 C#への可能性

この記事は Roguelike Advent Calendar 2020 19日目の記事です

こんにちは、Hourierです
今日は数ヶ月前に実験した結果をお送りします

皆さんは新しいセーブデータを作る時、どれくらい時間をかけますか?
その中でも特に、ダイスロール部分にどれくらい時間をかけますか?

ダイスロールの結果は以下のような感じです。
基本値の合計が86の時に理論上最悪値になります (87以上はそもそも永遠に停止しない)
以下の画面は複雑な計算に基づくもの値なので少数第1位未満切り捨てですが、ここでは見えている値をそのまま採用します
ダイスロール

さて、上のパラメータが実現する確率はいくつでしょうか?
答えは0.021^4 * 0.992^2 == 0.000000191381750784 ≒ 191ppb です
平均すると520万回に1回程度の確率で成功することになります
ところが実際にこのダイスロールを回すと、その100倍即ち5.2億回以上回してもぶっちゃけ出ないです
更に10倍の52億回でも出ない時は出ないです
これは単純な確率にして3.27*10^(-44)となり、「明日地球が滅びる確率とどっちが高いか」というレベルの低確率です
はて、そんな低確率がどうしてぽんぽこ発生するのだろうか?
これが第1の疑問として湧き上がりました

そこでコードを調査したところ、「HP」「身長」「隠密」等の値も全部ダイスロールで判定していることが判明しました
これ自体は毎回値が変わるので自明ですが、自明でないのはここからです

各種パラメータは「平均値と標準偏差を基に正規分布となるようにダイスを回す。出た値が規定範囲内に収まっていたらパスする」という動きをしていました
変愚蛮怒のダイスロールは、最終的なダイスロールに現れる結果を見る感じ、以下の要素から成ります (コードレベルで他に何をやっているのかも多少調べましたが、全部は調べきれませんでした)
  • 年齢
  • 身長
  • 体重
  • 社会的地位
  • 打撃修正
  • 射撃攻撃修正
  • AC (肉体のACなのか装備品の修正なのかは不明)
  • HP
  • レベル50時の最大HP (マスクデータ)
  • MP
  • レベル50時の最大MP (マスクデータ)
  • 所持金
  • 打撃命中
  • 射撃命中
  • 魔法防御
  • 隠密行動
  • 知覚
  • 探索
  • 解除
  • 魔法道具
  • 掘削
  • ライフレート (マスクデータ)
  • 各能力の最大値 (18/70 ~ 18/130) ← 多分これは一発で決まる
なんと最後を除いても22パラメータです
正規分布のテンプレを当てはめ、仮に全部「68.3%の確率 (偏差値で言えば40~60)でダイスロールをパスする」と仮定すると、「約1/4400」です
これらを加味すると「1/229億」!
いい感じの数字が出てきました!
ちなみに上記の数値は特別な条件 (狂戦士の隠密等)がない限りマイナスにはならないはずなので、実際の確率は70%台前半かそれ以上と思われます
そう考えるとそう外れた数値でもないでしょう

さて、こんな低確率のダイスロールをどうやってキビキビ動かすか?
これが第2の疑問点です
ようやくタイトル回収です、ここで「C言語」ではなく「C#」を使うとどうなるのか!

ということで(?)MultiThreadAutoRollerというツールを開発しました
単なるダイスロールシミュレータです
但し、「PCに搭載されたCPUスレッドを全部ぶん回してロールしまくる」というイカれたツールです
ここでは正規分布とか細かいことは考えず、「1/42億 のガチャを引くのに何秒かかるかの計測ツール」として作りました
これを使うと何秒くらいかかるでしょうか?

HourierのPCでは「最速1秒未満、最遅40秒程度」です!!
何分経っても出ないダイスロールもこれなら余裕ですね!
化け物級CPUをお持ちの方は最遅記録と共にお声がけ下さい!

ここまで爆速化できた理由は2つあります:
  • C言語ではマルチスレッド処理がほぼ不可能 (*1)だが、C#ならサクサク作れた
  • 画面描画を10fpsに落とす (*2)ことによって、ダイスロール方向にリソースを全力で割いた
これを叩き台にしつつ、今後Unity 2Dで作るとかそんな話になった時にガッツリ実装して、今の 度し難い 名状しがたき バールのようなもの コードをキレイにまとめ上げて行きたい所存です
それがいつになるかは全くの未定ですが、そうできたら嬉しいなという取り掛かりの報告と致します

明日は記者・題目ともに未定です……が、何か来ると思います
お楽しみに♪

*1 C言語の標準機能では今でも不可能だと思われます (調査不足かも)。そして現実問題、変愚のダイスロールはシングルスレッドで動作しています。OSごとに最適化されたマルチスレッド処理用ヘッダは多分ありますが、そうするとWindows用/OS X&Linux用/BSD用とプリプロがえげつないことになるので実装する気も保守する気もないです

*2 今の変愚蛮怒にも「Escを押すとその瞬間のパラメータ一覧を表示する」という機能があります。しかし事実上目押し不可能なフレームレートで基本値が目まぐるしく変わるので、10fpsでも60fpsでも事実上変わりません。ついでにいうとフレームレートを落とすためのコードが見当たりませんでした……

変愚蛮怒 3.0.0 進捗状況 特別編 その2 C言語の闇

こんにちは、Hourierです
今日は愚痴記事です、ユーザー側にも開発側にも有用な情報はありません

度々IRC #ぐりっどばぐ でも語られる、「もうCで書くの辞めね?」という議題について、個人的に考えていることをつらつらと述べていこうと思います
この記事はあくまで私見であることを強調しておきます

*bandの歴史
ここを読むような人は百も承知かもしれませんが、変愚蛮怒は以下のような経過を辿ってきました
UMoria (1987) → Angband (1990) → Zangband (1994) → 変愚蛮怒 (2000)

ところが、ネットに現存する複数の文献を漁ったところ、どうもこんな分かりやすい進化の過程は辿っていないようです
そもそもTopi Ylinen氏がZangband 1.0を作ったのは「Angband--」という、今となっては名前しか伝わらない古いAngbandのフォーク、及び「PC Angband」 (同じく詳細不明、恐らくUNIXでしか動作確認を取っていなかった旧Angbandに対し、DOSでも動くように各種調整を施したもの)からだったようです
なおこのヘンテコな名前は、「C++」と「NetHack--」から取られたようです (NetHack-- も名前と概要しか現存せず詳細不明)
そして「Angband--」に「新世界アンバー」を始めとするゼラズニイ作品群のモンスターがあって、Topi氏が上手いことZangband 2.1.0にまとめ上げたようです
この時、フォーク元をAngband 2.8.1 ('93/12)に変更したとのことです

当時のコードは変愚蛮怒v2.2.1時点でもプリプロセッサ「ANGBAND_2_8_1」という形で残っており、信じられないですがコードの上は25年に亘って後方互換性を維持していたことになります (*1)
但し、変愚蛮怒自体が(十数年前までのある時点までは)Ang/Zangからパッチを当て続けており、変愚自体にもあれこれセーブデータのフォーマットを改造し続けてきた歴史があります
当時のセーブデータを読めるかどうかは頗る怪しいのと、いい加減変愚蛮怒が独自路線を歩き始めてからの年数を考え、プリプロを全部削除しました
次の変愚蛮怒v3.2.0では、Zangband時代及び変愚蛮怒v1.X時代のセーブデータ互換性を消す予定です
今更10年前のセーブデータを引っ張り出して続きをプレイしようという人はいないだろう、ましてやZangband時代のセーブデータを変愚蛮怒にインポートする人は皆無だろう、という判断もあります

話をZangbandに戻します
残念なことに当時のソースコードは失われており、Topi氏がメンテナを離れた後のv2.1.1が、現存する最古のZangのソースコードです
続くZang v2.2.0の辺りで、パワーワイアームやサイバーデーモンといったモンスターが実装され、現在約1280体いるモンスターの内、約850体がこの時点で策定されました
Zang v2.1.1では583体が実装されていたので、約270体が新規に実装されたことになります
なおZang v2.1.1の時点で既に『モルゴス』が『サーペント』に置き換わってリストラされていたり、そもそもAng v2.8.1とモンスター番号に様々な食い違いがあるので、相当量の調整がなされたものと想定されます
Zang内のバージョンアップでも頻繁に若番にモンスターを追加するなど、当時の基準で考えても相当ドラスティックな修正がなされました
当時から既にモンスター個別のハードコードも色々あったはずですが、頑張って直したのでしょうきっと
そして変愚蛮怒前夜となるZang v2.2.8では870体です
引き算して400体程が変愚蛮怒にて新規実装された計算になります
但し、フォーク後もパッチをあれこれ取り込んできた歴史の関係上、変愚の新規実装はもう少し少なめかもしれません

さてここで、850体いるモンスターをMS-DOS等の16ビットOSで扱うことは不可能になり、この時Zangは32ビットOS専用になりました
各種64ビットOS版への対応は、longをintに再defineするという荒業によって達成されています (*2、3)
float/doubleは変愚v3.0.0現在でも1箇所も使われていません、「浮動小数点演算を扱えない環境でもコンパイルできて遊べるよ!」とかいう状態です
そんなゴミOSはもうこの世に存在しないか博物館用のレトロ機なので、いい加減何とかしたいです
70年代ですらIntel 8087があったというのに、90年代にもなって整数演算専用のコンピューターってどこにあったんでしょうか

んで何が問題かと言うと、「そんな状況なのに2020年3月までMS-DOS上で動かすためのコードが何故か現存していた」ということです
具体的には「main-dos.c」というファイルがありました
プリプロも数箇所と呼ぶには多すぎる程度に残っていました
16ビットOSへのサポートが打ち切られた時期と、その後の歴史を振り返ってみると、申し訳ない話ですが歴代担当者は怠慢だったとしか言いようがないです
時は97年、DOSはまだしも、80年代のSystem III UNIXとの互換性なんてどうでも良かったはずです
ましてや2020年にもなってその時代の業務用OSでゲームしようという人は酔狂そのものです
これが「クソコード」です、世のクソコードは変愚に比べれば半分以上が大したことないです (*4、5)

そして変愚蛮怒へと続きますが、Angband 2.Xのどこかと、Zangband 2.2.Xのどこかで激しいリファクタリングが行われた他は、変愚蛮怒v2.1.4までずっと手つかずでした
90年代のリファクタリングといってもどうやら「ソースコードを綺麗にする作業」にはほとんど使われず (それでも「差分がコード本体よりも大きい」と言われるほど大量にあったようですが)、「OSごとの互換性向上」や「画面描画周りの改善 (今と違ってグラフィックドライバの世界はカオスそのものだった:VRAMに直接ドットの色指定をしたりとか)」が中心だったようです
「int (4バイト)は贅沢だ、char (1バイト)を使え」「変数を5つ定義するなんて贅沢だ、1つだけ定義して限界まで使い回せ」「関数スタックは少なければ少ないほどいい、即ち関数1つの行数は極限まで長くしろ」という感覚が凄く色濃く残っていたのと、リファクタリングという概念がまだ浸透していなかったので、これ自体は仕方のないことなのかもしれません
この辺りは現メンテナであるdeskull氏のブログに詳しいです
他にも、現在のソースコードは700を超えまだまだ増える余地があるのですが、仮に当時ここまで分割しても大量のファイルを一度にコンパイルできる環境に乏しかったというのも1つあります
「出社直後」「昼休み」「帰社直前」の3回しかコンパイルするチャンスがなかった昭和なら時間も重要だったでしょうが、そんな時代はとっくに過ぎ去りました
現在のソースコードは、最近のマルチコアCPUが全力を出せばまぁ困らない程度に緩和されています

結局どうなったかと言うと、「魔法効果を表す関数がメイン処理だけで2400行」「直接攻撃用の関数が2500行」とかになりました
「火炎属性のボールが着弾したら周囲の木が燃える」とか「死の大鎌で攻撃をミスると種族に応じた倍率で攻撃が跳ね返る」とか、明らかに「その名前の関数で一括処理すべき内容でない処理」が大量に紛れ込むことになりました
我慢の限界、というか読むのが不可能だったので全部分解しました
v3.0.0は見違えるくらい綺麗になりました
もはや「魔法効果を表す関数 project()」は、それらのあらゆる処理の起点に過ぎず、effect/ 以下のファイルに魔法効果ごとに分類しています

さて、今までの変愚蛮怒は、UMoria時代のそれを受け継いで全てC言語で書いてあります
昔は「言語XXで書いたMoria/Angband」なんてのがいくつもあったようですが、現在観測範囲の全ては途絶しています
何故Cが生き残ったかというと、「どんなOSでも絶対コンパイラが用意されている言語」となるとC (と、精々C++)しかないからです
AndroidアプリはJavaのコードしか受け付けないですが、「Android上でCを書くソフト」は出回っているので、これで無理やり構築することが可能です
ところが流石に「ゲームをCで組む」なんてのは前時代過ぎます
Cは元々OSを書くために作られた言語なので、コンパイルの互換性はともかく言語自体がゲームに向いていません

何が起きているか?
変愚蛮怒、引いてはAngbandの時代から、player_type構造体というものが存在します
これは名前やMAX HP/現在 HPを始め、二重耐性の残りターンやら突然変異、抽出したエッセンスに習得した青魔法と何でもござれの超巨大構造体です
昔はグローバル変数として10000箇所とかいうレベルで参照されまくっていましたが、現在は70箇所未満です
どうしたのというと「ほぼ全ての関数に引数でplayer_typeを引き回している」となっています
あまりに長い間見続けたので感覚が麻痺していますが、明らかにおかしいです
「グローバル変数か関数の引数、ちょっと頑張ってstaticグローバル変数」しか選択肢のないCでここまでやるのは苦痛以外の何者でもありません
また、現在コードは「エレメント攻撃」「精神攻撃」等でまとめていますが、これは「ファイアボール (自分の魔法)」と「ファイアボール (敵の魔法)」と「焼棄エゴ」と「耐火の薬」と「バルログの自前二重耐性」の処理が完全に分離していてお互いに関係がないことを意味しています
元々は1つの関数にずらーっと並べていたことを考えると、「とにかく分けて何をしている処理なのか初見でも理解度が上がった」って意味では進歩していますが、理想形ではありません
上記の例で言えば、本質的には「火炎属性」であることが大事なのですが、火炎属性にまつわる諸々がパッと一目で分かるような手段はv3.0.0現在も存在しません

まさにこのために「オブジェクト指向設計」なるものが登場したんだなと思わざるを得ない構造のコードになっています

なお現在のコードは「とにかく今あるコードを短い (5~50行)関数に分離し、更に関数群を200~600行程度でファイルに分離し、更にファイルを6~20個程度でフォルダに分離する」をひたすら繰り返した結果です
完全にCっぽいというか手続き型設計のそれになっています
(それっぽく改造する方法はなくもないですが)どう足掻いてもCはCのままなのでこの問題は丁重に放置します
そもそもオブジェクト指向的に書いたところで関数名オーバーロードもインターフェースクラスもないのでほぼ無意味です
とはいえ「具体的にどうまとめるの?」となるとまた難しい問題が色々発生しますが……

ということで、今後はC++/C#及びゲーム開発エンジンであるUnityをベースにしていこうかしら、なんて話がちらほら出ています
C++は既にUMoria (2020年版!!)やD'angbandで実装実績がありますが、どこまでASCIIベースの描写で頑張っていくかは非常に難しいところです
タイルとかそういう問題ではなく、Windows APIを直接叩くという現在の変愚蛮怒は無茶ぶりが過ぎます (Win10は「もっと抽象化された画面描画APIを叩け」という方針でOSが作られています。どんなOSもその流れは変りません)
そう考えた時、Visual C++で画面設計も含めて頑張るよりも、最初から画面描画及びそのためのライブラリが充実しているC#で開発したいなと思っているところです
オブジェクト指向設計は銀の弾丸にはなりえませんが、少なくとも現在の「行数こそまともだけど結局処理が分散している」という、今あるものの延長線でしかないリファクタリングではなく、もっと抜本的に変える必要性がある、十分な時間があるならやりたい、というところまで言語切替の議論は来ています
まだ妄想の域ですが「こんな設計で作りたい」という話もいくつかIRCでは出ました
幸いにしてC#の基盤となる.NET 5はOS X、各種Linuxにも対応できる幅広いフレームワークです
.NET 5自体のソースコードも公開されているので、その気になればどんなOSにでも実装できます (*6)

ここには書ききれないくらい、例えば「near/farポインタの撲滅」とか「gotoの撲滅」とか「全ての変数を関数の先頭で宣言する文化 (1972~1988)の撲滅」とか……
要するに「学校どころか会社に残っている最古コードですら見たことがないクソの山」を崩すのに半年以上かかりました
あ、会社の最古コードはニーモニックとデータが同一のFF3状態コードだわ
りり

後は何をしたでしょうかねぇ
何の意味もないシンタックスシュガー×18を1つの関数に統合するとか、無秩序に絡み合ったobject.h (実はこれでも分割済:元は魑魅魍魎ヘッダ)をobject1.h/object2.h に分割する (しかもこの後object-*.h へ再分割)とか、グローバル変数を呼んでる関数マクロを絶滅させるとか、どうやったらここまで放置できるのっていうレベルのゴミ屋敷を片付ける作業が半年以上続いていました、はい
deskull氏は偉大すぎます、良くこんなコード構造で機能を追加できたもんだ……
ちなみにゲーム上重要な構造体であるmonster_typeもmonster_raceも、それどころかplayer_typeすら最初はどこにあるか不明でした
今はsystem/*-definition.h に大型の構造体は全部詰まってます
ここまで可読性を上げられるチャンスを、大負けに負けてVistaリリースからの14年放置するって、歴代担当者の目は全員節穴だったんでしょうか

さて、次は明るい話でもしようかと思います
ゲーム開発言語としては将来性マイナスのC言語を捨てて、C#へ乗り換えていこうという話題です
お楽しみに!

*1 一方で「ハードウェア乱数ジェネレータ」という謎のプリプロは現役です。Linuxの/dev/random を指している可能性もありますが、多分UMoria/Angband時代にガチの乱数発生器 (こんなの)が組み込まれたメインフレームがあったんではないかと推測しています。良く分からないので放置中です

*2 C言語の規格自体が超絶怪しいことになっていて、16どころか8ビットOS時代から存在する言語なので、荒業を駆使すれば色々できます。現在は全てintにしてしまっても良いはずですが、longが宣言されている箇所が多すぎて全然手が伸びていない状態です

*3 それはそれとしてWindowsにはWOW64という機能があって64ビット版OSでもほぼ平然と32ビット版のソフトを動かせます。Linuxも似たような仕組みが構築されています。OS XやAndroidでは一工夫必要なようですが……

*4 「互換性が凄い!」がウリではあったんですが、今やC言語の処理系は一部の画面描画を除き統一されているのでもう気にする必要はありません

*5 エアコン等の組み込み系・医療機器系は、最高50年くらい秘伝のソースを継ぎ足して、「リファクタリング禁止」「デッドコード削除禁止」「新規の関数は定義禁止」→ よって1関数数万行とかいうケンシロウ やZeroやrufもびっくりの修羅の国らしいので、私は絶えきれずに精神崩壊します 俺の妹が真っ赤なお鼻のトナカイさんなわけがない

*6 但し.NET自体を非対応OSに移植するのは死ぬほどめんどくさいです、BSDはまだやってる途中と思われます

開発者になろう

この記事は Roguelike Advent Calendar 2020 15日目の記事です

こんにちは、Hourierです
もうタイトルのビッグウェーブに乗り遅れた感満載ですが、改めて変愚蛮怒の作り方について説明しようと思います
もしバリバリ開発したい人がいたら、多分この記事を読むまでもないと思います
今後どこかで新しい人と巡り合う時、何かの助けになればと思い、この記事をしたためておきます

「わかんねーよばか」って方は筆者のツイッターまで!
手取り足取り 腰取り 教えますよ!

Liveアカウントの取得
まずはMicrosoftアカウントを取得しましょう
ここに手順が載っています
まぁ昨今のM$さんはアカウントがないとWindowsすらまともに動かせなくなるように縛りつつあるので、大抵の人は持ってると思います

Visual Studio Communityのインストール
続けて開発ソフトのインストールです
多分これが一番めんどくさいんじゃないかと思います
Visual Studioの公式HPに行って、Community版をダウンロードします
無料ですよ無料! いやあいい時代になったもんです
石油王の方はProfessionalでもEnterpriseでもお好きなものを購入すればOKです!!

ダウンロードして起動すると下図のような画面が出てくると思います
「画面ちげーぞこのやろう」って可能性も多少あるのでその時はコメントで教えて下さい!
チェックの入っている「C++によるデスクトップ開発」、これが重要です
ここにチェックを入れます

VisualStudioInstaller


次に[個別のコンポーネント] へ行きましょう
筆者の環境では他にやりたいことあるので色々チェックが入っていますが、変愚を作るだけなら以下のチェックだけでOKです
  • Windows 10 SDK (取り敢えず最新版)
  • NuGetパッケージマネージャー
  • 依存関係の検証
後は[変更] とか[インストール] とか書いてあるボタンを押してしばらく待つだけです
回線によってはかなり時間がかかるかもしれないので気長にお茶でもどうぞ

Gitのインストール
続けてバージョン管理ツール (VCS)「Git」をインストールします
ダウンロードはここです
インストールは特に何も考えずに次へ次へでOKです
「何年何月何日何時何分何秒 地球が何回回った時 に誰がどこを何行編集したか」が全て記録されます
その編集記録1つ1つを「コミット」と呼びます
一番古いコミットはmogamiさんのZAngbandからフォークした瞬間ですかね
(注:過去ログを漁る限り、これ以前にも何かうごうごしている形跡がありました)

もちろん変愚より更に巨大で黒歴史の長い コミット回数の多いソフトだと、全コミットをダウンロードするとディスク容量的にも操作の重さ的にも死ねるんですが、幸いにして変愚はそこまででもありません

LimeChatのインストール
ここに来た人なら1回は聞いたことがあるだろう「IRC」に接続するソフトです
必須ではありませんが、開発者アカウントはIRCで担当者に発行してもらうのが一番ラクです
ここからDLどうぞ~

ちなみに最近は #ぐりっどばぐ でも変愚開発系の話題が (主に筆者のせいで)中心ですが、その時その時流行ってるゲームだのアニメだのでテキトーに盛り上がっております
面白いネタをお持ちの方は変愚関係なくどうぞどうぞ!
私は週に1~2回、夜頃出没してます
別に毎日入る必要はないですからね、そういう気楽さが良いところです


リポジトリのクローン
さてここからが本題です
どこか開発用のフォルダを決めて下さい
筆者は「D:\Hengband_Development」とかそんな感じのフォルダにしてます
以下このフォルダを「./」と呼びます

フォルダの適当なところで右クリックすると「Git Bash Here」があるはずなのでそれを選択します
すると以下のような画面が出てきます
コマンドプロンプトみたいですが、アレより高機能です

以下のコマンドをコピペして下さい、最後のドットも忘れずに
git clone https://scm.osdn.net/gitroot/hengband/hengband.git .

次に「./Hengband/Hengband.sln」をダブルクリックします
そうすると以下のような画面が表示されると思います
VS画面

続けて「Hengband」を右クリック→NuGetパッケージの管理→復元をクリックします
この復元作業は最初の1回だけでOKです

さぁ後はコードをガリガリ書きましょう!
書き終えたら右上の「Hengband」を右クリックして「ビルド」を選択すればハイ、できあがり!!

……って簡単にできたら楽なんですけどね(しろめ
お作法が色々あるんですが、ここに書くには余白が少なすぎます
我こそはという方、ブロコメでもツイッターでもIRCでも、お好きな場所で質問して下さいっ
案外手探りでも何とかなるもんです!

明日はさぼりっこ様の「しまった!シラミを増殖し過ぎて捌き切れない…」の予定です
どうぞお楽しみに!

変愚蛮怒 3.0.0 進捗状況 特別編 その1 歴代ソースコードとの比較

この記事は Roguelike Advent Calendar 2020 12日目の記事です

こんにちは、Hourierです
本日は「リファクタリングはしまくったけど、その成果とは何だったのか?」を調査・報告します
しかしある程度都合のいい情報ばかり並べている部分もあります
手前味噌な話ですがご了承下さい

変愚蛮怒は「いつ・誰が・どのように・何回」加筆・変更・修正を実施したかというログが残っています
変更1回のことをコミットと呼びます
まずはこれを調べてみましょう

12/08現在、release/3.0.0Alphaには11,356回のコミットが存在します
その内約7600回が、v2.2.1リリース後にコミットされました
全体の2/3が、旧称v2.2.2、現v3.0.0のためだけに使われています
なおその内約3600回、即ちv3.0.0の約47%、全体の約31.5%が筆者によるコミットです
注:2000~2002年頃はコミット制度が確立していなかったので不明です

歴史を紐解いたところ、2004年10月~2009年3月の間は15回しかコミットがなく、2014~2017年も低調でした
一方v2.2.0がリリースされた2017年9月以降はかなりのハイペースであり、特に今年の1~8月だけで約4500コミットがなされるなど、極めて活発に推移しました
この一部、約700+α回のコミットは、英語版兼Mac OS X版の製作者であるEric Backwards氏のリソース修正 (スペルミス、文法ミス、英語として違和感のある文章等)によるものです

そしてそれを除いた3.0.0向けのコミット6900回の大半、感覚で言えば6000回がリファクタリング即ちコードの整理に使われました
Zangband時代から受け継いだ遺産がいかに膨大かつ不毛なものであったかを物語っています
これについては「コードの闇編」でご紹介します

さてここでは、SourceMonitorというソフトを使って、単純な行数ランキングを作ってみます
ステップ数 (行数 - 空行・カッコ・コメント等処理ではないもの)でもほとんどその傾向は同じなので省略します

今回は以下の4種類を見比べます
v2.1.4 (全くリファクタリングがなされていない時点)
v2.2.0 (少しずつ始まった時点)
v2.2.1時点 (途上の地点)
最新コミット (リファクタリングほぼ完了)

コード長ランキング
v2.1.4v2.0.0v2.2.1v3.0.0-Alpha
9717971797184088 (※1)
8930892688723618 (※2)
8812883788572647 (※2)
7947801880212331
7647762876271762 (※2)
7420742074201646
6903691268771425 (※2)
6755675567661368 (※2)
6737674467461341 (※2)
6440656065601247

※1 v3.0.0で解体予定
※2 v3.2.0で解体予定

見ての通り、もうすぐ10000行の大台という圧倒的な長さを誇るファイルがありました (do-spell.c)
私が参画した時点でも7000行以上のファイルがあって、5000行以上のファイルがそこら辺にゴロゴロしていました
それが最新では、まだ分割途中のplayer-status.c ですら4000行ちょっと、つまり半分以下です
その他は大半が3000行未満となっています
10位に至ってはリファクタリング前の1/5です ほめてほめてー

続けて、ファイル数及び関数数を比べてみましょう

関数リスト

v2.1.4v2.0.0v2.2.1v3.0.0-Alpha
ファイル数102
98
98
1588
関数数877
886
906
2985
関数/ファイル8.60
9.04
9.24
1.88
総行数264,095
263,889
264,415
228,735
行/関数301
298
292
76.6
行/ファイル
(※)
2589
2693
2698
144

※ *.cと*.hと一緒くたにした数字。*.cに限ると、概算でv2.2.1での平均行数は3000行以上、v3.3.0では300行以下

リファクタリングしたら行数が減る、意味不明ですね
あれれおかしいな このドキドキは(不整脈
数えていませんが5~6程度がコメントです
全部不要なので削除しました
なお一番大事な数字は「行/関数」です、これが少なければ少ないほど「可読性が高い (何をやってるかすぐ分かる)」、即ち良いコードであるということです

更に同じソフトを使って、サイクロマティック複雑度 (簡単に言えばif/for/while/caseの数)も数えてみます
1関数当たりの平均値です

複雑度
v2.1.4v2.0.0v2.2.1v3.0.0-Alpha
平均複雑度11.57
11.16
11.40
8.22
最悪複雑度498
498498170

平均で30%減、圧倒的ですね
最悪値も2/5に減らしました 以前のコードは放射性廃棄物
ちなみに最悪値を記録した関数は多分project() という魔法効果を表す関数です (調べてません)
火炎属性で周囲の木が燃える処理から始まり因果混乱属性によるステシャ処理まで全部入ってました
当然解体しました
或いは直接攻撃の関数かもしれません、混沌エゴのテレポート率計算から死の大鎌の反射倍率計算まで何でも入っていました
当然解体しました (とても大事なことなので100回言っても足りません)

ちなみにcase文が多いとどうしても複雑度も増えるので一概には言えませんが、魔法効果を表すenumが一番多く約110個であることを考えると、その4倍以上ある関数はリアルエルドリッチホラーです
現行のコードは170、しかもv3.2.0で減らす余地が十分にある170なので、相当マシになったと言えるでしょう

ここから先は更なるログルスの深淵へ踏み入れようと思いますが、(筆者の精神状態が)ヤバすぎてカレンダーには登録しません
Advent Calendar向けには、もっと楽しい話題をご提供する方針で考えています

次の記事はagrath様の「NetHack "raiden" の謎に迫る (3)」です(多分)
お楽しみに!
ギャラリー
  • 開発者になろう GitHub編
  • 開発者になろう GitHub編
  • 変愚蛮怒3.0.0 進捗状況 その36
  • 変愚蛮怒 3.0.0 進捗状況 特別編 その3 C#への可能性
  • 開発者になろう
  • 開発者になろう
  • 変愚蛮怒2.2.2 進捗状況 その21
  • 変愚蛮怒(2.2.2 開発中版) ハイエルフレンジャー その5
  • 変愚蛮怒(2.2.2 開発中版) ハイエルフレンジャー その3