なるほどえたきちのブログじゃねーの

チラ裏雑記帳

ポケモン過去作の解析とか乱数調整とかそこら辺。不定期更新。

【ポケモンBDSP】LCG in 2021【日替わり乱数】

この記事はPokémon Past Generation Advent Calender 2021 3日目の記事です。
adventar.org

あと固定乱数とは全く関係のない内容になっていますので悪しからず。

概要



先月発売されたポケットモンスターブリリアントダイヤモンド/シャイニングパール(以下BDSP)にて、発売前の解析によりグローバルPRNGにXorshift128(Unity標準関数)、ローカルPRNGにXoroshiro128+が使用されていることが分かっていたが、その後の解析により日替わりseedの遷移に64bitのLCGが使用されていることが判明した。
この日替わりseedは原作のDPと同様にIDくじ、大量発生の種族、ヒンバスの釣れるマス等に使用されているため、何らかの手段で逆算ができればそれらのランダム要素の予測が可能となる。

また、この調査はBDSPver1.0.0のROMデータ解析により行っている。
私が最後に確認したver1.1.1の時点では計算内容の再現が取れたが、先日のアップデート(ver1.1.2)で何かしらの修正が入っている可能性がある。

追記(2021/12/23)
ver1.1.3にて当該処理の修正が行われた。
seedの更新処理等は変更されていないが、IDの決定処理のみ以下のように修正されている。
これにより、1つのIDから未来のIDを予測することはできなくなったが、32bitの日替わりseedの予測が容易となった。
以前のバージョンまでは6日分の大量発生の情報が必要だったが、この修正により2日分のIDくじの結果で絞り込めるようになっている。
また、この計算処理の仕様上IDの下位5桁が00000-48575の場合は他のIDより1.1倍程くじに当選しやすい。

// くじ当たり番号の決定
void FieldLotteryWork__UpdateNumber(int64_t days)
{
	// 前後省略
	uint seed = RandomGroupWork__GetDefaultRandom();
	seed = seed * 0x41c64e6d + 0x3039;
	
	// くじ当たり番号の決定
	int WinningNumber = (seed >> 0xC) % 100000;
}

LCGのよくない実装



この予測についてだが、IDくじとヒンバスの釣れるマスについてはIDくじの結果を1度確認するだけで可能となってしまう。
これを可能とする原因箇所は以下の通りである。

// 日替わりseed更新
void RandomGroupWork__Update(int64_t days)
{
	// 前後省略
	ulong seed = PlayerWork__get_RandomGroup();
	for (int i = days; i > 0; i--)
	{
		seed = seed * 0x6c078965 + 1;
	}
}

// くじ当たり番号の決定
void FieldLotteryWork__UpdateNumber(int64_t days)
{
	// 前後省略
	uint seed = RandomGroupWork__GetDefaultRandom();
	seed = seed * 0x41c64e6d + 0x3039;
	
	// くじ当たり番号の決定
	int WinningNumber = seed & 0xFFFF;
}

勘のいい方は気付いたかもしれないが、DPPtとほぼ同じである。
ヒンバスタイルの決定処理は長くなりそうなので省くが、こちらもDPPtとほぼ同じ決定処理だった。
但し、IDくじ当たり番号の決定処理については原作と少し異なっている。
原作では32bitのseed値上位16bitを使用していたのに対し、今作は下位16bitを使用してしまっている。カルドセプトサーガ
これによりどのような問題が発生するかと言うと、IDくじに限り実質的に16bitのLCGとなる上にその内部状態が1つの結果を確認した時点で完全にリークしてしまうことになる。
また、この使い方ではLCGの性質上取られる値に規則性が生まれてしまう為、そもそも乱数として成り立っていない。(1日毎にIDの偶奇が変わる、等)
1度のくじ結果確認で未来予測が可能となる理由については以上の通りである。

Xoroshiro128の初項(64bit)に32bitの値入れてたりする辺りILCAが自前で実装した乱数生成器は大概何かしらの問題を抱えてる気がする

IDくじのバグ



また、そもそものIDくじの挙動にも問題がある。
BDSPではトレーナーIDが6桁(000000-999999)の値となる為、5桁でIDを取得するにしても00000-99999の範囲が得られなければならない。
しかし、BDSPの実装では0x0000-0xFFFF(00000-65535)までの値しか取得できない仕様となる為、34464通りのIDが完全に無視されることとなる。
更に、IDの比較処理にもバグがあり、6桁IDの内下位5桁しかチェックされていない。
その為、上位5桁が完全に一致しているポケモンを所持していたとしても以下の挙動になる。




おいふざけんな
剣盾のIDくじの仕様調べてみたら仕様通りらしい、謹んでお詫び申し上げます

つまり、6桁ID下位5桁が65536以上の人は一生掛かってもくじに当選しないこととなる。(修正アプデが入らなければ)

各種要素の未来予測



計算用のツールを作成したのでそちらを適当に触って欲しい。
ファイル置き場 - チラ裏雑記帳
IDくじの結果とヒンバスの釣れるマスについては1度のくじ結果確認により予測できるようになる。
大量発生の予測には日替わりseed下位32bitの特定が必要となる。
これはIDくじの結果と6日分の大量発生の内容の記録によって可能。





くじの結果からヒンバスタイルを予測した場合、緑のマスか赤のマスのどちらかが対象のタイルとなる。

また、大湿原の出現ポケモン決定や地下大洞窟の道具交換にも日替わりseedが使用されている。
但しこちらは乱数値の32bitキャストを行わずに計算している為、今回は対応しなかった。

終わりに



ここまで読んでくださってありがとうございました。
まさか最新作でこんな内容の乱数調整(?)ができるとは思っていませんでした。
今回の日替わり乱数とは別にBDSP固定乱数、SWSH野生シンボル乱数も本体改造無しで出来ることが分かったのでそちらのツール制作/調査も進めたいなと思います。

4日目は剣心氏による4gen捕獲チュートリアルです。