FF12の乱数について

概要

FF12が内部でどのように乱数を扱っているか調べたので、分かったことを書くよ。

目次

概要
表記
注意
導入
生成器Aの特徴
A系列のよくある利用パターン
A系列を使う処理
A系列の乱数を消費しない行動
応用例
分かっていないこと

表記

この文章では以下の規則に従う。

注意

この文章では、FF12内の乱数の規則性を観察し、それを制御する方法について述べている。こういうプレイははおそらく開発者の意図ではないし、不用意に使えばゲームバランスを崩す。この文章を読んだことでFF12を十分に楽しめなくなったとしても私は責任を負わない。

この文章の記述は全体的に検証が不十分なので、誤っている可能性が十分にあることに注意。

導入

一般にコンピュータゲームでは、処理の過程で乱数が必要な場合、一定の手順に従って計算で導き出した値を乱数の代わりに使う。こういうものを擬似乱数と呼ぶ。擬似乱数は、手順に従って作られているので乱数(無秩序な値)ではないが、乱数とみなしても実際上問題がないくらい規則性が目立たないように作られている。詳しくはググって欲しい。

FF12は少なくとも三つの擬似乱数生成器を持っていて、これらはそれぞれ独立に動作する。このうち一つは特に重要で、攻撃のダメージ計算や命中判定、トレジャーの中身の決定など、プレイヤーの目に触れやすい事象の多くを司っている。これを生成器Aと呼ぶことにしよう。以下ではほとんど生成器Aについてのみ扱う。

生成器Aの特徴

生成器Aは、0以上232未満の(ほぼ)ランダムな整数を一度に一つ生成することができる。整数を生成するたびにAの内部状態は刻々と変わっていくが、電源投入直後、およびハードウェアリセットの直後では、Aの内部状態はある一定の値になっている。このことによって、Aは毎回同じ系列の乱数列を生成する。この乱数列は具体的に数列として計算することができて、例えば最初の項は1820836235で、次は1372235862になっている。つまり、電源投入後最初に乱数を生成するとき、Aは常に1820836235を生成する。

したがって、Aの今後の出力を予測したい場合、それがAにとって電源投入後「何個目」の乱数なのかが分かれば十分である。言い換えれば、Aは内部に固定の(無限の長さの)乱数表を持っていて、乱数が要求されるたびに先頭から一個ずつ読んで使っているかのように振舞う。この乱数表のことを系列Aと呼ぶことにする。また、今までにAがn個の乱数を生成していた場合、次に生成されるのは系列A上の(n+1)番目の値である。このことを、現在、乱数表の上で位置nにいると考えて、nを(系列Aについての)乱数位置と呼ぶことにする。さらに、ある処理で乱数位置がk進んだ場合、その処理によって「乱数がk個消費された」と表現する。

系列Aが具体的にどのように計算されるかを知らなくても以下を読むうえで支障はないが、興味のある人のために書いておくと、AはMersenne Twister(MT19937)によって計算される。ただし初期化ルーチンは最新の2002年版のものではなく、1998年版のものを使う。seedは4537(4357じゃないよ!)。

A系列のよくある利用パターン

A系列は0から232-1までの値からなるが、実際にはもっと小さい値が必要とされることが多い。例えば、n個の選択肢から一つをランダムに選ぶ場合、FF12でよく使われているのは剰余を使う方式である。具体的には、乱数を一つ生成し、それをnで割った余りを取る。すると、0からn-1までの整数がほぼ等確率で得られる。

これの応用として、「k%の確率で何かを行う」という処理がある。これには、乱数を一つ生成(vとする)し、v % 100がk未満だった場合のみそれを行う。v % 100は0から99までの値をほぼ等確率で取るので、v % 100 < kが満たされる確率は約k%となる。この手続きは広範に使われるので、「(確度kの)%判定」という名前で呼ぶことにする。

確度kの%判定:
  A系列の乱数を一個生成し、vとおく。
  v % 100 < kなら判定成功、そうでなければ判定失敗。

A系列を使う処理

以下で、攻撃や魔法などの各種アクションの処理において系列Aの乱数がどのように使われるかについて書くが、調査に当たっては、周りに敵が居ない状態で、調査に関係ないステータス効果を全て解除した上で、味方を対象にしてアクションを実行している。実戦で敵に対して行動する場合には必ずしもそのまま当てはまらないかもしれない。

