himeshi’s blog

Simutrans本体改造まわりのお話をつらつらと

本家フォーラムに1年いて感じるあれこれ

この記事はSimutrans Advent Calendar 2日目のエントリとして書かれたものです。

adventar.org


今後の本体改造計画についてつらつらと書こうかなと思いましたがそれは次回に回します。

 

本家フォーラムに首を突っ込んで感じること


私はこの1年間様々なパッチを提案し、本家フォーラムに持っていって議論をしてきました。残念ながらその多くは未だ受け入れられていません。
理由はいろいろあるのですが、その一つとしてパッチの統合には以下の条件を満たす必要があることが挙げられます。

  • バグがなく実装が完全であること
  • 既存のゲームの他のシステムと一切矛盾をきたさないこと
  • 動作が重くならないこと。その増加分が無視できるほど小さいこと
  • その機能を使わないユーザーに一切影響を与えないこと。特に機能を使わないならば動作が全く重くならないこと。

(今のところ)趣味プログラマである私にとってこれらの条件を全てきちんと満たすことは決して簡単なことではありません。路線図メーカーを初期の頃からお使いになられた方々はわかると思いますが、最初はバグだらけでバージョンが上がるに連れまともに使えるようになるものです。路線図メーカーはとりあえず使用するにあたって問題ないレベルに達したと判断したので現在は開発を終了し、バグ報告のみ受けている状態です。
さて、個人で開発する分にも上の4条件が大切であることは言うまでもなく、条件をみたすように開発を進めます。ところが本家フォーラムで議論するときには4条件をただ満たすだけでは不十分です。条件を満たしていないと嫌疑をかけられたときには条件の充足を自ら証明しなければならないのです。疑いをかけられないちゃんとしたプログラムを持っていけと言われればソレまでですが。

結局OTRPもroadsignのfront/backも4条件を「完全に」満たさないと受け入れられないわけです。Devotee認定されているような「信頼ある」開発者はポンポン開発トランクにコミットしていますが信用のない人間がそこに参入するには実績を積み重ねて認めてもらわねばならないわけです。個人で開発してるような趣味プロジェクトなら少々問題のある機能でも利益が大きければとりあえず導入してしまうことはザラですが、シムトラの世界ではそうもいかないようです。注意していただきたいのは私はこの「完璧主義」を批判するつもりは一切ないということです。
本家フォーラムの人々がこのようなスタンスをとるのには理由があると考えています。ここで最近のシムトラが獲得した特徴を挙げてみましょう。

  • 動作が軽量である。バージョンアップを重ねても動作が重くなっていない。(むしろ軽くなっている)
  • きわめて動作が安定するようになった。5分毎にセーブしなきゃいけないなんていうのも過去の話。

新しいパッチを統合することはこの2つを犠牲にするリスクを含みます。結局、「便利になる反面不安定になるなら不便なままでいいから機能追加などしないほうがマシ」という考えになるのは至極当然ということになります。それはSimutransが成熟し、信頼性の高いゲームになった証でもあるのです。
さらにはSimutransは(私がリサーチ不足なだけかもしれませんが)branch建てというものを特に行っていません。すなはち一度統合したらそれを取り消したり、その影響を排しながら開発を続けるということが困難で、「ラディカルな」コミットを受け入れることが難しくなっています。結局スタンスとして、「あなたのパッチはバグがなく既存の機能と矛盾がなく動作が重くならず恩恵を受けないユーザーに影響を与えないなら統合しても良い。統合されるまではその開発は提案者一人に責任がある。」となっているように、私は感じます。ここからは私のただのわがままですが、最終完成まで一人で走りきらないと何も残らないというのはいささか辛く寂しいものです。

それでも和製パッチラッシュのさきがけとしてLCパッチや車庫パッチは(私が僅かな修正を加えたものの)すんなり(?)と本家に統合されました。言うまでもなくそれは統合の4条件を完全に満たしたからであり、私が未だできずにいるのはひとえに私がプログラマとしてあまりに未熟であるからだという批判は甘んじて受け入れなければなりません。つらいorz

 

