This post is also available in: English (英語)

「戦闘用のステータスは、物語にどの程度の影響を与えるように設計されているのか?」

「戦闘能力の低いキャラは、どのくらい過酷な旅路になるのか?」

「どの程度の能力値があれば、強大な敵に真っ向から殴り勝てるようにできているのか?」

Pythonで1万回ずつ戦闘シミュレーションをして、Ironswornの生み出す戦いの物語を数値的に検証してみた。

三行まとめ
  • 戦闘力皆無のキャラでも、「ギリギリで戦いを切り抜ける」体験ができるようにデザインされている。
  • 戦闘力特化のキャラは「明らかに人間が戦える相手じゃないだろう」という敵と渡り合うために旅をすること自体がお話しの筋書きになるようデザインされている。
  • 純粋な殴り合いの勝率は60~70%前後が実質的なソフトキャップとなっており、戦闘バランス自体が「無敵の英雄が常に勝ち続ける物語の生成器」として設計されていない。

検証の前提

確率計算のベース

確率計算には、以前書いた記事のデータを参考資料とする。

Ironswornの判定成功確率をシミュレーションしたよ – Kaburanai Games

この記事から抜粋すると、アクションロールの成功確率はこうなる。

 補正合計値  ミス(%)  ウィークヒット(%)  ストロングヒット(%)
1 45.1%39.6% 15.2%
2 33.1%43.6% 23.2%
3 23.1%43.6% 33.1%
415.1%39.7%45.0%
5 9.3%34.6%55.9%
65.3%29.3%65.3%
72.8%24.2%72.8%
81.4%20.2% 78.2%
90.9%18.0% 80.9%
101.0%18.0% 80.9%

確率計算のモデル

「真っ向勝負を挑んだ時の勝率」を計算するため、以下の戦闘モデルとした。

  • アセットやロールは考慮しない
  • 「ひたすらIronかEdgeの得意な方を使って攻撃しては、防御または回避を繰り返す」場面を想定して単純化した
  • 武器は槍や剣・弓などの戦闘向けの装備=2 Harmを与えるものを想定
  • イニシアティブを取っている時はStrikeで攻撃し、イニシアティブを失っている時はFace Dangerでイニシアティブ奪取を狙うものとする(Clashはルールブックでもおススメされない戦法なため無視。Secure an AdvantageやTurn the Tideは、単純化・均質化のため今回の試行では割愛した。)
  • Strike, FaceDangerともに、ストロングヒットでのイニシアティブ奪取とウィークヒット・ミスでのイニシアティブ喪失の流れはアルゴリズムに落とし込んだ。
  • Strikeでストロングヒットが出たらHarm+1なのは通常ルールの通り
  • 受けるダメージは全てHealthに受けるものとして一本化した。
  • Endure Harmのモメンタム増減やモメンタムとHealthの交換、Endure Harmをトリガーにしたイニシアティブの変化などはすべて無視した。条件を揃えるためと、「Iron型とEdge型での勝率の違い」まで扱い出すと、単純に票の長さが累乗で増え、10倍になるため。
  • 戦闘開始時のEnter The Frayは2もしくは3の有利な能力を使い、Weak hitの際はイニシアティブ奪取を選ぶものとして単純化した。Enter The Frayに使う能力は、初期状態では三つのうちどれか一つは必ず2以上になるためだ。
  • Health0になったら敗北するものとし、それより前にプログレスを10まで溜め切れば勝利するものとする。
  • Health0になってもキャラロストや永続ペナルティを無視すれば本来はもっと耐えられるが、あくまでここぞという大勝負でなく、通常の戦いをする場合という想定で進めた

プログレスについては、「10まで溜め切る」ことを勝利条件としているため、例えばTroublesomeの敵にStrikeで3 Harmを与え、合計9プログレスになった時も、10プログレスになるまで戦闘は続行するものとする。

この場合のコードは以下になる。

(コードでは参照ステータスをIronにしているが、IronでもEdgeでもこのモデルは成立する)

#10000Battles Ironsworn

import random as rand

print('Iron')
Iron = int(input())
print('Enter the Fray' )
EnterTheFray = int(input())
print('EnemyRank')
EnemyRank = int(input())

Progress = 0
Health = 5
Initiative = False
win = 0
lose = 0

def ActionRoll(Modifier):
    actiondice = rand.randint(1,6)
    actionscore = Modifier + actiondice
    if actionscore > 10:
        actionscore = 10
    challengedice1 = rand.randint(1,10)
    challengedice2 = rand.randint(1,10)
    if actionscore > challengedice1 and actionscore > challengedice2:
        Outcome = 'Stronghit'
    elif actionscore <= challengedice1 and actionscore <= challengedice2:
        Outcome = 'Miss'
    else:
        Outcome = 'Weakhit'
    return Outcome
        