銃による攻撃を行い、それが命中する場合、乱数を八個消費する。内訳は以下の通り。

ノックバック率の算出(2)

乱数を二個生成し、v0, v1とする。ノックバック率は以下になる。

ノックバック率 = 武器のノックバック値(10) + v0 % 攻撃者のレベル - v1 % 対象のレベル (計算結果が負のときは0)

これはバトルアルティマニアに載っている式と微妙に違うが、多分こちらが正しい。

ノックバック判定(1)

ノックバック率を確度とする%判定を行う。自分を攻撃する場合は成功してもノックバックしないが、判定自体は実行する。

非ミス判定(1)

非ミス率を確度とする%判定を行う。非ミス率は通常100で、判定はかならず成功するが、それでも乱数は消費される。この判定に失敗した場合、以下の判定は行わず、乱数を四つ消費するだけで終了する。

クリティカル判定(1)

クリティカル率(5)を確度とする%判定を行う。

ダメージ計算(2)

まず、基準値が与えられると、基準値の1倍以上9/8倍未満のランダムな値を得る手続きrand98を次のように定義する。

rand98(基準値):
  基準値が0なら、乱数を消費せず0を結果とする。
  そうでなければ、乱数を一個生成し、vとする。次のように結果を定める。
  結果 = 基準値 + (v % ⌊基準値 * 12.5⌋) / 100

要するに、取りえる範囲の中にある0.01の整数倍から、ランダムに一つ選んで結果としている。

銃のダメージはrand98を使って次のように計算される。

rand98(攻撃値)を実行し、結果をr0とする。
rand98(攻撃値)をもう一度実行し、結果をr1とする。
次のようにダメージを定める。
ダメージ = ⌊r0 * r1 * 倍率⌋

ここで「倍率」とは、属性・天候の影響やHP満タン攻撃力UPなどのオプションによる補正をすべて乗算したものである。

追加効果付加判定(1)

追加効果発生率を確度として%判定を行う。追加効果のない弾を装備している場合でも変わらず乱数を消費する。

銃による攻撃をキャラクターに指示し、攻撃モーションに入った直後に装備変更などで攻撃をキャンセルすると、乱数は四つだけ消費される。

素手

素手による攻撃では、通常十個の乱数が消費される。n hitsの連撃が発生した場合、全て命中するなら消費は9n+12個になる。

消費される十個は、前半と後半の五個ずつに分けられる。連撃が発生した場合、後半五個の判定のみが連撃回数分繰り返される。以下は内訳。

ノックバック率の算出(2)
ノックバック判定(1)

この二つは銃の場合と同じ。

不明(1)

使途不明だが乱数を一つ消費する。

連撃判定(1)

連撃率を確度とする%判定を行う。連撃率は次の式によって算出される。

連撃率 = ⌊連撃値 * 0.7⌋ (源氏の小手を装備していない場合)
連撃率 = ⌊連撃値 * 1.8⌋ (源氏の小手を装備している場合)

この判定に成功した場合、さらに確度Mの判定を11回行い、(成功した回数+2) hitsの連撃が発生する。ただし11回の判定にすべて成功しても12 hitsしか発生しない。Mは次のように定義される。

M = 8 (⌊最大HP/4⌋ ≦ 現在HP)
M = 16 (⌊最大HP/8⌋ ≦ 現在HP < ⌊最大HP/4⌋)
M = 32 (⌊最大HP/16⌋ ≦ 現在HP < ⌊最大HP/8⌋)
M = 64 (現在HP < ⌊最大HP/16⌋)

n hitsの連撃が発生する場合、後半部分の判定をn回繰り返すが、後半部分と後半部分の間(計n-1回)に、使途不明の乱数を四つずつ消費する。

以下が後半部分。

盾ガード判定(1)

盾の回避率を確度とする%判定を行う。成功した場合以下は行わない。

武器ガード判定(1)

武器の回避率を確度とする%判定を行う。成功した場合以下は行わない。

パリィ判定(1)

パリィ率を確度とする%判定を行う。成功した場合以下は行わない。

非ミス判定(1)

非ミス率を確度とする%判定を行う。失敗した場合以下は行わない。

ダメージ計算(1)

ダメージの算出手順は次の通り。

rand98(攻撃値)を実行し、結果をvとする。
以下のようにダメージを定める。
ダメージ = ⌊(v - 防御値) * 倍率⌋

