仄暗いレートの底から

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ぬおーでもわかる乱数調整 其の一

はじめに

この記事はPokémon RNG Advent Calendar 2016 16日目の記事です
Pokémon RNG Advent Calendar 2016 – Adventar


この記事はいわゆる「ポケモンの乱数調整入門」の記事です。
ただし、「ぬおーでもできる」ではなく「ぬおーでもわかる」となっている通り、「乱数調整ができるようになる」ことを目指すのではなく、

・「乱数調整とは具体的にどういう行為なのか」を理解し
・ゲーム内のランダムな事象を「擬似乱数/乱数列」の概念から理解すること

を目指すものです。

ですから、「仕組みとかどうでもいいから手っ取り早く理想個体が欲しい!」という方は、ぬおーさんではなくヤドンさんにお願いしてください。

前置きで難しそうな印象を受けるかもしれませんが、できるだけイメージがつかめるよう書いていくつもりですので、お暇があるなら読んでいってください。

あと、同じPokémon RNG Advent Calendar 2016にて、mizdraさんが同じような内容の記事を書いてくださっています。ていうかほぼ内容被ってますね。一応あちらは4世代メイン、こちらは3世代メインなので差別化はできています。劣化とか言わないで


準備するもの

今回は特にありません。短めのお話だけなので。


Linear Congruential Generators

さて早速本題に入ります。

まずは、「そもそも、乱数調整の『乱数』とは何ぞや?」 というお話から。

簡単に言っちゃうと、ランダムな数です。そのまんま。
ですが、この「ランダム」というのが難しいんです。プログラムにとっては。
だって命令された通りのことしかできませんから。
「1から6までの数字を適当に選んでよ」と言われても、「適当に」ができないんです。

でも、ゲーム内のNPCはランダムに動いてます。
草むらを歩いてポケモンが出てくるタイミングはランダムです。
出てくるポケモンの種類も、その性格・性別・個体値etc.もランダム。
相手の行動もランダム。技の追加効果もランダム。威張る自傷も麻痺バグもランダムです。
どうやって「ランダムな決定」ができないはずのプログラムが、こういったランダム性を作り出しているのでしょう?

そこで出てくるのが「擬似乱数」です。
擬似乱数とは、本当の意味でランダムではないけれど、「乱数」に要求される性質をだいたいクリアしている数の並びのこと。
「命令された通りのことしかできないのなら、可能な限りランダムっぽく見える動きをするように命令すればいいじゃん」という発想です。

例えば…

0, 1, 2, 3, 4, 5, 6, 7


1, 1, 2, 2, 3, 3, 4, 4

は、パッと見「ランダムな並び」とは思いませんよね? むしろ綺麗に並んでいるように見えます。
では、

1, 6, 7, 4, 5, 2, 3, 0

これだとどうでしょうか。
少なくとも、上の2つよりはずっと「ランダムな並び」に見えませんか?
でも実はこれ、計算で作った並びなんです。
「前の数に5を掛け、1を足した数字を8で割った余り」を並べているんです。疑っている方はちゃちゃっと計算してみてくださいな。

…と、このような簡単な計算で、数字の並びをランダムっぽく見せることが可能なんです。
「前の数にAをかけ、Bを足し、Mで割った余り」を並べていくことで擬似乱数を作り出す方法を、

Linear Congruential Generators

と言います。LCGと略されることが多いです。

ちなみにLCGを数式(漸化式)で書くと

x[n+1] = Ax[n] + B % M

となります。上で使ったのは

x[n+1] = 5x[n] + 1 % 8

です。

さっきの擬似乱数列は、0の次に1に戻ります。
つまり、{1, 6, 7, 4, 5, 2, 3, 0}の並びを延々と繰り返すわけです。
8回計算を繰り返せば同じ数に戻るので、「周期が8である」といいます。

LCGは、A, B, Mの値を上手くとれば、周期がとても長くなります。
ポケモンの3世代、4世代で使われているLCGは

x[n+1] = ‭1103515245‬x[n] + ‭24691‬ % ‭4294967296

です。周期は、Mと同じ‭く4294967296‬です。四十二億九千四百九十六万七千二百九十六。とても長いですね。
3世代のポケモンでは、このLCGを使って、ランダム性を作り出しているというわけです。
(4世代、5世代は別の種類の、もっと高度でかっこいい擬似乱数も使っています。
その名も「メルセンヌ・ツイスタ」。必殺技みたいですね)

さて、「前の数にAをかけ、Bを足し、Mで割った余り」を並べていく、と言いましたが、

じゃあ、最初の数はどうするんだよ?