def Strike(IronStrike,EnemyRankStrike):
    HarmToYou = 0
    StrikeSuccess = ActionRoll(IronStrike)
    if StrikeSuccess == 'Stronghit':
        HarmToEnemy = 3
        HarmToYou = 0
        StrikeInitiative = True
    elif StrikeSuccess == 'Weakhit':
        HarmToEnemy = 2
        HarmToYou = 0
        StrikeInitiative =False
    else:
        HarmToEnemy = 0
        HarmToYou = EnemyRankStrike
        StrikeInitiative = False
    return HarmToEnemy, HarmToYou,StrikeInitiative   
    
   
def FaceDanger(IronFaceDanger,EnemyRankFaceDanger):
    HarmToYou = 0
    FaceDangerSuccess = ActionRoll(IronFaceDanger)
    if FaceDangerSuccess == 'Stronghit':
        HarmToYou = 0
        FaceDangerInitiative = True
    elif FaceDangerSuccess == 'Weakhit':
        HarmToYou = 0
        FaceDangerInitiative = False
    else:
        HarmToYou = EnemyRankFaceDanger
        FaceDangerInitiative = False
    return 0, HarmToYou, FaceDangerInitiative
    
def StartInitiative(EnterTheFraySimulate):
       EnterSuccess = ActionRoll(EnterTheFraySimulate)
       if EnterSuccess == 'Stronghit' or EnterSuccess == 'Weakhit':
            InitiativeStart = True
       else:
            InitiativeStart = False
       return InitiativeStart
      
def DamageRace(BattleInitiative,BattleIron,BattleEnemyRank):
    BattleHarmEnemy = 0
    BattleHealthDamage = 0
    if BattleInitiative == True:
        (BattleHarmEnemy,BattleHealthDamage,BattleInitiative) = Strike(BattleIron,BattleEnemyRank)
    else:
        (BattleHarmEnemy,BattleHealthDamage,BattleInitiative) = FaceDanger(BattleIron,BattleEnemyRank)
    return BattleHarmEnemy,BattleHealthDamage,BattleInitiative

    
def ProgressByRank(HarmToEnemy,RankOfEnemy):
    HarmMultipler = 1
    if RankOfEnemy == 1:
        HarmMultipler = 12
    elif RankOfEnemy == 2:
      HarmMultipler = 8
    elif RankOfEnemy == 3:
          HarmMultipler = 4
    elif RankOfEnemy ==4:
         HarmMultipler = 2
    else:
          HarmMultipler = 1
    ProgressIncrease = HarmMultipler * HarmToEnemy
    return ProgressIncrease
        

for i in range(0, 10000):
    Health = 5
    Progress = 0
    Start = StartInitiative(EnterTheFray)
    while Health > 0 and Progress < 40:
        (HarmEnemy,HealthDecrease,Initiative) = DamageRace(Start,Iron,EnemyRank)
        Progress += ProgressByRank(HarmEnemy,EnemyRank)
        Health -= HealthDecrease
        if Health <= 0:
            lose += 1
        elif Progress >= 40:
            win += 1

        
      
        
        
print('Win        ' + str(win))        
print('Lose      ' + str(lose))

検証結果

それぞれのIronのステータスごと、かつそれぞれの敵ランクごと、かつEnter the Frayに用いる能力の値ごとに1万回のシミュレーションを行い、勝率を算出した。

それぞれのランクの敵に対する勝率は小数点第二位以下を切り捨てているため、合計しても100%にはならない。

また、乱数による試行に基づくため毎回この結果になるわけではなく、場合の数を数え上げたものとも異なる結果となる。注意されたい。

1万回試行しているとはいえ乱数による揺らぎがあるため、「低ステータスの場合の方が勝率が高い」ケースもそれなりにある。

初期状態のIronとEdgeの最大値は3、その場合のEnter the Frayに使える能力値は最大でも2だが、

  • Alchemistで+1
  • Oathbreakerで+1
  • Maskedで+2
  • Bindで+2

を使えば、IronまたはEdgeは9までは伸ばせる。

(MaskedとBindは制約があるため、この表のとおりの結果にはならないが、参考程度に見てみて欲しい)

 Iron or Edge Enter the Fray Troublesome Dangerous Formidable Extreme Epic
