跳到正文
zenn · 游戏开发社区· 来源· vi·· 6 小时前AI 评分42

为什么宝可梦的个体值是“31”,努力值是“255”?从内存与汇编解读其机制【内部数据解析】

なぜポケモンの個体値は「31」で努力値は「255」なのか?メモリとアセンブリから仕組みを読み解く【内部データ解析】

正文 · 原文

ふと気になったので、メモリとアセンブリを覗いてみることにしました。

  • 個体値の最高値(V)はなぜ「31」なのか?
  • 努力値の1ステータス上限はなぜ「255」なのか?
  • なぜキリの良い数字ではなく、このような中途半端な数字なのか?

実は、これらの数字の裏側には、1996年に発売された初代『ポケットモンスター 赤・緑』から続く、ゲームボーイの極限のハードウェア制約と、開発陣(ゲームフリーク)による凄まじいビット演算の職人技が隠されています。

今回は、初代ピカチュウ版のメモリ(WRAM)を実際に覗きながら、ポケモンステータス決定プログラムの仕組みを解剖していきます。


1. 前提知識:ビットとは「倍々ゲームのスイッチ」である

本題に入る前に、本記事のすべての基盤となるビット幅と最大値の対応関係を整理しておきます。

ビット(bit)とは、電灯のスイッチのようなものです。
「OFF(0)」か「ON(1)」の2つの状態しか持てません。

2進数において、すべてのビットが1(オール1)になったときの最大値は 2^n - 1 で表されます。
・4-bit(2^4 - 1) ➔ 15 (初代の個体値上限)
・5-bit(2^5 - 1) ➔ 31 (現代の個体値上限)
・8-bit(2^8 - 1 / 1 Byte) ➔ 255 (現代の努力値上限)
・16-bit(2^{16} - 1 / 2 Byte) ➔ 65535 (初代の努力値上限)

私たちが普段使っている10進数は「1、10、100、1000」と10倍ずつ位が上がりますが、コンピューターの2進数は「1、2、4、8、16、32、64、128……」と2倍ずつ価値が増える「倍々ゲーム」の世界です。

では、このスイッチを順番に「すべてON」にして足し算してみましょう。

  • スイッチ4個(4ビット)をすべてON
    1 + 2 + 4 + 8 = 15 (初代の個体値の最大値)
  • スイッチ5個(5ビット)をすべてON
    15 + 16 = 31 (現代の個体値の最大値)
  • スイッチ8個(8ビット=1バイト)をすべてON
    31 + 32 + 64 + 128 = 255 (現代の努力値の最大値)
  • スイッチ16個(16ビット=2バイト)をすべてON
    2^{16} - 1 = 65535 (初代の努力値の最大値)

これらの「15」「31」「255」という中途半端な数字は、「用意されたビット(スイッチ)をすべてONにしたときの理論上の最大値」そのものだったのです。


2. 前時代の混沌:初代の努力値「65535」と、全能力カンストの世界

歴史をゲームボーイ時代まで遡ってみましょう。

当時の努力値は、全ステータスを「16ビット(最大65535)」まで蓄積することができました。


▲ 実際のピカチュウ版メモリ(0xD13C〜0xD145)。努力値が「FF FF(65535)」で全能力カンストしている様子

そもそも、なぜ努力値は「16ビット(65535)」も必要だったのか?

現代の努力値は「ピカチュウを倒すと素早さ+2」のように1〜3ポイントずつ増えます。しかし初代は全く異なり、「倒した相手の『種族値』がそのまま自分の努力値に加算される」という豪快極まりない仕様でした。

例えば、種族値が「HP:80, 攻撃:82, 防御:78, 素早さ:80, 特殊:80」のフシギバナを1匹倒すと、自分の各ステータス努力値が一撃で+80前後も跳ね上がります。

