あけましておめでとうございます. ニアトです. 2022年もよろしくお願いいたします.
さて, BDSPが発売されてから一か月以上経ちますが, 剣盾にて証乱数が発見されたことによって乱数界隈ではダイパリメイクのことが忘れ去られつつあるように感じます.
このままPokémon Legendsが発売したら完全に忘れされられてしまいそうな気がしたので, BDSPに関連するお話を一つ書き留めておくことにします.
ポケモンBDSPでは, GlobalRNGとLocalRNGの2つの乱数生成器が存在しており[1] [2], 基本的にはGlobalRNGを用いてランダムな事象を決定しています. [3]
LocalRNGには, 剣盾でも用いられているXoroshiro128+が使われており, 剣盾にて先駆者がいくつかの調査がなされていることから, 今回の話ではGlobalRNGに用いられているXorshift128について取り上げることにします.
Xorshift128について, 数学的な説明をざっくりとしておきます.
Xorshift128は, 4つの32bit変数
ある時点での内部状態を並べて,
具体的な
また, 出力は内部状態の下位32bit, 即ち
乱数調整をする際に用いられる手法は2通り存在します.
1つは乱数生成器に与えられる初期状態(seed)を調整してから目的の内部状態まで遷移させる手法で, もう1つはある時点での内部状態を出力から逆算してから目的の内部状態まで遷移させる手法です.
近年(第六世代以降)では, 初期状態を人為的に操作することが困難なことから, 後者の手法が採られることが多いですね.
今回についても, 後者の手法を採用します. 内部状態の復元手法についてですが, 実のところSM, USUMの孵化乱数(TinyMT)[4]や剣盾の証乱数(Xoroshiro128+)[5]と似たような手法が使えます. 同じ方法が使えるならそれで殴れば良いじゃないかということで具体的に殴っていきましょう.
乱数生成器の内部状態を復元するには, その出力を十分な量だけ観測して, 内部状態を逆算してやる必要があります. ゲーム中にGlobalRNGが使われている場面は数多くありますが, 今回はフィールド上での主人公の瞬きに注目します.
主人公の瞬きを決定するために, 約1秒に1回の間隔で乱数消費が行われます. このときの乱数生成器の出力
ここで, 演算子
つまりこの関数は, 主人公が瞬きをしたときの乱数生成器の出力の下位4bitが観測できることを意味しています.
補足:
「瞬きが発生した場合は、瞬きの種類に従って、乱数値の下位4bitが0000か0001であることが確定する」ということですね。
(夜綱氏によるコメント)
しかしながら, 瞬きをしていない時の出力からは殆ど情報を得ることが出来ないため, これまでのような "連続する
主人公の瞬き以外に乱数を要する要素が存在しないことを仮定します.
先ほどの話の通り, 瞬きから連続する乱数列の情報を得ることは困難ですが, 主人公が瞬きをしたある時点を基点として, 何消費目に瞬きが生じる出力となったかを観測することは出来ます.
基点から瞬きが起こった時点までの経過秒数がほぼそのまま消費数となることを利用しましょう.[7]
考察に移ります.
瞬きの種類
最初の瞬きを基点として, 発生した
基点でのxorshiftの内部状態
そのような行列
あとは,
行列
また, この記事を記述する際に, 夜綱氏にお力添えをいただきました. この場をお借りして御礼申し上げます.
厳密には日替わりの事象を決定する乱数生成器も存在する(https://bzl.hatenablog.com/entry/2021/12/03/002525)ため3つです ↩︎
https://github.com/zaksabeast/BDSP-RNG-Reference/blob/main/src/timeline.rs ↩︎
実際は1秒よりも僅かに長いようです ↩︎