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

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

2022年12月

変愚蛮怒3.0.0 進捗状況 C++編

こんにちは、Hourierです
今回はプログラミング経験者向けの技術的な記事です
そうでない方は「変愚の開発は何か大変やなー」くらいに思いながら見てもらえると嬉しいです
記事の記述は12/17 00:00 のものです

まずは構造体&クラスです

ちょっと復習:
C言語においてクラスはなく、構造体しかありません
大きな違いは、「構造体に、そのフィールド変数を作成・更新・削除するメソッドを定義できない」です
そして構造体とクラスの違いは、C++においてはほぼありません
「アクセス修飾子がデフォルトでpublicになる (構造体)かprivateになる (クラス)か」だけです
構造体から構造体を派生させたり、構造体に純粋仮想関数を定義したりできます
また、変数をスタックに積むかヒープに積むかはプログラマが自分で決められます
コピー代入か参照代入かも決められます
これらが、C#やJavaにはない特徴です (※1、※2)


v2.2.1 (5e4acf9)では、以下のコマンドで調べる限り、構造体が24個定義されていました
v2.2.1 と微妙に違うのは、この時期はコード整形が全くされていなかったため、可能な限りカウント数を上げられるように調整しているためです
grep -rn --include='*.h' --include='*.c' "struct.*{" | wc -l

v3.0.0 α版 時点では、同様のコマンドで195個定義されていました
grep -rn --include='*.h' --include='*.cpp' "struct .* {" | wc -l
クラスは250個です
grep -rn --include='*.h' --include='*.cpp' "class .* {" | wc -l

上記の通りこの2つはC++においてほぼ同等のものなので、20倍近くに増えたことになります
これは何故でしょうか
そしてそれを調べる前に、何故「ほぼ同等のもの」が両方運用されているかについてお話します
理由は以下の2つです

・C言語時代に激長関数を分離する時、構造体を定義することで長い変数スコープをフィールド変数に押し込んだ (変数スコープが長いという問題は以前残り続けているものの、関数の見通しは100倍以上良くなった)
・言語をC++に切り替えた時、ある程度意味を持つ運用にした (後述)

これらの歴史的経緯により、「メソッドを持たなくて良いような単なるデータ集合」は構造体に、「メソッドを持たせて状態管理を行うデータ集合」はクラスに定義するようになりました
何となく自然発生的にそうなった記憶があります

話を戻すと、構造体が増えたのは以下の理由によります
要するに「24個全部神構造体」とかいう、ルルイエの旧神達も裸足で逃げ出す気の弱い女性はその名を聞くだけで卒倒する茨城県みたいな魔境だったわけです
  • ゲームシステムに直接関係しない構造体も積極的に定義し、1関数 (最悪値2400行!!)を分割した
  • ゲームシステムに直接関係するにも関わらず、構造体の中に長々と定義されていたフィールド変数群を別な構造体に切り出した
いくつも印象深いPRはありますが、いくつか挙げると以下の3つです
特にItemEntity::xtra1~5は、「メモリ制限がキツかった90年代にはある程度仕方なかったにせよ、エクストラとかいうふざけた命名のフィールド変数」には中々の殺意を覚えました
なお最悪は「my_fopen()」です、Zangbandのソースコードはプログラミングの教科書じゃねーぞ

C++へのコンパイルオプション切り替えは2021/05/02、α21版に行われました
そしてα22版からC++設計が本格的に始まりました
IRC時代から延々と議論がされていましたが、切り替えはその時まだ進みませんでした
具体的には以下の事由がありました:
  • C++で開発したくない心理的障壁
  • C#への切り替え要望 (主に私)
  • 実際問題C++へ切り替えた時に出てくるであろうコンパイルエラー解消工数

しかし議論の場がDiscordへ移り、それに伴って、特に心理的障壁が新規開発者の流入でどんどん下がっていったことはおぼろげながら記憶しています
コンパイルエラー解消工数についてもその辺りで「頑張って背負おう」という話になっていきました
具体的には下記に挙げる予約語の調整が行われました
私はこの作業に直接携わっていませんが、メンバーによると「new」や「friend」という変数名がC++でのコンパイルを妨げていたとこのことです
その他諸々の微調整があったらしいですが詳細は私も知り及ばない部分があります

