「カルドセプトサーガ」というゲームで、サイコロが常に「偶数・奇数」のパターンを繰り返す、というバグがあって一時期祭りがありました。
- 痛いニュース(ノ∀`):【Xbox360】「カルドセプトサーガ」で、プログラマーがランダムなサイコロを作れなかったことが発覚
- 「カルドセプトサーガ」にダイス目が偶数と奇数を繰り返すバグ(slashdot)http://lovelove.rabi-en-rose.net/blog.php?n=256
- カルドセプトサーガ不具合情報
以上を参考にしてもらえば大体分かると思います。
もくじ
■自称「正しいコード」も間違っていた
2ちゃんのスレでどうも自称「正しいコード」が張られたらしい。今チェックしてきたんですが、見つからない。もう遥か昔に消えたか・・・。
これに関するうさだBlogのls氏のコメントが興味深いです。
やがてそのような書き込みの中に、Cコードを示して「サイコロなんかたったこれだけで作れるのに」と発言する物が複数現れた。そしてこれが最も重要な点だが、そのようにして示されたコードは、私が見た限りでは一つ残らず全てカルドセプトサーガのプログラマが犯したのと同じミスをしていた。
ワラタ。というわけで、今回は同じ間違いを犯していない正しい乱数発生コードを書こう!がテーマです。
こちらの記事と一緒に読むと分かりやすいと思われます。
■バグを分析
【法則1】
最大ダイス目が偶数のマップでの非AI戦において、ダイスによって導かれる数値は奇数と偶数を交互に繰り返す。
これはただ単に線形合同法を使って最下位ビットを捨てなかっただけですね・・・。最下位ビットは常に01010101・・・と0と1を交互に繰り返します。(定数値が悪ければ0か1を繰り返します)
【法則2】
最大ダイス目が奇数のマップでの非AI戦においても法則性が指摘されています。
※確定情報では無いため、現時点での統計情報を採りあげています。
例)
ダイスの最大目が7かつ祠の無いマップにおいて、
前に出た目からの予測が可能な、次の目の傾向。
・1の次は1か4か6
・2の次は2か4か6
・3の次は2か4か7
・4の次は2か5か7
・5の次は1か3か5か7
・6の次は1か3か5
・7の次は1か3か6
これは難しい。上位4ビットだけを取り出したとかだったらありうる周期は2^4=16以下なので、周期がたまたま7とあってしまった、と考えられますが・・・。そんな馬鹿な仕様にしてるはずが無い・・・。
これはすいません置いておきます><
数学が得意な人or解析済んだ人教えて><
XBOX360の改造コードが見つからないということは・・・まだ解析は出来ないのかな?
■改善策
ワラタ
ABA GAMESでも使ってました。だから何って話ですが。ABAGAMESの実装でも最下位5ビットは捨ててました。
- 線形合同法を少し工夫する
- 下位数ビットを捨てる
すっげー当たり前のことを延々と述べてすいません。ってかこの話題って少し詳しい人間なら大体知ってるもんだと思ってたんですが。違うのか。こう偉そうにいっておいて間違ってたら泣けるな・・・。
と思っていたら、上で紹介したスラッシュドットにこんな書き込みが。
VCのrandはそれほど質が悪くありません。 gccなどは非常に質が悪く、最下位bitは必ず0と1を繰り返します。 XBOX360の開発はVCじゃない(マイクロソフトなのに)ってことでしょうね
gccの標準ライブラリ書く人って大分詳しい人間だと思ってたんですが・・・。
■Cで書いた「自称正しいコード」
懐かしい、成分解析の時のアレ以上に良いコードが思い浮かばない。つーかこれでFA?
少し変えて再度書いちゃいます。
unsigined int seed = 0; //グローバル変数. unsigned int random(){ int result; seed *= 214013; seed += 2531011; // ->次に呼び出されたときのseedに使う result = seed; result = result >> 0x10; result &= 0x7fff; return result; }
■
ちなみにこれ、VisualC++の標準ライブラリまんまだと思われます。今回のカルドセプトサーガもXBOX360ということで開発環境はVisualC++だと思うんですが・・・。「開発環境によって乱数の発生アルゴリズムが変わっちゃいけない」と思って自前で用意したんでしょうか。リプレイ機能があるそうなので、ある意味この判断は正しいかも。他機種に移植する気があって、それらのソフト間でリプレイを行き来できるようにする気があるならね。他機種間でネット対戦させるなら、場合によっては必要かも・・・。