ここで「倍率」は、基礎倍率である(力 * (力 + レベル) / 256)に各種補正倍率を掛けたものである。

斧、ハンマー、ハンディボムによる攻撃のダメージ計算

まず、基準値として実数xが与えられると、0以上x未満のランダムな値を生成する手続きrand11を次のように定義する。

rand11(x):
  x < 0.01なら、0を結果とし、乱数を消費せずに終了。
  そうでなければ、乱数を一個生成(vとする)し、結果を次のように定める。
  結果 = (v % ⌊x * 100⌋) / 100

この定義を使って、これらの武器のダメージは次の手順で計算される。

rand98(攻撃値)を実行し、結果をaとする。
c = (a - 対象の防御値)と定める。c < 0ならばダメージは0で、これ以上乱数を消費せずに終了。
そうでなければrand11(倍率)を実行し、結果をrとする。ダメージを次のように定める。
ダメージ = ⌊c * r⌋

ここで「倍率」とは、(力 * (活力 + レベル) / 128)の基礎倍率に各種補正値を掛けた値である。

ケアル、ケアルダ

乱数を一個だけ消費して、回復量の計算に用いる。計算手順は素手によるダメージ計算と同様。ただし攻撃値の代わりに魔法威力、防御値の代わりに0を使い、基礎倍率は(魔力 * (魔力 + レベル) / 256 + 2)として計算する。

ポーション、ハイポーション、エクスポーション、エーテル、ハイエーテル

乱数を一個だけ消費する。使途は不明。

トレジャーの開封

トレジャーを調べると、まずギル判定が行われる(確度はトレジャーごとに異なる)。その後の処理は次の通り。

ギル判定に成功した場合
そのトレジャーに設定されている額が固定なら、乱数を消費せずにその値のギルを得る。そうでなければ、乱数を一個発生させ(vとする)、(1 + v % 最大値)ギルを得る。
ギル判定に失敗した場合
非レア判定を行う。確度は、ダイヤの腕輪を装備しているなら90、していないなら50。ここで「非レア」とは、シナリオアルティマニア記載のトレジャー表のうち左側の欄に書かれている内容を指す。

セーブデータのロード

定義から、電源投入時の乱数位置は常に0だが、セーブデータをロードして操作可能になるまでに数百個の乱数が消費される。このとき、同一のデータでも、ロードを複数回試すと、操作可能になった時点での乱数位置が完全には一定しない。"セロビ台地/街道の休息所"でセーブしたデータで試したところ、乱数位置はおよそ570±10の範囲内だった。未検証だが、この挙動は本体の型番によって異なる可能性がある。なお、この調査に使用したPS2はSCPH-70000。

A系列の乱数を消費しない行動

興味のある人のために、系列A以外に見つかっている二つの擬似乱数生成器について分かっていることを書いておく。どちらの生成器も線形合同法を使っている。線形合同法の漸化式を「xn = (xn-1 * a + b) % m」と書くことにすると、一方は(a, b, m) = (6364136223846793005, 1, 264)で、各項xnに対して⌊xn / 232⌋ % 231を出力とする。初期値はおそらく1。もう一方は(a, b, m) = (75, 0, 231-1)で、各項をそのまま出力する。初期値はおそらく305420679。

応用例

乱数位置の特定

プレイ中、何らかの方法でその時点の乱数位置を具体的に特定することができれば、乱数位置を意図的に調整することで、その後の乱数の出方をある程度制御することができる。例えば、乱数位置を100個進めたいと思ったなら、自分に向けて銃を12回撃ち(死なないように属性吸収などの準備を講じた上で)、その後ケアルを4回唱えれば良い。

乱数位置の推定には、ダメージ量や回復量の数値を使うのが手軽だろう。乱数系列Aは完全に計算で求まるので、各乱数位置に対して予め例えば「その位置でケアルダを唱えたときの回復量」を計算して表にしておき、実際に2~3回ケアルダを唱えてみてその回復量の並びを表から探す。「全ての乱数位置」を網羅した表を作るのはもちろん不可能だが、実際には10万未満の乱数位置の範囲で表を作れば大抵事足りる。

考え方としてはこの作業は単純だが、手で計算して表を作るのは計算量が膨大なので現実的でない。この作業を補助する簡単なJavaScriptツールを同梱したので、興味があれば試して欲しい。