そして大量の「ソースコードはC言語のまま&ViewとModelは分離されないまま&メソッドなき貧弱ドメイン構造体が大量定義されたまま」我々はC++時代に突入しました
構造体へ最初にメソッドを繰り込んだPRは↓です

この後、開発者の作業ペースや体調等の紆余曲折を経て、少しずつ基幹構造体 (src/system/ 以下の諸ファイル)へのメソッド注入は続きました
同時に、基幹構造体自体もC++化に伴って設計改善のために追加されていきました
この流れの中で、誰からともなく「メソッドが定義されたりコンストラクタが定義されているものはC++っぽくクラスと呼ぼう」という話が持ち上がりました
そのためには、単なるリネームで済む場合は良いのですが、場合によっては大規模なヘッダ順序入れ替えが必要でたいへん面倒な作業もありました
これらは気合と熱意で何とか処理してきた歴史があります

そんなこんなの事情でもって、構造体とクラスは我らが変愚蛮怒では混じっているという次第です
メソッド不要の単機能構造体は現在も不定期に増えており、変愚蛮怒から構造体がなくなる日は永遠に来ないと思われます

なお、ここまで長々と書いてきましたが、作業予定の方は順調に肥大化を続けております
本記事執筆時点で400個以上の未作業チケットが残っています
概ね週に10個程度のペースで増えていっている気がします
「絶対にC++のSTLで書き直した方が見通しが良くなる!」って思っても諸事情で中々進みません
主たる事情は「大作業を思いつく→コード調査をする→小作業が10個くらい出てくる→全部消化する→その頃には別な大作業を思いつく→以下無限ループ」です
部分最適をある程度進めないと、全体最適をかけた時にかなりの無理が生じるんですね
ただ日進月歩といいますか、部分最適はある程度進んできています
どこかの時点で加速度的に全体最適化を成し遂げるPRを複数出せる日は来るのではないかと想像しています
2024年頃には来ていると良いなぁ

無茶苦茶なバベルの塔を何とか穏便に解体するリファクタリング作業は、コンパイルが通らなかったり諸々のバグが起きたりして苦痛ですが、汚いコードが綺麗なコードになっていく快感じみたものは何物にも代えがたいものがあります
もし、「作業したい」でなくとも「詳細を知りたい」とか「今後の予定が知りたい」とか少しでも興味がありましたら、ぜひDiscordの開発チャンネルへお越し下さい
開発メンバー一同、両手を挙げて歓迎 しつつ愚痴の延々とした垂れ流しすると思います!


※1 C#の構造体は以下のような特徴を持ちます
  • 常にintと同様の値型であり左辺値へはコピー代入される (左辺値のプロパティを変えても元構造体のプロパティは変わらない。バグの温床)
  • 継承できない
  • 常にスタックへ積まれる (下手にListやDictionaryへ詰めると、ボクシングによりヒープとスタックを往復してCPU効率もメモリ効率も極度に悪化する。構造体しか使えない場合もいくつかあるので回避策もあるが、構造体かクラスか選べる状況下なら性能的例外を除きクラスで宣言するのがマスト)

※2 Javaには構造体という仕組み自体がありません。歴史的経緯・ネイティブコードとの相互利用・ネットワーク利用等の事情で似たような機能は色々ありましたが……

変愚蛮怒3.0.0 進捗状況 2022総集編

こんにちは、Hourierです
本記事はRoguelike Advent Calendar 2022 の10日目の記事です