OTRPの行き詰まり

各パッチがどのような状況であるかは次の記事で詳しく書くつもりですが長くなるのでOTRPの話だけはこの記事で触れようと思います。OTRPは2月にver1を出して現在12_3まで進みましたが、予備調査も含めると今年(2017年)の1月アタマから開発してるので2017年まるまるOTRPに捧げてきたことになります。
既に何度か述べているようにOTRPは既に「機能的には」完成しています。しかし以下の問題により統合に至っていません。

  • 望ましくない挙動(=バグ)がまだ残っている。バグの発生源になる可能性がある箇所が多すぎる、かつ特定困難。
  • アルゴリズムが複雑でコードレビューをする人が困り果てている。というか書いた本人にもわけがわからないシロモノになってしまった。
  • 複雑なのでコードの保守性が著しく低下する
  • OTRPとは直接関係しないが付随して生じる問題が非常に多い。(例:複数マスからなる交差点)
  • パフォーマンスが悪化しないという証明が難しい。

既にOTRPは皆様に実行ファイルが配布され、お楽しみいただいてる方もいらっしゃいます。動作的にはほとんど問題がないことがプレイするとわかると思います。
しかし統合となるとそれでは不十分なのです。潜在的な問題箇所が存在しないことが、コードを見れば自明な状態にしなければならないのです。この「完璧主義」の理由は先ほどお話したとおりです。現行のOTRPの統合はリスクが大きく、軽量さと安定性を確保しなければならない本家開発陣には到底受け入れがたいものです。決して本家フォーラムの方々がケチをつけているわけではありません。和製パッチが持って行かれて本家フォーラムで散々に批判されてつい怒りに震えるのはわかりますが、本家フォーラムの(少なくとも)称号付きメンバーの方々はコードの隅々を知り尽くしている、いわばSimutrans開発のスペシャリストであり、彼らによる批判はいつも謹んで受けなければなりません。

ではなぜOTRPはこのような事態になってしまったのでしょうか? OTRPは元来2週間で開発を終えるつもりでした。現行Simutransは既に追い越しロジックを持っており、なぜかそれに厳しい条件を設けているので、その条件を撤廃すれば簡単に片側二車線道路が完成する、最初は本気でそう思ってました。
2週間で完成するはずのパッチになぜ1年間も費やすことになったのか?それはこの1年、「設計上の矛盾」と「アルゴリズムの矛盾」にひたすら対処し続けてきたからに他なりません。
「設計上の矛盾」はovertaking_modeの導入やゆずりあいシステム、車線固定標識に見ることができます。単に制限を外しただけでは対向車に対処できず、最終的には道路自体を一方通行にする必要がありました。ただ制限を外すだけでは車両は適切な車線に移動せず、スムーズな交通の実現には標識による誘導が必要で車線固定標識を導入しました。特にovertaking_modeの設計はかなり紆余曲折を経ており、wayアドオンの属性→wayobjで付与する属性→建設時にモードを選択 と2回も大きな仕様変更をしています。設計上の矛盾はこの1年で解決が為されたと考えています。
アルゴリズムの矛盾」こそがOTRPを停滞させてきた要因です。開発をはじめて最初に突き当たった問題は、車両は追い越しを始めると一切他の車両の面倒を見なくなることでした。すなはち、対向車が来てもすり抜けるわけです。今問題になっているのはroad_vehicle_t(そのスーパークラスであるvehicle_tとvehicle_base_tを含みます)のcan_enter_tile()とno_cars_blocking()です。わかりやすく言えば、「そのタイルに入れるか(次のタイルに既に車がいるならば停止する)」「追い越しを始めるか、やめるか、なにもしないか」の判別処理をします。現行simutransでは車両運行の時原則として「次に入る予定のマスに進入できるか?」を定期的に検査して進んでいきます。