もし努力値を1バイト(8ビット=最大255)で作っていたら、たった3〜4戦戦っただけで即座にカンスト・オーバーフローしてしまうのです。レベル1から100まで何千回と戦い続けるRPGの成長曲線を受け止めるために、2バイト(16ビット=最大65535)という広大な器が不可欠でした。

さらに、初代のステータス計算式には数学的トリックが隠されています。

\text{努力値によるステータス上昇値} = \left\lfloor \frac{\sqrt{\text{努力値}}}{4} \right\rfloor \times \frac{\text{レベル}}{100}

蓄積した努力値の「平方根(ルート \sqrt{} )」を取って計算していたのです。

16ビットの限界値である「65535」の平方根を計算してみると、

\sqrt{65535} \approx 255.998\dots \longrightarrow \mathbf{255}

つまり、「倒した敵の種族値を累積させるために16ビット(65535)まで貯めさせて、ステータス計算時にルートを取ることで綺麗に8ビット(255)へ着地させる」という、極めて合理的で美しい数学的設計だったのです。

ROM内部のアセンブリ命令を見てみる

初代ポケモンのROM内部にあるステータス計算ルーチン(CalcStat)の実装を見てみましょう。

出典:GitHub pret/pokered(engine/pokemon/stat_calcs.asm)

.statExpLoop
	xor a
	ldh [hMultiplicand], a
	ldh [hMultiplicand+1], a
	inc b               ; ルートの候補値を1増やす
	ld a, b
	cp $ff
	jr z, .statExpDone
	ldh [hMultiplicand+2], a
	ldh [hMultiplier], a
	call Multiply       ; (現在の候補値)^2 を計算
	ld a, [hld]
	ld d, a
	ldh a, [hProduct + 3]
	sub d
	ld a, [hli]
	ld d, a
	ldh a, [hProduct + 2]
	sbc d               ; (候補値)^2 が 努力値 を超えたか判定
	jr c, .statExpLoop  ; 超えるまでループして平方根を算出
.statExpDone

ゲームボーイの8ビットCPU(カスタムZ80)には「平方根命令」が存在しません。そのため、開発陣は候補値を2乗(call Multiply)しながら比較し、アルゴリズムの力技で平方根を求めていたのです。


3. 【検証】初代ピカチュウの生メモリを覗く──消えた「HP個体値」の謎

では、個体値はどうだったのでしょうか?
初代の個体値は、スイッチ4個の「4ビット(0〜15の16段階)」でした。

これもまた、なぜキリの良い数字にしなかったのでしょうか?
答えはゲームボーイの極限のメモリパッキングにあります。

コンピューターの基本単位である「1バイト(8ビット)」は、ちょうど真ん中で半分に割ると「4ビット + 4ビット(上位4ビット / 下位4ビット)」という2つの部屋に分割できます。この4ビットの単位を「ニブル(nibble)」と呼びます。

当時の初代ポケモンでは、手持ちポケモン1匹に割り当てられるデータ領域はわずか44バイト。
もし1つの能力値に1バイトずつ消費していたら、個体値だけで貴重なメモリが圧迫されてしまいます。そこで開発陣は、「1バイトの中に2つのステータスの個体値をぎゅっと詰め込む(ニブルパッキングする)」という選択を取りました。

  • バイト1:攻撃(4bit) + 防御(4bit) = 1バイト (8bit)
  • バイト2:素早さ(4bit) + 特殊(4bit) = 1バイト (8bit)

つまり、「たった2バイト(16ビット)のメモリ枠の中に、できる限り多くのステータス個体値を詰め込むための最小単位」こそが、4ビット(0〜15)だったのです。

実際にメモリを見てみましょう。

ピカチュウの個体値が格納されているアドレス、0xD146 と 0xD147 に注目します。

  • 0xD146 : [ D C ]
  • 0xD147 : [ 6 9 ]

16進数の1桁はちょうど4ビット(0〜15)に対応しています。これを4つのステータスに分解してみます。

  • D (16進数) = 13 (攻撃の個体値)
  • C (16進数) = 12 (防御の個体値)
  • 6 (16進数) = 6 (素早さの個体値)
  • 9 (16進数) = 9 (特殊の個体値)