と思った方もいらっしゃるのではないでしょうか。
そうなんです。最初の数は、外から与えてやらないといけないんです。
これがいわゆる「初期seed」です。
ここらへんについては長くなり、実際に「乱数調整」を行う話に直接関係してくるので、また後の回で扱うことにします。(失踪しないように頑張ります)
ただ、Emでは基本的に初期seedが0で固定ということだけ書いておきます。


乱数消費

上で「LCGを使ってランダム性を作っている」と書きました。
今回のお話の中で、一番重要なのがこれなんです。
「ゲーム中のランダムな事象はすべて擬似乱数の計算によって表現されている」
これさえ理解すれば、乱数調整について半分理解したも同然です。(残り半分は具体的な処理についてです)
すべて、というのがポイント。ランダム要素があれば、そこには必ず"乱数消費"があります。

今回のシメは乱数消費についてです。
4世代、5世代の乱数調整ではぺラップを鳴かせまくりますが、あれも乱数消費のためです。

数列の、並んでいる数の1つ1つを数学用語で「項」と呼びます。
上で挙げた{1, 6, 7, 4, 5, 2, 3, 0}の乱数列を例にとると、第1項が1、第2項が6、…、第8項が0で、第9項で第1項と同じ1に戻ります。
擬似乱数列の項1つ1つのことを、"seed"と言ったりします。
seedは英語で「タネ」のことです。乱数を生み出すタネというわけです。
(本来は〈初期値として与える値 = 初期seed〉だけをseedと呼ぶらしいですが、ポケモン界隈では〈seed = 乱数列の各項〉という定義が広まっており、なおかつ乱数列の項に手を加えて乱数値を得るので〈seed = タネ〉と表すのが表現として不適だということもありません。なので、本記事では"seed"を上記の意味で使い、また、解説することにします。)

3世代の擬似乱数は0から4294967295の数の列だと言いましたが、ゲームを起動している間、内部値として保存されているのは、そのうち1つだけです。
わざわざ4294967296個の数値の並びをテーブルとして保存して今何番目か、なんてしなくても、次のseedもその次のseedも計算で出せますからね。
seedをLCGの計算式に放り込んで保存されているseedを次のseedに移すことを「乱数消費」または単に「消費」と言います。
例えるなら鼻をかむためにティッシュ箱からティッシュを取り出す感じのイメージですね。汚い

消費が行われるのは主に乱数を使いたいときです。
ゲーム開始時に表IDと裏IDを決定する処理を例に見てみましょう。
裏IDが先に決まり、次に表IDが決まるようになっています。

初期seedを0番目として、n番目のseedをS[n]と呼ぶことにします。
内部値としてS[n]が保存された状態でID決定処理に入ると、まず乱数を消費。得られたS[n+1]から裏IDが計算(かっこよくいうと"生成")されます。
そしてさらにもう1つ乱数を消費。得られたS[n+2]から表IDを生成。これで終わりです。
図にするとこんな感じ。
s_ID.png


このように、原則1回の生成処理につき、1回の乱数消費が行われます。
1回の消費で得られたseedが異なる2つの計算につかわれることはありません。

ちなみに、せっかく乱数消費して得られた乱数値が、何にも使われないこともあります。
3世代では画面を描画する処理と一緒に乱数消費が行われます。

コマ送り①

コマ送り②
……2枚とも同じに見えますが、下の男の子が1ドット分右に動いています。
これで1消費されます。

描画は1秒間に約60回行われ、描画1回を「1フレーム」と数えます。60フレームにかかる時間≒1秒です。
つまり、3世代では1秒間に60回の消費が行われているのです。
4世代5世代では1消費にぺラップを「ア″----」と鳴かせる必要があると考えると、ものすごい速度です。
これも後の回で詳しく書きますが、3世代の乱数調整はこの描画ごとの消費に頼ることになります。
というか乱数調整をする際は描画ごとの消費以外に乱数が消費されず、されても誤差程度でわかりません。このことから、消費数の単位としてもフレーム(F)が用いられています。


さいごに

まとめると、

・ゲーム内のランダム要素は擬似乱数の計算で表現されている
・3世代ではLCGという方法で擬似乱数が作られている
・1回の乱数計算につき1回の乱数消費が行われる

といったところでしょうか。

以上、ぬおーでもわかる乱数調整 其の一でした。
書こう書こうと思いつつ先延ばしにしてきたのをようやく公開できました。
できる限り自分の中のイメージを引っ張り出して書いたつもりですが、わかりにくい・これは違う等の意見や誤字・脱字は遠慮なくご指摘ください。答えたり答えなかったり反映させたりさせなかったりします。

ここまで読んでくださって、本当にありがとうございました。
続きは近いうちに書きます。多分。次回は個体生成の過程を見てみる予定。
スポンサーサイト

Powered By FC2ブログ
Template By oresamachan
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。