はじめに
この記事はPokémon RNG Advent Calendar 2016 一日目の記事です🎉🎉🎉
www.adventar.org
この記事では主にコンピュータゲームで話題になっている乱数調整について, ポケモンを例に説明します. 以下の方々を対象にした入門的な記事になっています.
- 乱数調整という言葉を初めて聞いた人
- 乱数調整という言葉を聞いたことはあるが, その意味や具体的な手順を知らない人
- 乱数調整をやってみたいと思っているが, 手を出せずにいる人
ポケモンのゲームシステム, 16進数表記, ビット演算を知っている/理解できる前提で書いています. ご了承下さい.
わかりやすさを優先して一部現実と異なる事が書かれている場合があります. 誤字や重大な間違いを発見した場合は修正しますのでこの記事のコメントもしくはTwitterのリプライにてお気軽にご連絡下さい.
「乱数調整」とは
記事のタイトルを見て, 「乱数調整って何だろう?」と疑問に思った方もいるでしょう. 本記事では「乱数調整」の仕組みを理解し, 実際に乱数調整を行ってみることを想定したものとなっています.
「乱数調整」について説明する前に「乱数」について軽く学習しましょう.
「乱数」とは
「乱数」とはサイコロの出目のように規則性がなく予測不能な数のことです. サイコロを例にとって考えてみます. サイコロを数回振ると, 1, 3, 4, 3, 2, 6, 1, ...
というように1~6の出目を得ることができます. 一般に, サイコロの出目には規則性が無く, 得られる値はでたらめに並んでいます. このように, でたらめに並んだ数列を「乱数列」と言い, その数列の元を「乱数」と言います*1. 乱数列はそれ以前の数から次の数を予測することが出来ないという特徴があります.
1回目 |
2回目 |
3回目 |
4回目 |
5回目 |
6回目 |
2 |
4 |
6 |
4 |
4 |
1 |
7回目 |
8回目 |
9回目 |
10回目 |
11回目 |
12回目 |
3 |
4 |
5 |
5 |
6 |
? |
※ サイコロを11回振った時の出目を表にしました*2. 1 ~ 11回目の出目に規則性が無く, 12回目の出目を予測することは出来ません.
乱数の種類
ここで, コンピュータで乱数を扱う方法を紹介します. 詳しい説明は省きますが *3, コンピュータは確定的な計算を用いてしか乱数を生成することが出来ません. 以下にコンピュータ上での具体的な乱数の計算例を示します.
S[0] = 0
S[n+1] = (3 * S[n] + 5) % 7
S = { 5, 6, 2, 4, 3, 0, 5, 6, 2, 4, 3, 0, 5, 6, 2, 4, 3, 0, 5, 6, ... }
この乱数生成法は「線形合同法(LCG)」と呼ばれ, 漸化式を用いた簡易的な乱数生成法の1つです*4.
線形合同法は適当な初期値からスタートして、次の漸化式で乱数を生成します。
X[i] = (A * X[i-1] + C) mod M
これで 0 以上 M 未満の値を生成することができます。A, C, M の選び方は 参考文献 [2] より引用します。
M が 2 の累乗なら A mod 8 を 5 または 3 とし(5 の方が安全)、定数項 C を奇数とする。このとき、周期はちょうど M となり、その 1 周期分には 0 から M - 1 までの整数が 1 個ずつ現れる。
高速にするには C = 0 とし、初期値 X0 を奇数にする。ただし、周期は M / 4 になる。
サービス終了のお知らせ
S[0]
は乱数列の初項で, 初期Seed, もしくはSeedと呼ばれます. 乱数列は初期Seedと漸化式を用いて逐次的に求められます. このように確定的な計算によって得た乱数を擬似乱数*5, 対してサイコロのような確定的な計算以外で得た乱数を自然乱数と区別して呼びます. 擬似乱数生成法には漸化式以外を用いたものもありますが, 漸化式を用いた擬似乱数生成法には以下のようなメリットがあり, コンピュータと相性が良いのでこの方法が主流です.
- 漸化式と初期Seedを記録しておけば、誰でも同じ数列を再現できる (注: ある乱数列を再現するために全ての項を記録しておく必要がない)
- (コンピュータ上での計算が)高速で低コスト
あなたの使っている乱数、大丈夫?-危ない標準乱数と、メルセンヌ・ツイスター開発秘話-
初期Seedは乱数列を決定する重要な要素の1つで, 出来るだけ乱雑な値になるように設定されます. 通常は乱数生成器を起動した時刻などを元に設定されます.
擬似乱数列は確定的な計算によって生成されているので, それを構成する各乱数の値を算定することが可能です. 事前に擬似乱数列を構成する個々の乱数を求めることができれば, それらを基に決定される事象を予測し, 狙った事象を発生させることも可能となります.
改めて「乱数調整」とは
乱数を軽く理解できたところで改めて乱数調整の意味について説明します. 乱数調整とは「 次に得られる乱数を予測し, 狙った乱数を得ること 」を指します. 先程のサイコロの例で考えるならば, 何かしらの手法で12回目のサイコロの出目を事前に予測し, 実際に予測した出目を得ること, これが乱数調整です.
実際にはサイコロの出目のような自然乱数を予測することは神でもない限り出来ないので, 算定可能な擬似乱数を対象に乱数調整が行われます.
乱数調整は「 事象を予測し, 狙った事象を発生させること 」といったようにもっと広義的に捉えられる場合もあります*6. これは調整する対象を乱数から事象(将来発生する事象)に拡張しただけです. これは調整する対象をミクロな見方をするか/マクロな見方をするかの違いです. この記事ではポケモンにおける乱数調整で一般に利用されている後者の意味で乱数調整を捉えます.
ポケモンで使用される乱数生成法
乱数調整の言葉の理解が深まったところでポケモンでどのように乱数が扱われているかを見ていきましょう!
使用するハードウェア, ソフトごとに乱数生成法が異なりますが, 今回は DS Lite + ポケットモンスター ハートゴールド(HG)を使用します. ポケモンシリーズで使用される乱数生成法の多くはネットのえらい人たちが既に調べてくれているのでわざわざ自分で調査する必要はありません. えらい人たちに感謝 🙏
HGでは以下で定義される線形合同法によって乱数が生成されています.
frame := フレーム (0 < frame)
year := 年 (2000 ≦ year ≦ 2099)
month := 月 (1 ≦ month ≦ 12)
day := 日 (1 ≦ day ≦ 31)
hour := 時間 (0 ≦ hour ≦ 23)
minute := 分 (0 ≦ minute ≦ 59)
second := 秒 (0 ≦ second ≦ 59)
S[0] = (((month * day + minute + second) * 0x1000000)
+ (hour * 0x10000)
+ (frame + year - 2000)) % 0x100000000
S[n+1] = (0x41C64E6D * S[n] + 0x6073) % 0x100000000
年, 月, 時間, 分などの日時はゲームで「つづきからはじめる」を押した瞬間のハードに設定されている日時が用いられ, frame
はDSを起動してソフトを選択し, ゲームが起動した瞬間から「つづきからはじめる」を押した瞬間までの時間をフレーム*7(単位の記号はF, 1秒≒60F)という単位に換算したものです. ただし実際には, DSのメニューからソフトを選択し, 起動するまでに350F(約5.8秒)のラグが発生 *8 しており, その時間は frame
に含まれないことに注意して下さい. また, ゲームの仕様によりフレームは2ずつしか増加しません.
例えば, 僕が今この文章を書いている2016年11月26日3時10分10秒*9にDSのメニューからゲームを選択し, 2016年11月26日3時10分40秒に「つづきからはじめる」を押したとすると, 初期Seedは以下のようになります .
frame = (40s - 10s) * 60F - 350F = 1450
year = 2016
month = 11
day = 26
hour = 3
minute = 10
second = 40
S_0 = (((month * day + minute + second) * 0x1000000)
+ (hour * 0x10000)
+ (frame + year - 2000)) % 0x100000000
= 0x500305BA
実擬似乱数列
LCGで生成される乱数にはとても不思議な特徴があります. ここでLCGの漸化式を見てみましょう.
S[n+1] = (0x41C64E6D * S[n] + 0x6073) % 0x100000000
0x41C64E6D
, 0x6073
は両方とも奇数であることに注目して下さい.
奇数*偶数+奇数
は奇数
奇数*奇数+奇数
は偶数
より, なんとこの擬似乱数列は 偶数, 奇数, 偶数, 奇数, ...
を繰り返してしまいます! 不思議ですね!
これはよく知られたLCGの欠点の1つで, 最下位bitが 0, 1
を繰り返すというものです.
UNIX の /usr/ucb/cc を解析した結果、 rand( ) は以下の動作をすることがわかった。
static long x=1;
void srand(long s) { x=s; }
long rand() { x=x*1103515245+12345; return x&2147483647; }
これは、非常にシンプルな線形合同法である。 この乱数の最下位ビットは0と1の繰り返しになる。 すなわち、偶数と奇数が交互に生成される。 このことから、この乱数で下位ビットを乱数として使うのは危険であることがわかる。 また、ある乱数が得られたら、次に現れる乱数が1種類しかないという欠点も持つ。
良い乱数・悪い乱数
これを知らずにLCGで生成された生の乱数を使用すると大きな偏りが生じてしまうので *10 , ポケモンでは S[n] >>> 16
(>>>
は0 埋め右シフト) を擬似乱数として使用しています. 本記事では説明のために実擬似乱数列 r[n] = S[n] >>> 16
と定義しておきます (様々な乱数調整関連の記事で r[n]
という式自体は出てきますが, それに名前が付いてなくて不便なので勝手に僕が名付けました).
乱数の消費
ポケモンでは新しい乱数が生成されたとき, 「乱数が消費された」と言い, 乱数生成器で乱数が生成される度に乱数が消費されていきます. 乱数が2度生成された場合は2回乱数が消費され, 「乱数が2消費された」「2F消費された*11」といった表現が使われます.
以下に乱数を消費する事象の代表的な例を示します.
消費数 |
事象 |
1 |
ウツギ博士に電話を掛ける(通話内容の決定で乱数が消費される) |
1 |
NPCが振り向く/移動する*12 (NPCが振り向く向き, 静止時間の決定) |
1 |
録音した音声を鳴き声として設定したペラップの鳴き声を聞く(鳴き声を聞く度に音の高さが変わる, 音の高さの決定で乱数が消費される) |
2 |
たんパンこぞう ゴロウに電話を掛ける(電話内容自体に疑似ランダムな要素はありませんが, 内部で乱数生成器によって2F消費されています) |
4 |
連れ歩いている眠り状態のポケモンに話しかける(上と同じく疑似ランダムな要素はありませんが, 内部で乱数生成器によって4F消費されています) |
徘徊ポケモンの数 以上 |
「つづきからはじめる」を押してゲームを再開する(徘徊ポケモン*13の出現ポイント決定に乱数が消費される, ゲーム再開前後で徘徊ポケモンの出現ポイントが同じになったとき出現ポイントの再決定が行われる, ゲーム再開時に自動的に消費が行われるので強制消費と呼ばれます) |
手持ちポケモンの数 |
128歩歩く(128毎歩くたびに行われる手持ちポケモンのなつき度判定で乱数が消費される) |
12以上 |
ラジオのチャンネルをオーキドはかせのポケモンこうざに切り替える(詳細: オーキドはかせのポケモンこうざ - oupoの日記) |
これらの乱数消費法を用いることで, 自分の好きな回数だけ乱数を消費できます.
ウツギ博士との電話で乱数調整
「消費」という概念, 上の説明だけでは分かりにくいですね...
実際にウツギ博士に電話を掛けて消費がどんな操作であるのかを確認しましょう.
準備
ウツギ博士が電話で話す内容はストーリーの進行具合によって変わってくるのですが, 今回は以下の条件の元でやってみます.
- 殿堂入り前にウツギ博士にトゲピーを見せ, 変わらずの石を貰っている
- 殿堂入りをしている
- ポケルスに感染した個体、もしくは感染した後完治した個体をポケモンセンターで回復させたことがある
この条件下では, ウツギ博士が電話で話す内容が3種類に固定され, 電話をする度に r[n] % 3
で対応するメッセージが決定されるようになります.
r[n] % 3 |
メッセージ(先頭のみ) |
0 |
ポケモンの しんかというのは~ |
1 |
カントーには まだ ぼくの〜 |
2 |
ポケルスが くっついた~ |
今回は先程計算した初期Seed 0x500305BA
で乱数調整を行います.
初期Seed 0x500305BA
での n = 20
までの乱数列は以下のようになります.
n |
S[n] |
r[n] |
r[n] % 3 |
メッセージ(先頭のみ) |
0 |
0x500305BA |
0x5003 |
2 |
ポケルスが くっついた~ |
1 |
0xA4E47CA5 |
0xA4E4 |
2 |
ポケルスが くっついた~ |
2 |
0x1FE1B8B4 |
0x1FE1 |
1 |
カントーには まだ ぼくの〜 |
3 |
0xE89ADD17 |
0xE89A |
2 |
ポケルスが くっついた~ |
4 |
0x1017853E |
0x1017 |
0 |
ポケモンの しんかというのは~ |
5 |
0xD090FFD9 |
0xD090 |
1 |
カントーには まだ ぼくの〜 |
6 |
0xF6876DD8 |
0xF687 |
0 |
ポケモンの しんかというのは~ |
7 |
0x0831F56B |
0x0831 |
0 |
ポケモンの しんかというのは~ |
8 |
0xB1CE7902 |
0xB1CE |
2 |
ポケルスが くっついた~ |
9 |
0xB754824D |
0xB754 |
0 |
ポケモンの しんかというのは~ |
10 |
0x233D513C |
0x233D |
0 |
ポケモンの しんかというのは~ |
11 |
0xC0443EFF |
0xC044 |
2 |
ポケルスが くっついた~ |
12 |
0x207AE506 |
0x207A |
1 |
カントーには まだ ぼくの〜 |
13 |
0xEEBFB801 |
0xEEBF |
0 |
ポケモンの しんかというのは~ |
14 |
0xA37806E0 |
0xA378 |
1 |
カントーには まだ ぼくの〜 |
15 |
0x5D738DD3 |
0x5D73 |
1 |
カントーには まだ ぼくの〜 |
16 |
0x439C0D4A |
0x439C |
1 |
カントーには まだ ぼくの〜 |
17 |
0x66BA94F5 |
0x66BA |
0 |
ポケモンの しんかというのは~ |
18 |
0x015272C4 |
0x0152 |
2 |
ポケルスが くっついた~ |
19 |
0x36AAF5E7 |
0x36AA |
2 |
ポケルスが くっついた~ |
20 |
0x356175CE |
0x3561 |
0 |
ポケモンの しんかというのは~ |
ポケモンでは S[0]
は疑似ランダムな事象を決定するためには用いられず, S[1]
以降が用いられます. つまり, ゲームを再開して初めて生成される乱数は S[1]
となります. また, 僕が乱数調整を行うHGは徘徊ポケモンが3匹いるので, ゲーム再開時に自動的に3~6F消費されます. よって初めにウツギ博士に電話を掛けるときに使われる乱数は S[4]
, S[5]
, S[6]
のどれかです.
乱数調整する際は出来るだけ不定NPC (先程の消費の例で挙げた乱数を消費するNPCのこと) がいない所でやると良いでしょう. 自分が消費している最中にNPCに勝手に消費されてしまっても困りますからね. 不定NPCがいない所であればどこでも良いです. 僕は主人公の部屋でやることにしました. ここで一度セーブしてハードの電源を切りましょう.
トロフィーコンプしてないことがバレてしまう…
初期Seedを合わせる
初期Seed 0x5003048F
に合わせるには2016年11月26日3時10分10秒にDSのメニューからゲームを選択し, 2016年11月26日3時10分40秒に「つづきからはじめる」を押す, でしたね. 以下の手順で初期Seedを合わせましょう.
- エメタイマーで30秒間のタイマーをセットする
- ハードの電源を入れ,
本体設定パネル > 日付&時刻パネル > 日付パネル
及び 本体設定パネル > 日付&時刻パネル > 時刻パネル
からハードの日時・時刻を2016年11月26日3時10分0秒に設定する
- 直ぐにハードを再起動し, 上画面の時計の針が10秒を指した直後にHGを選択する. 同時にエメタイマーを起動する.
- 「つづきからはじめる」ボタンの画面で待機し, エメタイマーのカウントが0になったらボタンを押す
- ゲームが再開したらポケギアのマップを開き, 徘徊ポケモンの位置から初期Seedを特定する
- 初期Seedが目標のものと異なっていたらずれを確認し, タイマーにずれを反映させて
2
に戻る. 一致すればそこで終了.
「エメタイマー」, 「徘徊ポケモンの位置」のあたりを解説します.
1. エメタイマーで30秒間のタイマーをセットする
ポケモンでは frame
を合わせるためにエメタイマーと呼ばれるタイマーをよく使います *14. これは乱数調整用に開発されたタイマーでフレーム⇛秒変換機能など乱数調整を補助する様々な機能が付いています.
使い方はサイトの下部にある説明を読めばわかります. 今回は30秒間待機したいので「時間」欄に「30」と入力しました.
5. ゲームが再開したらポケギアのマップを開き, 徘徊ポケモンの位置から初期Seedを特定する
「乱数の消費」の項でも取り上げましたが, 徘徊ポケモンの位置の決定には S[1]
以降の任意の個数の乱数が使われます (徘徊ポケモンが3匹の場合). これを利用して徘徊ポケモンの位置からどの初期Seedを引いたのかある程度絞ることが出来ます. 「HGSS徘徊 初期seed確認」というツールを使ってみましょう.
セーブ直前の位置
セーブした直前での徘徊ポケモンの位置を <ライコウの位置>,<エンテイの位置>,<ラティアスの位置>
の形式で入力します. 位置は 29ばんどうろ
だったら 29
, 21ばんすいどう
だったら 21
といったように数値で指定します. 今回は 32ばんどうろ,29ばんどうろ,5ばんどうろ
だったので 32,29,5
と入力しました.
ロード後の位置
ソフトを再開したときの徘徊ポケモンの位置を入力します. 形式は「セーブ直前の位置」欄と同じです.
目標初期seed
合わせたい初期Seedを入力します. 今回は 0x500305BA
と入力しました.
下4桁の範囲
徘徊ポケモンの位置を検索するの初期Seedの範囲を, 初期Seedの下4桁の範囲で決めます. 「つづきからはじめる」を押した日時・時刻が固定であれば初期Seedの上位4桁は固定なので, 基本的に下4桁のみが変動します. 今回は 0x500 ~ 0x5D0
と入力しました.
実際にこの手順でやると, ゲーム再開後の徘徊ポケモンの位置は 29,45,3
となりました. これを先程の「ロード後の位置」に入力して「計算」ボタンを押すと以下の出力が得られました.
これより, 目標の初期Seed 0x500305BA
に対して 0x500305A0
を引いていたことがわかります. 下4桁の差を取ると 0x05BA - 0x05A0 = 26
となり, 待機時間が 26F
短かったことになります. ちなみに括弧の中に表示されている数はその初期Seedでの強制消費数を表しています. つまり, 目標の初期Seed 0x500305BA
での強制消費数は3F, 初めにウツギ博士に電話を掛けるときに使われる乱数は S[4]
となります.
次に待機時間のずれをタイマー側にも反映させましょう. 30秒間の待機時間を 26F
だけ延長したいので 30s * 60 + 26F = 1826F
で 1826F
を待機時間として設定すれば良いことになります.
この設定でもう一度 2~5
の操作をやってみたところ, 見事徘徊ポケモンの位置が 33,30,22
となり, 初期Seedを 0x500305BA
に合わせることに成功しました!
めでたい! 🎉🎉🎉
今回は2回目のチャレンジで初期Seedを合わせることに成功しましたが, 1/60秒間隔での操作が要求されるので割りとシビアです. いつかは成功するのでめげずに何度も挑戦してみましょう.
ここまでの操作をおさらいします.
- エメタイマーで30秒間のタイマーをセットする
- ハードの電源を入れ,
本体設定パネル > 日付&時刻パネル > 日付パネル
及び 本体設定パネル > 日付&時刻パネル > 時刻パネル
からハードの日時・時刻を2016年11月26日3時10分0秒に設定する
- 直ぐにハードを再起動し, 上画面の時計の針が10秒を指した直後にHGを選択する. 同時にエメタイマーを起動する.
- 「つづきからはじめる」ボタンの画面で待機し, エメタイマーのカウントが0になったらボタンを押す
- ゲームが再開したらポケギアのマップを開き, 徘徊ポケモンの位置から初期Seedを特定する
- 初期Seedが目標のものと異なっていたらずれを確認し, タイマーにずれを反映させて
2
に戻る. 一致すればそこで終了.
乱数を消費する
初期Seedを合わせ終わったので後は乱数を消費するだけです. 17回電話を掛けた場合 *15, 上で計算した乱数列から次の順番でメッセージが流れるはずです.
n |
S[n] |
r[n] |
r[n] % 3 |
メッセージ(先頭のみ) |
4 |
0x1017853E |
0x1017 |
0 |
ポケモンの しんかというのは~ |
5 |
0xD090FFD9 |
0xD090 |
1 |
カントーには まだ ぼくの〜 |
6 |
0xF6876DD8 |
0xF687 |
0 |
ポケモンの しんかというのは~ |
7 |
0x0831F56B |
0x0831 |
0 |
ポケモンの しんかというのは~ |
8 |
0xB1CE7902 |
0xB1CE |
2 |
ポケルスが くっついた~ |
9 |
0xB754824D |
0xB754 |
0 |
ポケモンの しんかというのは~ |
10 |
0x233D513C |
0x233D |
0 |
ポケモンの しんかというのは~ |
11 |
0xC0443EFF |
0xC044 |
2 |
ポケルスが くっついた~ |
12 |
0x207AE506 |
0x207A |
1 |
カントーには まだ ぼくの〜 |
13 |
0xEEBFB801 |
0xEEBF |
0 |
ポケモンの しんかというのは~ |
14 |
0xA37806E0 |
0xA378 |
1 |
カントーには まだ ぼくの〜 |
15 |
0x5D738DD3 |
0x5D73 |
1 |
カントーには まだ ぼくの〜 |
16 |
0x439C0D4A |
0x439C |
1 |
カントーには まだ ぼくの〜 |
17 |
0x66BA94F5 |
0x66BA |
0 |
ポケモンの しんかというのは~ |
18 |
0x015272C4 |
0x0152 |
2 |
ポケルスが くっついた~ |
19 |
0x36AAF5E7 |
0x36AA |
2 |
ポケルスが くっついた~ |
20 |
0x356175CE |
0x3561 |
0 |
ポケモンの しんかというのは~ |
動画を取りました.
ウツギ博士乱数
確かに上の表の通りにメッセージが流れています. 乱数調整成功です. 👏👏
ウツギ博士との電話で乱数調整 その2
ただ予想通りにメッセージが流れただけではつまらないのでもう少し高度なことをしてみましょう. 今度は,
- ウツギ博士が「ポケモンの しんかというのは~」というメッセージを15回以上連続で話す
ことを乱数調整してみます. ウツギ博士に進化について熱く語ってもらいましょう.
乱数と初期Seedの計算
まず目的のメッセージを15回以上連続で話す乱数 S[n]
, 及びその初期Seedを計算しなければなりませんね. 本来であれば既存のツールを使って計算…といきたいところですかこんなどうでもいい乱数調整のためのツールなんかあるはずがありません. ではどうするか. 無いなら作ってしまいば良いのですよ. ✨😉☝️
という訳でサクッとC++で目的の乱数と初期Seed, 必要な消費数を出力するプログラムを書きました. プログラミング チョットデキルな皆さんなら当然書けますよね???
ウツギ博士にポケモンの進化について熱く語ってもらう.
# ソースコードをclone
$ git clone https://gist.github.com/faf3c48f67d999fc8072795e6c67a901.git abuse-dr-utsugi
# コンパイル
$ cd abuse-dr-utsugi
$ g++ AbuseDrUtsugi.cpp -o AbuseDrUtsugi.exe -std=c++11
# 実行
$ ./AbuseDrUtsugi.exe
n S[n] S[0]
10 0X23E5E254 0XD2160812
5 0X2A991401 0X89070502
9 0X3B4F3C64 0X96010A6D
4 0X72038E62 0X4114049E
5 0X7B215E11 0XE8040852
6 0X810C32E0 0X89070502
5 0XA2B1DC2D 0X4114049E
いくつか初期Seedの候補か出力されましたが, ここでは初期Seed 0x89070502
を狙います. 先程と同様に手計算で起動時刻を求めました.
- DSメニューからゲームを選択: 2099年5月20日7時2分10秒
- 待機時間: 1533F
- 「つづきからはじめる」を選択: 2099年5月20日7時2分35秒
初期Seedを合わせる
エメタイマーは以下のように設定しておきます.
僕の場合はセーブ直前での徘徊ポケモンの位置が 32,29,5
だったので「HGSS徘徊 初期seed確認」には以下のように設定しました.
1回目
徘徊ポケモンの位置は 30,31,12
でした.
0x502 - 0x4ee = 20
より, 待機時間を 20F 延長し, エメタイマーに 1553F を設定しました.
2回目
徘徊ポケモンの位置は 30,32,9
でした.
おや…? 徘徊ポケモンの位置を正しく入力したのに初期Seed候補が出力されませんね. ここで左下にある「秒のずれを許容」にチェックを入れて秒や分のずれを考慮した初期Seed候補検索をしてみましょう.
初期Seed候補 0x8a070500
が出力されました. ここで, 0x8a - 0x89 = 1
より, 「つづきからはじめる」を押した時刻が1秒遅れたと推測されます. エメタイマーの設定はそのままにして, DSメニューからゲームを選択する時刻を1秒早めて(2099年5月20日7時2分9秒)みます.
3回目
徘徊ポケモンの位置は 29,35,2
でした. 成功です 👍
乱数を消費する
強制消費数は3Fなので, 初めにウツギ博士に電話を掛けるときに使われる乱数は S[4]
となります.
乱数列を計算すると, 以下のようになっています.
n |
S[n] |
r[n] |
r[n] % 3 |
メッセージ(先頭のみ) |
0 |
0x89070502 |
0x8907 |
0 |
ポケモンの しんかというのは~ |
1 |
0xDD101E4D |
0xDD10 |
0 |
ポケモンの しんかというのは~ |
2 |
0x05A6BD3C |
0x05A6 |
0 |
ポケモンの しんかというのは~ |
3 |
0xCE0F3AFF |
0xCE0F |
2 |
ポケルスが くっついた~ |
4 |
0xC0B03106 |
0xC0B0 |
2 |
ポケルスが くっついた~ |
5 |
0x2A991401 |
0x2A99 |
0 |
ポケモンの しんかというのは~ |
6 |
0x810C32E0 |
0x810C |
0 |
ポケモンの しんかというのは~ |
7 |
0xE2F249D3 |
0xE2F2 |
0 |
ポケモンの しんかというのは~ |
8 |
0x1FDA194A |
0x1FDA |
0 |
ポケモンの しんかというのは~ |
9 |
0x5CCDB0F5 |
0x5CCD |
0 |
ポケモンの しんかというのは~ |
10 |
0x41FD5EC4 |
0x41FD |
0 |
ポケモンの しんかというのは~ |
11 |
0x5B5971E7 |
0x5B59 |
0 |
ポケモンの しんかというのは~ |
12 |
0xE57441CE |
0xE574 |
0 |
ポケモンの しんかというのは~ |
13 |
0x51E12929 |
0x51E1 |
0 |
ポケモンの しんかというのは~ |
14 |
0xB61F64E8 |
0xB61F |
0 |
ポケモンの しんかというのは~ |
15 |
0x0F8D073B |
0x0F8D |
0 |
ポケモンの しんかというのは~ |
16 |
0x29E26E92 |
0x29E2 |
0 |
ポケモンの しんかというのは~ |
17 |
0x6A05F09D |
0x6A05 |
0 |
ポケモンの しんかというのは~ |
18 |
0xEA45A94C |
0xEA45 |
0 |
ポケモンの しんかというのは~ |
19 |
0x36069DCF |
0x3606 |
0 |
ポケモンの しんかというのは~ |
20 |
0xA200A396 |
0xA200 |
0 |
ポケモンの しんかというのは~ |
21 |
0xC821BB51 |
0xC821 |
2 |
ポケルスが くっついた~ |
よって16連続でウツギ博士がポケモンの進化についての話をしてくれます. 熱いですね.
動画を取りました.
ウツギ博士乱数 (16連続同一メッセージ)
確かに16連続で熱い話をしてくれていますね. 乱数調整成功です 👏👏
この記事で行う乱数調整はこれで終わりです *16. 乱数列を意識しながら乱数調整を行ったので, 疑似ランダムな事象の裏ではどんな計算が行われているのかということをイメージする力が付いたのではないでしょうか. 以下で他にどんなことに対して乱数調整できるのか軽くリストアップしたので是非ご覧になって下さい (色乱数や高個体値ポケモンの乱数はありきたりすぎるのでカットで 👋).
他にどんなことが乱数調整できるの?
面倒になってきたので雑にいきます.
d.hatena.ne.jp
僕がパッと思いついたのはこれくらいですが, これは入れるべきというものがあれば教えてください 🙏
乱数調整の勉強する際に参考になるサイト
上に同じく, これは入れるべきというものがあれば教えてください 🙏🙏🙏
おわりに
以上が Pokémon RNG Advent Calendar 2016 1日目「乱数調整 入門」となります. ここまで読んでくださった方々, ありがとうございます! お疲れ様でした! 😃
期限までに書ききることができなかったのは残念 *17 ですが, Pokémon RNG Advent Calendar 2016 では興味深い記事が次々と投稿されてきています.
残りの日も Pokémon RNG Advent Calendar 2016 で楽しんでいきましょう!
www.adventar.org
2日目は @sub_827 さんの担当です!