……お気づきでしょうか。
「HP」の個体値が、メモリ上のどこにも存在しません。

ゲームボーイのカセットに搭載されたセーブ用SRAMや作業用WRAMは、わずか数キロバイトという極限の世界でした。4ビットの個体値を5ステータス分保存するには20ビット(2.5バイト)必要ですが、0.5バイト(4ビット)がどうしても足りなかったのです。


4. 職人技:初代開発陣はどうやってHP個体値を決めたのか?

では、HPの個体値はどのように計算されていたのか。
初代のROMを逆アセンブルしたコードを解析すると、当時のプログラマたちの職人技が浮かび上がってきます。

開発陣は、「他の4つの能力値の最下位ビット(奇数か偶数か)」をパズルのように1ビットずつ繋ぎ合わせることで、存在しないHP個体値をその場で錬成していました。

  • 攻撃個体値 が奇数(最下位ビットが1)なら \longrightarrow +8 (bit 3)
  • 防御個体値 が奇数(最下位ビットが1)なら \longrightarrow +4 (bit 2)
  • 素早さ個体値 が奇数(最下位ビットが1)なら \longrightarrow +2 (bit 1)
  • 特殊個体値 が奇数(最下位ビットが1)なら \longrightarrow +1 (bit 0)

先ほどのピカチュウ(DC 69)を実際に計算してみましょう。

  • 攻撃個体値 : 13 (奇数 / 2進数 1101) \longrightarrow bit 3 = 1 (8点)
  • 防御個体値 : 12 (偶数 / 2進数 1100) \longrightarrow bit 2 = 0 (0点)
  • 素早さ個体値: 6 (偶数 / 2進数 0110) \longrightarrow bit 1 = 0 (0点)
  • 特殊個体値 : 9 (奇数 / 2進数 1001) \longrightarrow bit 0 = 1 (1点)

\text{錬成されたHP個体値} = 8 + 0 + 0 + 1 = \mathbf{9}

このHP合成ロジックも、ROM内部で実際に実行されている生の命令列を見てみます。

出典:GitHub pret/pokered(engine/pokemon/stat_calcs.asm)

push bc
	ld a, [hl]  ; 攻撃個体値のバイトをロード
	swap a
	and $1      ; 最下位ビット(奇数なら1)を抽出
	sla a
	sla a
	sla a       ; 左へ3回ビットシフト(×8点)
	ld b, a
	ld a, [hli] ; 防御個体値のバイトをロード
	and $1      ; 最下位ビットを抽出
	sla a
	sla a       ; 左へ2回ビットシフト(×4点)
	add b
	ld b, a
	ld a, [hl]  ; 素早さ個体値のバイトをロード
	swap a
	and $1
	sla a       ; 左へ1回ビットシフト(×2点)
	add b
	ld b, a
	ld a, [hl]  ; 特殊個体値のバイトをロード
	and $1      ; 最下位ビットを抽出(×1点)
	add b       ; 4つのビットを足し合わせてHP個体値(0〜15)が完成
pop bc

各ステータスの最下位ビットを抽出し、sla a(Shift Left Arithmetic=左算術ビットシフト)で桁をずらしながら足し合わせていく職人芸が、そのままコードとして残されています。


5. 第3世代(GBA)の革命:なぜ「31」と「255」に進化したのか?

2002年、ハードウェアが「ゲームボーイアドバンス(GBA)」へと世代交代を果たします。
CPUは8ビットから32ビット(ARM7TDMI)へと進化し、ポケモンの内部データ構造は劇的な刷新を遂げました。

ここで、本記事の最大の疑問に戻りましょう。
「なぜ個体値は『31』で、努力値は『255』になったのか?」

その決定打となった、2つの美しいエンジニアリングの理由を解剖します。

なぜ個体値は「31(5ビット)」なのか? ──32ビットレジスタの限界パッキング