5hits法の原理

5hits法とは、もともとIZJSで発見された手法で、大まかに以下の手順に従うことでトレジャーからレアアイテムを高確率で得ることができるというものである。

  1. トレジャーの近くでセーブし、ハードウェアリセットをする。
  2. ロードし、目標のトレジャーがPOPするまでゾーンチェンジを繰り返す。ただし、ある程度繰り返してもPOPしない場合は最初からやり直す。
  3. 自分を繰り返し殴る。このとき、ガードが発生しないように盾は外すか、カメオのベルトを装備する。
  4. 5 hitsの連撃が発生したら、ダイヤの腕輪を装備して目的のトレジャーを開ける。
  5. 条件として、トレジャーを開けるまで敵に攻撃されてはいけない。

結論から言うと、この手法は、トレジャーを開ける時点での乱数位置を1445に固定することで機能している。この付近の系列Aは次のようになっている。

1444: 2510889783
1445: 1861786791
1446: 1379767799
1447:   80686868

位置1445と1446の値は下二桁がそれぞれ91と99で、どちらも大きい。これは、かなり確度の大きな%判定でも失敗するということである。実際、乱数位置が1445の状態でトレジャーを開けた場合、ギル判定と非レア判定はどちらも最大確度90なので確実に失敗し、結果としてレアアイテムが手に入る。なお、IZJSの透明武具は非レア判定の確度が95であるようだが、比較対象は99なのでやはり失敗する。

5 hitsの連撃自体は、乱数位置が1388のときに素手で攻撃すると発生する。

1387:  382760416
1388: 2267807233 ここで攻撃; ノックバック確率計算1
1389: 1886692943 ノックバック確率計算2
1390: 2131861130 ノックバック判定
1391: 2952945076 (不明)
1392: 2341692401 連撃判定(確度3) → 成功
1393: 3404601602 連撃回数増加判定(確度8) → 成功
1394: 2203834536 連撃回数増加判定(確度8) → 失敗
1395:  961042754 連撃回数増加判定(確度8) → 失敗
1396: 2005317402 連撃回数増加判定(確度8) → 成功
1397: 2981387111 連撃回数増加判定(確度8) → 失敗
1398: 2833431843 連撃回数増加判定(確度8) → 失敗
1399: 3060953428 連撃回数増加判定(確度8) → 失敗
1400: 3953158966 連撃回数増加判定(確度8) → 失敗
1401: 3263761202 連撃回数増加判定(確度8) → 成功
1402:  161153939 連撃回数増加判定(確度8) → 失敗
1403: 1977869617 連撃回数増加判定(確度8) → 失敗; 5 hitsの連撃発生
1404: 3808337799 盾ガード判定(確度0) → 失敗
1405: 3799551709 ...(以下略)...

5 hitsの連撃によって57個の乱数が消費されるので、連撃後の乱数位置はちょうど1445になる。

なお、連撃が発生しない場合、自分を殴る毎に乱数位置は10ずつ進む。そのため、位置1388に到達して5 hitsを発動させるためには、乱数位置の一の位がちょうど8である状態で自分を殴り始めなければいけないように思える。しかし、実際には、一の位は0か1でもよいことがある。一の位が0なら、位置1070で4 hitsの連撃が発生し、連撃終了後には位置が1118になるので、あと27回の自打で1388に達する。一の位が1の場合、位置751で3 hitsの連撃が発生し、乱数が39個消費されて位置が790になる。これもあと26回の自打で1070に達する。

このように、乱数位置1388は自打の繰り返しによって比較的到達しやすく、滅多にない5 hitsという連撃を発生させ、しかも連撃直後の乱数位置がトレジャーの開封に非常に適しているという、良い性質を併せ持った稀有な位置であり、5hits法はこの特殊な性質をうまく利用している。

"セロビ台地/交差ヶ原"のトレジャーPOP法則

乱数の絡む事象がどのような法則に基づいて決定されるのか調査する手順を、具体例を通して紹介する。例として、"セロビ台地/交差ヶ原"内の各トレジャーが出現するかどうかがどのように決定されるか調べる。

シナリオアルティマニアのp.122によると、トレジャーが出現するかどうかはそのゾーンを訪れたときに決定される。そこで、まず、交差ヶ原に進入するときにいくつの乱数が消費されるか調べてみる。