Simutransでの場所の管理の単位は「座標」です。そしてこの座標は整数型です。これが全ての元凶です。
simutransの道路交通では非常に不思議なことが起こっています。同じマスに車が2台いても良いのです。但し「逆方向を向いているなら」同じマスに2台いても良い、これが内部で行われていることです。「1マス1台」の原則が崩れているのでこれだけでも十分複雑です。余談ですが、描画上では車線が区別されているように見えますね。アレは進行方向によって描画位置をズラしているだけであって、内部的には車両はどちらの車線にいようとも座標(80,100)にいる。それだけなのです。同じ車線の車も対向車線の車も同じ座標で見えているので現行シムトラでは相手車両の「向き」によってそのマスへの進入可否を判断しています。(下の図)

f:id:himeshi:20171126100547p:plain
これだけなら平和でした。そこに追い越しを導入するとどうなるでしょう。「同じマス」に「同じ方向」の車が2台いることになります。そこで従来の追い越しロジックでは追い越し中か否かというパラメータが編成ごとに保持されています。OTRPは既存のコードを拡張して作っていったので「追い越し中か否か」のパラメータでどちらの車線にいるかを管理しています。

f:id:himeshi:20171126100555p:plain
高速道路のような典型的な片側二車線の直線道路を想像してください。前に進む分には自分の車線の前方に車がいるかチェックすればいいですね。隣の車線がどういう状態であろうと気にする必要はありません。なので隣の車線は無視して自分の車線の前方マスのみ面倒を見ればよいです(図の左側)。今度は片側二車線道路どうしが直交している交差点を想像してください。先ほどのロジックがそのまま使えるでしょうか?答えはNOです。交差点マスで横切った車両に関しては追越車線も走行車線も両方見なければなりません。図では自車が走行車線、相手が追い越し車線を走っていますがこの状態でマスに進入したらアウトです(図の右側)。ですが自分と同じ方角の車両に関しては隣の車線は無視して構いませんね。わけがわかりません。「自車に対して相手が横向きであれば追越車線・走行車線両方見れば良い」と仰せられるかもしれませんが、今度はdiagonal(斜め道路)に対処できなくなることが、少し考えればすぐわかります。 
既存のロジックの拡張を繰り返した結果このような問題が無数に生じている、それが今のOTRPなのです。矛盾が見つかるたびに条件分岐をして特殊な処理を挟んで対処してきました。その結果大量のコーナーケース処理が発生し、アルゴリズムが難解になり、保守性が低下し、とってもとってもバグが出てくる地獄に陥りました。しかし一度実装したものを捨てることはなかなか辛いもので、拡張の繰り返しで乗り切ろうとした結果が今の状態なのです。

だいぶ高度な話をしてしまいました。話について来られた皆さんなら気づくと思います。「次に入る予定のタイルに進入できるか?」の「タイル」が大きすぎることに。

 

OTRPはどこへ行くのか

現行の「1タイル」の中では車両は2車線×4方向=8状態取ることができ、この状態のいかんによって挙動が大きく変わることがアルゴリズムを難解にする要因の一つであることを説明しました。ならばその「タイル」を細分化すれば良いのではないか?というわけです。例えば今の1マスを上下左右の4マスに細部化すれば走行車線と追越車線は別の座標で扱うことができます。

タイルを細分化するということは今のシムトラの座標システムに手を加えることになります。この座標破壊は大きな挑戦で、解決しなければならない課題が多数ありますし、どうせやるなら他のプロジェクトと一緒にやりたいのでこの方向の話は別の機会にすることとします。

私はこのことにもっと早く気づくべきでした。いや、薄々気づいてはいました。けれどもそれを拡張で乗り越えようとした結果、何度もintegration candidateを出し、拒否され、バグ修正に追われ、けれども事態は改善しないという状況を繰り返してきたのです。

これが今のOTRPです。私は決してOTRPを諦めたくありませんし、メンテナンスも続けるつもりですが、いつゴールに辿り着くか全くわからないOTRPに時間を費やすより前に実装すべき案件が山のようにあります。それらについては次の記事でお話するとしましょう。
最後に、OTRP開発に参加していただける方大募集しております。プルリク、お待ちしております。