12 62.6%44.9% 13.3% 1.7% 0.0%
13 71.5% 51.6%13.9%1.7%0.0%
2265.6%57.0% 26.6% 7.8%0.0%
2375.9% 65.3%31.0%8.7%0.0%
3266.5%62.3% 42.4% 20.6%1.0%
3376.1%71.8%48.9%24.0%1.0%
4265.9%63.6% 54.3%38.7%4.2%
4376.3%75.5% 62.4%45.5%5.2%
5267.2%66.9%60.4% 52.9% 14.0%
5377.3%77.3% 70.7%60.6%17.2%
6266.7%66.8%65.1%61.3%28.3%
6377.3%76.7%74.5% 70.7%33.2%
7266.5%67.0%66.3%64.7%43.8%
7375.9%76.9%74.9%74.4%49.8%
8266.9%67.1%66.8%66.5% 53.5%
8377.1%76.2%76.0%76.5% 62.3%
9267.1%66.9%66.9%66.0%56.7%
93 76.4%77.5%76.8%76.5%66.7%

黄色は「純粋にIronやEdgeが高いケースを、それらが低い代わりにEnter the Frayの能力値が高く先手を取りやすいケースが上回る」閾値を指す。この値からは、先手を取ることを重視したほうが勝率が上がる。

シミュレーションから見る物語上の敵の強さ

ゲームシステム上、敵の強さは5ランクに分けられている。

Ironswornの敵にはゲーム的な特殊攻撃や特殊行動などの仕組みが一切存在しない。

物語上の設定や特殊能力を除けば、戦闘での敵の強さは

  1. タフさ
  2. 与えてくるダメージ

の二点だけで、シンプルに表現される。

ちなみに、Iroswornでは「敵の弱点・障害となるものを準備して弱体化させたり、味方についてくれる勇敢な村人などと一緒に戦う場合、敵の強さのランクを一段階だけ下げて良い」というルールが標準ルールとしてある。

この点も考慮に入れながら、検証結果を基に、それぞれのランクの敵を少し掘り下げてみよう。

ランク1 Troublesome

  • 一般人ひとり
  • 「壊れた」ひと一人
  • 大ネズミ一匹

アイアンランドにおける最弱の敵……のはずなのだが、検証結果だと戦闘関連の能力が1のキャラでも9のキャラでも大して勝率が変わらない。というか、武の道を究めたようなキャラでも一般人や大ネズミ相手に23.1%で負けてしまう。

「道端の喧嘩やコソ泥の襲撃」のようなほんの小さな諍いがあっても、戦闘力皆無のキャラだからと言ってビビる必要はない。最弱の戦闘力のプレイヤーキャラでも、こうした場面を切り抜けることはできうるのだ。

逆に、戦闘狂のキャラは注意が必要だ。こうした弱い敵にすら、負けて苦汁を嘗めさせられる可能性がかなりある。

ランク2 Dangerous

ルールブックに最も多くの敵が掲載されているランク帯。

  • 一般人3~5人の集団
  • 山賊一人(他、訓練された戦士や狩人もこのランク)
  • 狼一匹
  • 大蜘蛛
  • さまよう骸骨
  • 狼人間

いかにもな、「旅の途中に出くわす、それなりにきちんと危険なモブ敵」と言った感じの顔ぶれ。

EdgeとIronがともに最低値の1な探索特化&戦闘系アセットも未所持の初期キャラが、ちょうど五分五分で戦えるラインの敵。

Iron or Edgeが1の場合と2の場合では10%程の勝率の差こそあれ、それ以上の帯域ではほとんど勝率の差が無い。

ここまでを見ても、Ironswornの設計は

貧弱で戦闘を避けることが前提となる探索型のキャラが、運悪く戦う羽目になってしまっても、村外れの遭遇戦程度なら何とか生き抜ける

ようにデザインされていると言えるだろう。

ランク3 Formidable

ここから、一気に敵の強さが跳ね上がる。

このランク帯の敵は、

  • 山賊(ないし訓練された戦士や狩人)3~5人の集団
  • 精鋭中の精鋭レベルの戦士一人
  • 狼3~5匹の集団
  • 一般人6~10人の集団
  • 怨霊
  • トロール

という顔ぶれだ。

戦闘力の無いキャラの勝率は13%程度と絶望的で、IronやEdgeをバフ効果なしでの最大値である3持っているキャラですら45%前後とかなり厳しい勝率だ。

そりゃ戦うのが苦手なキャラが狼五匹に勝てるわけもない……という、一般的な感覚とゲーム的な勝率が丁度いい具合にシンクロしている

しかし、ここで先ほど述べた敵の弱体化ルールが活きてくる。

敵に毒を盛ったら?

群れている山賊を情報力とペテンの力で仲たがいさせ、一人になったところを(つまり、Dangerous相当の敵に角が下がったところを)一対一での戦いに持ち込めば?

表からもわかるように、勝率は13%から50%程度まで一気に上げられる