まず、リセット後、街道の休息所のセーブデータをロードし、銃のダメージを使って乱数位置を特定する。その後交差ヶ原に入り、再び乱数位置を特定、ゾーン進入直後の乱数位置を逆算する。二つの乱数位置の差を取ることで、交差ヶ原に進入したとき16個の乱数が消費されたことが分かった。

街道の休息所に戻り、もう一度同じ手順で進入時の乱数消費を計る。すると、今度は6個しか乱数が消費されなかった。これは、トレジャーの再出現の条件を満たしていないためにトレジャーの出現判定が省略されたからだと考えられる。アルティマニアによると交差ヶ原内のトレジャー出現位置は10箇所であり、最初の16個の乱数消費のうち10個がトレジャーの出現判定に費やされていたと考えると辻褄が合う。

では、消費された16個の乱数のうちどれがどのトレジャーの出現判定に利用されたのか考える。アルティマニアによると10箇所のトレジャーの出現率は全て50%となっている。そこで、ひとまずトレジャーの出現判定が確度50の%判定によって行われていると仮定してみる。消費された16個の乱数について、その位置で確度50の%判定を行った場合の成否を並べると、次のようになった。

n y n n n n n y n y y n n y y n (yは成功、nは失敗)

次に、交差ヶ原を実際に回って、どの位置のトレジャーが出現しているかを確認する。出現していたトレジャーは4つで、具体的には、アルティマニアのトレジャー番号を使って呼ぶと、55, 57, 59, 60だった。

16個の乱数消費のうち、トレジャーの出現判定に使われた乱数位置は連続していると仮定してみる。つまり、トレジャーの出現判定の間に無関係な乱数消費がないと仮定する。先の16個の%判定の合否列のうち、長さ10で、そのうちちょうど4つが成功している部分列は二通りしかない。

n[y n n n n n y n y y]n n y y n および、
n y n n[n n n y n y y n n y]y n

ここまでの仮定が正しければ、このうちどちらかがトレジャー判定の部分に対応している可能性が高い。

今あるデータだけではこれ以上のことは何も言えそうにないので、一旦セーブ/リセットして同様のデータを別ケースについて集める。これを繰り返して計5回分のデータを集めた。

0: n y n n n n n y n y y n n y y n    出現: 55, 57, 59, 60 (これが最初のデータ)
1: n y n y n y n y n n n y n y y n y  出現: 52, 53, 58, 59
2: y n y n y n n n y n y y n y n n y  出現: 51, 55, 57, 59, 60
3: n y n n y y n n y n y n y n y n n  出現: 51, 52, 57, 60
4: y n y y n n n n y y n y n n y y    出現: 51, 53, 56, 60

まず予想外のこととして、交差ヶ原進入時の乱数消費が16個でなく17個である場合があった。このケースでは、トレジャーの出現判定部分の前か後かのどちらかで、追加の乱数消費があったと仮定することにする。

以上の仮定のもとで、結果を矛盾なく説明できる乱数の使い方が一つだけあることが分かった(このチェックにはコンピュータを使ったが、人力でもできないことはないと思う)。具体的には以下の通り。

  1. 乱数を1個または2個消費する(使途不明、1個か2個かの条件も不明)。
  2. 乱数を10個消費して、一個ずつトレジャーの出現判定に使う。判定の順序は、55, 53, 51, 52, 54, 58, 57, 56, 60, 59。
  3. 乱数を5個消費する。rémy lachaudによるとこれはトラップの種別決定に用いられる(一箇所につき一個)。

これで、計16個または17個の乱数を消費することになる。

以上で、交差ヶ原のトレジャーPOP法則について仮説を立てることができたので、次はこれを検証する。もう一度リセットして実際に試したところ、大体仮説どおりのPOP状況になっていた(本来はもっとまじめに検証すべきなのだろうが、単純作業の繰り返しにいい加減飽きていたので適当)。最後の締めくくりとして、乱数位置を調整してリボンのトレジャーをPOPさせ、実際にリボンを入手して終了。

交差ヶ原の地図に、各トレジャーの出現判定の順序を書き込むと次のようになる。

交差ヶ原の地図。判定順は西から順に9, 10, 6, 7, 8, 1, 5, 2, 4, 3

分かっていないこと

現状では分かっていることの方が少ないが、すぐ思いつく未調査点は以下。