Pythonで学ぶゲーム数理工学：ポケモン編1

##  はじめに


ポケモンバトルにおいて、どの技を選ぶかは勝敗を大きく左右する重要な要素です。
特に「なみのり」や「ハイドロポンプ」のように、高威力だが命中率が低い技と、命中が安定しているものの威力がやや低めの技のどちらを採用すべきかは、戦術を考える上で大きな判断ポイントになります。

また、実際のダメージは、レベル・こうげき／とくこう・ぼうぎょ／とくぼう・技の威力・乱数補正といった複数の要素を組み合わせた複雑な計算式によって決まります。

本記事では、これらの計算式をPythonで再現し、技ごとの期待値の比較や、実際に与えるダメージのばらつきをプログラムで可視化していきます。

##  参考リンクまとめ

https://x.com/ingenlife/status/1905820331660685814

- ダメージ計算式

https://latest.pokewiki.net/%E3%83%80%E3%83%A1%E3%83%BC%E3%82%B8%E8%A8%88%E7%AE%97%E5%BC%8F


ポケモンダメージ計算式
レベル50の場合
①レベル×2÷5+2  小数点以下切り捨て
※レベル50の時22
②22×威力×こうげき(とくこう)÷ぼうぎょ(とくぼう)  小数点以下切り捨て
③ ②式÷50+2×乱数(0.85〜1.00の0.01刻みの16個) 小数点以下切り捨て



##  Pythonコード



```python
# ----------------------------
# 技のパラメータ設定 / Move Parameters
# ----------------------------

# なみのり（Surf）のパラメータ
power_surf = 90         # 威力 / Power
accuracy_surf = 100     # 命中率 / Accuracy (%)

# ハイドロポンプ（Hydro Pump）のパラメータ
power_hydro = 110       # 威力 / Power
accuracy_hydro = 80     # 命中率 / Accuracy (%)

# ----------------------------
# 期待値の計算 / Expected Value Calculation
# ----------------------------

# 期待値 = 威力 × 命中率（命中率は%を小数に変換）
expected_surf = power_surf * (accuracy_surf / 100)
expected_hydro = power_hydro * (accuracy_hydro / 100)

# ----------------------------
# 結果の出力 / Print the Results
# ----------------------------

print("=== Expected Power of Water Moves ===")
print(f"Surf (なみのり)       : {expected_surf:.2f} (Expected Value)")
print(f"Hydro Pump (ハイドロポンプ): {expected_hydro:.2f} (Expected Value)")

```

```python
# ダメージ計算の各ステップをわかりやすく表示 / Detailed Damage Calculation Display

# -----------------------------
# 基本ステータス / Basic stats
# -----------------------------
level = 50              # レベル / Level
attack = 182            # 攻撃側のこうげき / Attacker's Attack
defense = 189           # 防御側のぼうぎょ / Defender's Defense
power = 100             # 技の威力 / Move power

print("=== ポケモンダメージ計算の各ステップ / Pokémon Damage Calculation Steps ===")

# -----------------------------
# Step 1: レベル補正 / Level correction
# -----------------------------
step1 = (level * 2) // 5 + 2  # 22
print(f"[Step 1] レベル補正: ({level}×2)÷5+2 = {step1}")
print(f"[Step 1] Level Correction: ({level}×2)/5 + 2 = {step1}")

# -----------------------------
# Step 2: 基礎ダメージ計算 / Base damage
# -----------------------------
step2 = (step1 * power * attack) // defense
print(f"[Step 2] 威力×こうげき÷ぼうぎょ: {step1}×{power}×{attack}÷{defense} = {step2} (切り捨て)")
print(f"[Step 2] Power × Attack ÷ Defense = {step2} (floored)")

# -----------------------------
# Step 3: 定数補正 / Constant correction
# -----------------------------
step3 = step2 // 50 + 2
print(f"[Step 3] 定数補正: {step2}÷50+2 = {step3} (切り捨て)")
print(f"[Step 3] Constant Correction: {step2}/50 + 2 = {step3} (floored)")

# -----------------------------
# Step 4: 乱数補正 / Random factor (16 values from 0.85 to 1.00)
# -----------------------------
random_factors = [round(0.85 + 0.01 * i, 2) for i in range(16)]
print(f"[Step 4] 乱数補正係数一覧 (16段階): {random_factors}")
print(f"[Step 4] Random multipliers (16 steps): {random_factors}")

# -----------------------------
# 最終ダメージ一覧 / Final damage list with random factors
# -----------------------------
final_damages = [int(step3 * r) for r in random_factors]
print(f"[Result] 各乱数に対する最終ダメージ: {final_damages}")
print(f"[Result] Final damage values for each random factor: {final_damages}")

# -----------------------------
# ダメージ範囲 / Min-Max damage
# -----------------------------
print(f"[Range] 最小ダメージ～最大ダメージ: {min(final_damages)} ～ {max(final_damages)}")
print(f"[Range] Damage Range: {min(final_damages)} to {max(final_damages)}")

# -----------------------------
#  おまけ: 最大ダメージ目的関数 / Max Damage Objective Function
# -----------------------------
def max_damage(power_input, attack_input, level=50, defense=189, weight=1.0):
    """
    技の威力と攻撃力から最大ダメージを計算する関数
    Function to calculate maximum Pokémon damage (random = 1.00)
    """
    step1 = (level * 2) // 5 + 2
    step2 = (step1 * power_input * attack_input) // defense
    step3 = step2 // 50 + 2
    max_damage = int(step3 * 1.00 * weight)  # 最大乱数補正1.00と重み定数
    return max_damage

# 入力例 / Example input
example_power = 120
example_attack = 200
weight_constant = 1.0  # 重み係数（必要に応じて変更）

# 出力 / Output
objective_damage = max_damage(example_power, example_attack, weight=weight_constant)
print("\n=== 最大ダメージ目的関数 / Max Damage Objective Function ===")
print(f"入力: 技の威力 = {example_power}, 攻撃 = {example_attack}, 重み = {weight_constant}")
print(f"出力: 最大ダメージ = {objective_damage}")


```





# 結果

=== Expected Power of Water Moves ===
Surf (なみのり)       : 90.00 (Expected Value)
Hydro Pump (ハイドロポンプ): 88.00 (Expected Value)






![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/a5cb53a0-f7e9-4b75-8df2-8ea59b09f603.png)