開発基盤がODSNだった時はこっちに記事を上げないと備忘録として機能しづらかったこともあって頻繁に書いていましたが、GitHubに移った瞬間からぱったりと記事作成が止まってしまいました
GitHubだけで完結するのは楽で嬉しいですが、その分中々大多数のプレイヤー諸氏に解説記事を下ろしていくのが億劫になってしまいますね、すみません
ということで今回は久々の記事であると同時に、「この1年何してたん?」を振り返ってみようと思います
本記事ではあまりプログラミングの細かいことには立ち入らず、数字だけで実績を解説していきます
また、状況は12/10 00:00 時点のものです
色々装飾したかったんですが時間切れ

もくじ

チケット・PR数
コミット数
ソースコード差分
実装したアイテム
実装したモンスター
印象深かったチケット
Wiki

まずは消化したチケットの数と実装したPR (※1)の一覧を見てみましょう

チケットベースで141個、PRは142個あります
PRの方が多いのは、「わざわざチケットを作成するほどでもなかろう」と判断していきなりPRを投げたものが含まれるためです

次はコミットを見てみましょう
以下のコマンドで確認できます
git log --oneline --after '2022-01-01' --before '2022-12-10' | wc -l

はい、1447件でした
続けて私が担当したコミットを数えてみましょう

git log --after '2022-01-01' --before '2022-12-10' | grep "Author: Hourier" | wc -l

こちらも同様に770件でした
半分以上が私のコミットですね

ソースコードの差分は以下の通りです
差分計測ツールは「かぞえチャオ!」を使っております

新規ステップ数:15,545行
修正元ステップ数:147,863行
修正ステップ数:43,212行
流用ステップ数:123,754行
削除ステップ数:14,624行

新規+流用+削除=73,381行

当然一人でやった訳では無いですし、単なるリネームも沢山あります
一方で「年間7万行以上」というのは、下手な商業ソースコードよりもぶっちゃけ差分が多いです (※3)
(仕事でやった行数の5倍以上あるんじゃないか)

次に、ユーザにも意味のある (またはネタ枠)、印象深かったチケットを何件か挙げてみます
ゲーム体験そのものの拡張はほとんどできていませんが、実はちょこちょこフレーバー強化には努めています
特に、クエストや町の名前を人類が解読可能な名称に変更できたのは大いなる進歩でした
「普段は何も話さず、死亡時のセリフだけは話せるようになる機能の追加」

最後にWikiです
新しい開発者がDiscord/GitHub への移行後に複数名いらっしゃって頂き、感激の極みです
そんな彼らがスムーズに入っていけるよう、設計情報を中心に色々メモっています
そこのあなたもぜひ! まずはDiscordの開発チャンネルへ気軽にお越し下さい!

そんなこんなでじっくりゆっくり作業は進んでおります
これからも進化する変愚蛮怒をよろしくお願いします!

明日のカレンダーは未定です

※1 プルリクエスト。ある特定の関心事に基づいた作業一式。1~10回程度のGitコミット (※2)からなる。100回とかのコミットを提出されてもチェックできないので弾かれる

※2 必要最小限の作業1回。1コミットで多くのことをやると大抵嫌われる

※3 仕事の場合、客先へ御用聞きしに行ったり仕様を詰めたり自動テストコードを書かなかったり手動テスト項目を2000個作ったり2000個のテスト項目を目視チェックしたりハードが故障したり会議したり会議したり会議したり会議したり計画立てたりリスケしたりリスケしたりリスケしたりリスケしたり課長に振り回されたり課長の無能さに呆れたり課長に書類を差し戻されたり課長にキレたりする時間がとてつもなく長いので、オープンソース開発スピードとはまるで異なる (ただ非効率なだけ)
ギャラリー
  • 開発者になろう GitHub編
  • 開発者になろう GitHub編
  • 変愚蛮怒3.0.0 進捗状況 その36
  • 変愚蛮怒 3.0.0 進捗状況 特別編 その3 C#への可能性
  • 開発者になろう
  • 開発者になろう
  • 変愚蛮怒2.2.2 進捗状況 その21
  • 変愚蛮怒(2.2.2 開発中版) ハイエルフレンジャー その5
  • 変愚蛮怒(2.2.2 開発中版) ハイエルフレンジャー その3