なぜ、最終的なすばやさは、8192(2^13)個の数で表されるのか?
はじめに
ポケモンの、オーバーフローについての記事です。
すばやさのオーバーフローについて、有意義な会話をできたので、それを引用し、仮説をまとめます。
前提知識
元々のすばやさが、実際に行動順の判定に使われるまでには、いくつかの工程があります。
詳しくは、以下の記事で解説しています。
以下の工程です。
A. ポケモンのすばやさ実数値
B. Aの値にランク補正・持ち物などの補正をかける
C. Bの値が10000を超えているなら、10000になる
D. トリックルーム中なら、10000からCの値を引く
E. Dの値が8192以上なら、8192で割った余りになる
今回は、このうち、Eについて取り上げます。
コメントの引用と、一部解説
とも湯と見るダメージオーバーフロー【ポケモンSV】【ゆっくり解説】 - YouTube
のコメント欄から
きっかけ
コメ主さん
ダメージも素早さもカンスト値を決めて、越えたらカンスト値でとめればいいのに、なんで最大値で割るんだよ。
元コメントです。以下がコメントに連なる返信です。
Ksan1024さん
別に最大値で割る処理が入ってるわけではない
たとえば数字が3ケタしかない世界で999に1 を足すと1000になろうとするけど千の位がないので000= 0になる感じ
これがゲームの中では2進数でやってるので65535とかの意味不明な数字で出てきてる
超えたらそこで止める、みたいな処理ってオーバーフローが発生する前提ではすごく作るのが難しい
滅多に起こることじゃないからあんまり気にせずにそのままオーバーフローさせておいていいやって話になる
コメ主さん
YouTubeでみんな割るって言うからコード解析してそういう処理が入ってるのを確認した人がいるのかと思ったら、結局そういう処理無しに変数に代入してるだけか…
それならゲームはできる限り無駄な処理を削りたいからなんの対処もしてないのか?…
Ksan1024さん
実際素早さには変な値で割った余りを求める処理が入ってるらしいのでそこからの誤解かもしれない
それか「こういった計算でオーバーフロー後の値を求められます」という説明を「このような処理が行われているのか!」と誤解した人の話が拡散されているのか
私は解析したわけじゃないけど16ビット32ビットとかの上限値のオーバーフローは一旦桁が溢れた以上「割った余りを求める」っていう処理もできないはずなので入ってないと思う
そのほうが楽というのはそうなんだけど「オーバーフローしたかどうか検出する」というのもまあまあ面倒なので割にあわないといったほうが正確かも
オーバーフローって、自然にあふれてしまうという話なんですよね。
このブログでは、以下記事でちょっと解説しています。
【ポケモン】てんのめぐみ+にじ+チャージビームのオーバーフロー後の追加効果発生確率はおそらく25ではなく24である理由 - テツポンドのブログ
さて、冒頭にあった「実際素早さには変な値で割った余りを求める処理が入ってるらしいのでそこからの誤解かもしれない」という文章が気になりました。
変な値で割った余りを求める処理というのは、先ほどの最終的なすばやさ工程でいうでいうEのことなのですが、私は、ここはオーバーフローと呼んでいいものだと思っていました(後述のとおり変な桁数なので積極的には呼ばないようにしていましたが)。
それに対して、Eはオーバーフローではないという意見を目にしたため、そう考える理由を聞いてみたくなりました。
会話
私
すばやさの「変な値で割った余りを求める処理」(トリル処理の後にくるやつですよね?)は、オーバーフローと呼ぶべきものだと思っていたのですが、そうではないであれば意見をお聞きしたいです。
「そうではないであれば」は誤字で、「そうではないのであれば」です。
そう思わない理由を聞きたいという意図でした。
Ksan1024さん
私の知らない「オーバーフロー」という言葉の使い方があるのだとしたらわかりませんが、私の理解ではオーバーフローとは現象であり処理ではありません
「〜という処理」である以上オーバーフローとは言わないと理解しています
Ksan1024さんの言葉を引用する形で「変な値で割った余りを求める処理」という言葉を使ってしまったため、こういう返答をいただいてしまいました。修正して伝えなおします。
私
おっしゃることは同感で、そのうえで、
「(略)する処理」は処理ではなく、オーバーフローという現象なのではないかと思うのですが、Ksanさんはどう思いますか?
ということを聞きたかったです。
ポケモンwikiのすばやさのページでは
他の“処理”と並べて書く都合上、処理かのように書かれていますが、本来は処理ではなく現象なのではないかと思っている次第です。
意見の理由
Ksan1024さん
改めてwikiを確認してきました
ソースコードなどを解析しないと正確なところはわからない問題だとは思いますが、これはオーバーフローではなさそうだと考えます
理由を端的にまとめると、
①8192という値があまりにも中途半端であること
②直前の計算で10000という値を取り扱っていること
以上の2点ですね
①については、2のべき乗であるという意味ではコンピューターにとってある程度キリのいい数字とは言えるのですが、メモリ領域は普通8ビットのメモリを最小単位とし、それを2枠または4枠くっつけたものがよく使われます
2の13乗ということは13ビット、そんな変な単位でメモリを使うというのは不自然です
「10000より小さくて10000に最も近い2のべき乗数」という理由でこの処理に使うことが決められたというのは想像できますが、やむを得ず溢れてしまったとは考えづらいです。
②については、①で問題視した部分を無視して13ビットのメモリを使っていたと仮定したとき、「10000から引く」という処理がそもそも13ビットのメモリの中では実現できない、という話です
工夫すればできないことではないですが、そこまでして13ビットのメモリを使う理由も10000から引く処理を入れる理由も思いつきません。
こんなところでどうでしょうか
「メモリ領域は普通8ビットのメモリを最小単位とし、それを2枠または4枠くっつけたものがよく使われます」については、感覚的には分かっていたものの、明確に説明できる状態になかったため、勉強になります。
16bitと32bitがよく使われるので、13bitにするなら、それは、意図的な処理である可能性は考えられても、オーバーフローという現象であるとは考えにくいというわけです。
仮説の提案
私
ありがとうございます。
やはり理由を上げるとするならそこですよね。
②については、私も考えたことがあるのですが、
ダメージ計算の場合、計算途中は32bitで最後は16bitとなっていることを踏まえると、
すばやさ計算の途中までは16bit(たしか海外の信用可能文献にすばやさは16bitとありました)で、最後だけより小さい桁なのは不自然ではないかも
というところに落ち着いていました。
①が一番問題になるところですよね。3bit分の不自然さ。
16bitで計算をした後、13bit分のすばやさ計算結果格納部分と、3bit分の例えば同速時用乱数格納部分により構成される16bitデータを出す、という仕組みになっていることは、あり得るものでしょうか?
ダメージ計算は、このコメントがやり取りされている上記動画にもあるとおり、計算途中は、32bitでやっていて、最終的なダメージだけ16bitになるという仕組みになっています。それを踏まえると、計算途中は16bitで、最終的には13bitというのは、13bitというものを使うことの不自然さ(①)を除けば、不思議なことではないと考えます。
今回は、理由が①と②に分割されていて、②については、これで自分の中では解決しました。
①について考えていたとき、16bitが基本なのであれば、13bitの情報とともに格納される3bitの何かがあるのではないかと考え、上記のようなことを思いつきました。しかし、こういう技術的なところについて、そこまで詳しいわけではないので、詳しそうなKsan1024さんに聞いてみました。
仮説への返答
Ksan1024さん
同速判定と言われて少し考えました。もしかしたらそこが理由かもしれません。
すばやさの計算後の値を3ビット左シフトして、(実質8192で割った余りだけが残る)その下位3ビットに乱数を入れることで「同速時の特殊処理を組まなくても数値の順番に並べるだけで同速判定ができる」というコードは作れるかもしれません
3ビットのただの乱数だと被ってしまうので被らない乱数を出す処理を用意する必要がありますが、ダブルバトルの場合3体以上の同速が入ることもあり、例外処理を作らずに判定できるのは良さそうです
最大8体までの同速判定が処理できるので、トリプルバトルのときに作られた処理がそのまま残っている、と思うと3ビットというのも合理的に見えます。
今度は10000でクリップしてる部分の処理が謎ではありますが、もしそうだとしたらオーバーフローであると言えそうです。
技術的には、「値を3ビット左シフトして、その下位3ビットに乱数を入れる」という処理になることが分かり、勉強になります。
「同速時の特殊処理を組まなくても数値の順番に並べるだけで同速判定ができる」「例外処理を作らずに判定できるのは良さそうです」という部分から、便利そうだということもわかりました。言われてみると、確かに、こういう仕組みになっていないと面倒なのかもしれないと思いました。
再掲
A. ポケモンのすばやさ実数値
B. Aの値にランク補正・持ち物などの補正をかける
C. Bの値が10000を超えているなら、10000になる
D. トリックルーム中なら、10000からCの値を引く
E. Dの値が8192以上なら、8192で割った余りになる
Eをオーバーフローと呼んで良さそうという結論になりました。
ちなみに、DとEの兼ね合いからトリル中にすばやさ1809~8191のポケモンがすばやさが低いポケモンより先に動く現象を、「オーバーフロー」と呼んでしまう人がとても多いですが、これは間違いです。
クリップするというのは、「切る」「刈る」「切り抜く」「切り取る」「穴をあける」など(Weblio辞書から)の意味であり、Cを指しているようです。
私
お、その可能性ありえそうですか!いい仮説を立てることができて嬉しいです。
3ビットは、同じくトリプルが頭に浮かびました。今のダメージ計算の仕組みになったのが第五世代で、トリプル登場と一致するのもそれっぽいですね。
最近でもマックスレイドバトルとかは5体なので、残している理由が今もまだしっかりあると思います。
オーバーフローであると自信を持って言いやすくなって良かったです。
1万のところの謎は、なんでしょうね。
トリックルームの処理を16ビット内でするために、65535以下の定数を使ってポケモンWikiでいうcとdの処理をする必要があるのは間違いないですよね。
普通なら、8192を使って、8192でクリップしトリル処理を8192−すばやさで行うのが自然でしょうか?
3bitでは、0~7の8個の数を表現できます。トリプルバトル(と群れバトル?)の6匹が行動するときが、同速判定が最も発生するときなので、ちょうどいいです。
Cの処理で出てくる定数が10000である理由は、よく分かりませんでした。
仮説の仕組み
今回の仮説をまとめます。
A. ポケモンのすばやさ実数値
B. Aの値にランク補正・持ち物などの補正をかける
C. Bの値が10000を超えているなら、10000になる
D. トリックルーム中なら、10000からCの値を引く
と進んだ後、のEの部分は、こうなっているという仮説です。
〜〜〜〜〜
Dまで進んだ結果得た、16bitの情報を、13bitにします。
2進数表記で、0110 0011 1011 1101 というすばやさがあった場合、
左の3つは、2進数13桁分を格納する枠に収まらずに消えて、 0 0011 1011 1101 という情報になります。
ここで、左の3つ「011」が消えてしまうのがオーバーフローです。
そして、0 0011 1011 1101 と 乱数である2進数3桁(例えば、100)を合わせて、
0001 1101 1110 1100 という16bitの情報を作ります。
この16bitの情報を用いて、すばやさの判定とすばやさが同じときの判定を行います。
〜〜〜〜〜
以上が仮説です。
辻褄確認
行動順の判定は、 以下のような優先順で行われます。
第五世代以降
若い番号の効果を優先して行動順が決まる
1 わざの効果
おさきにどうぞ/りんしょう/コンビネーションわざ/トラップシェル - 行動順を引き上げる
さきおくり - 行動順を最後にする
2 優先度
3 せんせいのツメ/イバンのみ/クイックドロウ - 同じ優先度内で最初に行動する
4 こうこうのしっぽ/まんぷくおこう/あとだし/きんしのちから - 同じ優先度内で最後に行動する
5 補正を考慮したすばやさ
6 乱数
ここで、すばやさの次にすぐ乱数がくることから、5と6を一緒に判定するということも可能です。よって、今回の仮説と辻褄があいます。
プログラム推測
これも仮説です。
1 重複なく0~7の乱数を、場にいるポケモンの数だけ生成し、並べる。
2 それぞれのポケモンのすばやさ計算をする。16bitの結果を得る。
3 それを8倍して、実質13bitの情報にする。
4 それぞれのポケモンに割り当てられた1の数を足して、実質16bitの情報にする。
5 その16bitの数値が大きいものから、行動順を割り当てる。
同速判定用の乱数は、重複なく割り当てる必要があることに注意が必要です。そうしないと、すばやさが一緒かつ乱数も一緒で完全に判定用の数値が一致するというケースが発生することがあり、そのときに行動順判定ができなくなってしまうからです。
追記:有識者のコメントなど
メモリによる物理的な事情で16bitとかに丸められることが多いというだけで、別に"中途半端な"ビット数で丸めることは多々あると思うんだけど(例えば標準的なLCGは符号の有無を無視するために31bit)、途中の値であれ「13bit整数として扱う仕様」があればオーバーフローと言えるんじゃないのかな
— 夜綱 (@sub_827) 2023年5月28日
かなり有識のFFの方に、意見をいただけました。ありがとうございます。
3世代ではどうだっけ
— 夜綱 (@sub_827) 2023年5月28日
第四世代以前のすばやさは、ポケモンWiki「すばやさ」にも情報がないので、何か分かれば教えていただけるととても嬉しいです
— テツポンド (@tetspond) 2023年5月28日
(1つ前のツイート、コメントありがとうございます。安心しました。)
「なぜ13bitなのか?」の考察としてかなり尤もらしい説だと思いましたが、一方で3世代でも13bitだった覚えがあるので、もしそうだとすれば少し奇妙な気もしますね
— 夜綱 (@sub_827) 2023年5月28日
確認してみます
ありがとうございます。
— テツポンド (@tetspond) 2023年5月28日
そうなんですか!?確認結果が気になります
(確認結果はいまだにかえってきていません。)
もしかしたらトリプルバトルの実装による最大6体の同速に対応するために3bit割かれてるから残りの13bit(0〜8191)がトリル時の処理に使われているのかもしれない、って考察面白いなあ
— momonji (@mo_mo_moura) 2023年5月29日
BWでトリプルと共に実装した仕様の名残が剣盾/SVのレイドバトルに活きてたら面白いな
— momonji (@mo_mo_moura) 2023年5月29日
おわりに
前から引っかかっていたことについて、会話の中で、良い仮説を立てられてよかったです。会話に付き合ってくださったKsan1024さんに感謝です。
何か間違いや、処理Cの定数が10000であることの理由などを推測できた方がいれば、お伝えくださると嬉しいです。
お読みいただきありがとうございました。
すばやさ関係、オーバーフロー関係は、本ブログの一押し記事なので、興味があれば、以下カテゴリを読んでくださると嬉しいです。
Twitterアカウント
6183字(追記後6975字)