GBAのCPUは「32ビット」です。つまり、CPUが一度に処理できる整数の標準レジスタサイズは「32ビット(4バイト)」単位になりました。

一方、第3世代のポケモンには、全部で「6つの能力値(HP、攻撃、防御、特攻、特防、素早さ)」が存在します。

では、「1つの32ビット整数の箱の中に、6つの能力値の個体値を均等に詰め込む」には、1能力あたり何ビット割り振れるでしょうか。

32 \text{ bit} \div 6 \text{ stats} = \mathbf{5.333\dots \text{ bit}}

整数として均等に割り振れる最大のビット数は、「5ビット」しかありません。

  • 5ビット \times 6能力 = 30ビット (32ビットの中に綺麗に収まる)
  • 余った 2ビット = 特性(Ability)フラグやタマゴフラグとして無駄なく活用

もし1能力を6ビット(0〜63)にしてしまうと、合計36ビットとなり32ビットの箱から溢れてしまいます。逆に4ビット(0〜15)のままだと24ビットしか使わず、空間が余ってしまいます。

「32ビットCPUの箱の中に、6つの個体値を限界まで詰め込むための最大公約数」──それこそが、5ビット(2^5 - 1 = 31)だったのです。

なぜ努力値は「255(8ビット)」なのか? ──初代の「+63」を維持した最適化

初代の努力値は、各ステータス2バイト(16ビット=65535)を消費していました。6能力になれば、努力値だけで12バイトも食ってしまいます。
そこで開発陣は、各ステータスの努力値を「1バイト(8ビット=最大255)」に圧縮しました。

そして、ここからがゲームフリークの天才的な数理設計です。
GBAの新しいステータス計算式を見てみましょう。

\text{努力値によるステータス上昇値} = \left\lfloor \frac{\text{努力値}}{4} \right\rfloor \times \frac{\text{レベル}}{100}

レベル100のとき、努力値が最大255まで溜まるとどうなるでしょうか?

255 \div 4 = 63.75 \longrightarrow \mathbf{+63 \text{(最大上昇値)}}

初代の複雑な平方根計算(\sqrt{65535} \approx 255 \div 4 \approx 63)で得られていた最大上昇値も、まったく同じ「+63」でした。

開発陣は、「努力値MAXで実数値が約63上がるという初代の育成バランスを寸分狂わず維持したまま、重い平方根ルーチンを完全撤廃し、1バイトの努力値を2ビット右シフト(\div 4)するだけの超高速な処理へ置き換えた」のです。

さらに、合計値に設けられた「510」という制限キャップ。
これは、255 \times 2 = 510、つまり「2つの能力値を限界の255まで特化(極振り)できるちょうど2倍の数値」として設計されたものでした。


まとめ:制約が生んだビット演算の芸術

私たちが何気なく厳選し、対戦で使っている「31」や「255」という数値。

それは単なるゲームバランスの数字ではなく、「四半世紀前、画面の向こうの開発者たちが数キロバイトのメモリ空間の中で知恵を絞り、ビットを1つずつスイッチに見立てて戦った歴史そのもの」だったのです。

こうした内部構造を知った上でゲームを眺めると、普段のプレイ画面がまったく違った深みを持って見えてくるのではないでしょうか。


🎬 動画で直感的に理解したい方へ

本記事で解説した内容は、自作のアニメーションを用いて動画化し、YouTubeで映像作品として公開しています。

より理解を深めたい方は、ぜひこちらの動画もあわせてご覧ください。

少しでも知的好奇心が刺激されたら、ぜひ「Like」や「フォロー」をよろしくお願いいたします。

今後もゲームの内部データやバグの正体など、低レイヤの視点から解説していきます。

次回は「なぜ色違いポケモンは1/8192で光るのか? 排他的論理和(XOR)と境界値バグの数理解析」をお届けします。

来源:zenn · 游戏开发社区 · zenn.dev