ここにある物語は、「知恵や勇気、狡猾さをもって生き抜くならば、弱くても格上相手に何とか戦える」という物語だ。

確率シミュレーション的にも、Ironswornは「弱い者が知恵を巡らして強い敵を相手取る」という物語が自然と生まれるようにしていると言えるだろう。

ランク4 Extreme

ここから、明確に生まれる物語が変わって来る

このランク隊の敵は

  • 山賊や戦士6~10人の集団
  • 精鋭中の精鋭レベルの戦士3~5人
  • 巨人
  • バジリスク
  • マンモス
  • エルダービースト(最も古い樹木と並ぶ年月を生きた獣)
  • ワイバーン
  • 存在するだけで呪いを振りまく強力な怨霊

見ての通り、明らかに並の人間が対処できる範疇を超えている。

貧弱なキャラが勝てる見込みは1.7%しかなく、自らを鍛え上げた戦士キャラであっても20%前後でしか勝てない

なんなら、ルールブックの解説ですら「出会ったら死を覚悟しろ」だの「絶対に触れてはいけない」だの言ってくる。

つまりは、一般的な人間の範疇にとどまっていては、勝ち目がない領域だ。

このランクの相手と対等に(=勝率50%で)渡り合いたいのなら、IronかEdgeを、何らかの手段を使って5までブーストするしかない

Iron、Edge特化のキャラが比較的条件の軽いAlchemistアセットを取得し、そのうえで人間の集落を遠く離れた深い森の奥に隠れ住まうエルフ達と絆を結ぶことが取得条件のMaskアセットも取得して、やっと戦う土俵に立てる。

他の能力をブーストするアセットも、

  • Beast(たいていはExtremeランク)を倒して、そいつらの毛皮を手に入れないと使えないBindの上位スキル
  • 鉄への誓いを破った後にのみ受けられる、高難度の贖罪クエスト完遂が条件となるOathbreaker

など、ブースト能力を身に着けるためにはかなりのハードルがある。

また、ルールブックにあるこれらの敵から派生するクエスト例も、「正面から戦って勝つ」ことを要求するものは無く

  • 呪いを解く
  • 怒りを鎮める
  • 棲み処へ帰す
  • 大規模集落間の衝突を防ぐ

など、明らかに非戦闘的な方法を提案している

人の域を超えた高みへと昇る」か「そもそも戦わずして解決する道を探す」か。

お話の歯触りが、Formidableまでとは違っていることが分かると思う。

ランク5 Epic

伝説上の生物レベルの存在。

このランクに属する敵は、基本ルールでは一体だけだ。

  • リヴァイアサン

Iron, Edgeが5のキャラですら14%しか勝ち目が無く、五分五分の状態までもっていくにはIronかEdgeが8必要である。

ここまで来ると、もはや人間というよりサイヤ人の領域に入って来る

真正面から戦いたいなら、徹底して長く厳しい修行でキャラクターを鍛え上げ、数々のスキルに習熟しなければいけない。

伝説と戦う力を得るために、遥かなる武者修行に赴く

そうした遠大なサーガの果てに、それでもまだ五分近くは負ける可能性が残っている。

ここまで来ると、もはや神話の領域へと足を踏み入れることになる。

終わりに

いかがだったろうか?

筆者が全体を通して思ったのは、「戦闘力特化キャラでも、勝率80%を超えないの?」ということだった。

ルールが先か、理屈が先なのかは分からないが、「常勝無敗の英雄が、無双し続けるお話し」にはならないように調整されているように思われた。

もちろん、今回の試行では瞬間的なダメージブースト系アセットを考慮していないことは念を押しておきたい

Ironswornには数々の強力なアセット(特にダメージブースト系)があるため、それら全てを使いこなせば机上の計算でもFormidableくらいの相手なら一撃必殺、Extreme相手にも出会い頭で体力を半分削るくらいのことはできる。

あくまでこの記事は、「ガチンコの殴り合いが、ルール的に、お話の設計的にどういう位置づけなのか」を考えるだけの記事だ。

物語生成マシーンを支える確率論を肴に、ルールブックをじっくり読んでニヤニヤして欲しい。

では、よいゲーマーライフを!

最後までお読みいただき、ありがとうございました!!

******************************************************************

本記事は、TRPG『IronSworn』のルールを解説・引用し、必要に応じて翻訳や再構成を行っています。

IronSworn © Shawn Tomkin
本作品は Creative Commons Attribution 4.0 International
CC BY 4.0)ライセンスの下で公開されています。

原著公式サイト:https://www.ironswornrpg.com/

This work is based on Ironsworn (found at www.ironswornrpg.com), created by Shawn Tomkin, and licensed for our use under the Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/).