私は現在、プレイヤーが与えるダメージに常に0.8から1.2の間のランダムな係数を掛けるマルチプレイヤー戦闘システムを扱っています。
理論的には、真にランダムなRNGは、最終的に同じ数を何度も返す可能性があります(テトリスのジレンマを参照)。これにより、プレイヤーが常に非常に高いダメージを与え、他のプレイヤーが常に非常に低いダメージを与えるマッチになります。
これが起こらないようにするにはどうすればよいですか?一部のRNGは、繰り返しを避ける点で他のRNGよりも優れていますか?
私は現在、プレイヤーが与えるダメージに常に0.8から1.2の間のランダムな係数を掛けるマルチプレイヤー戦闘システムを扱っています。
理論的には、真にランダムなRNGは、最終的に同じ数を何度も返す可能性があります(テトリスのジレンマを参照)。これにより、プレイヤーが常に非常に高いダメージを与え、他のプレイヤーが常に非常に低いダメージを与えるマッチになります。
これが起こらないようにするにはどうすればよいですか?一部のRNGは、繰り返しを避ける点で他のRNGよりも優れていますか?
回答:
ダメージ結果の事前設定リストを作成してシャッフルすることで、テトリスと同じ方法で解決できます。
プレイヤーが線形分布で0.8倍から1.2倍のダメージを与えることを知っているとしましょう。リスト[0.8、0.9、1.0、1.1、1.2]を取得します。ランダムにシャッフルしますので、例えば[1.2、1.0、0.8、0.9、1.1]を取得します。
プレイヤーが初めてダメージを与えるとき、彼らは1.2xを与えます。その後、1x。その後、等、1.1x。配列が空の場合のみ、新しい配列を生成してシャッフルする必要があります。
実際には、一度に4つ以上の配列に対してこれを行うことができます(たとえば、[0.8,0.8,0.8,0.8,0.9,0.9,0.9,0.9、...]で開始します)。それ以外の場合、シーケンスの期間は十分に短く、プレイヤーは次のヒットが「良い」かどうかを判断できます。(ただし、ドラゴンクエストIXのHoimiテーブルのように、戦闘により多くの戦略を追加することもできます。
私は実際にこれを行うためにいくつかのコードを書きました。その要点は、統計を使用して不運なストリークを修正することです。これを行う方法は、イベントが発生した回数を追跡し、それを使用してPRNGによって生成される数にバイアスをかけることです。
まず、イベントの割合をどのように追跡しますか?これを行う単純な方法は、これまでに生成されたすべての数値をメモリに保持し、それらを平均化することです。これは機能しますが、ひどく非効率的です。少し考えた後、次のことを思いつきました(これは基本的に累積移動平均です)。
次のPRNGサンプルを取得します(サンプルが0.5以上の場合は処理します)。
Values: 0.1, 0.5, 0.9, 0.4, 0.8
Events: 0 , 1 , 1 , 0 , 1
Percentage: 60%
各値が最終結果の1/5に寄与することに注意してください。別の方法で見てみましょう。
Values: 0.1, 0.5
Events: 0 , 1
0
は値の50%に貢献し、値の50%に貢献することに注意してください1
。さらに少し:
Values: [0.1, 0.5], 0.9
Events: [0 , 1 ], 1
現在、最初の値が値の66%を占め、最後の値が33%を占めています。基本的に、これを次のプロセスに分解できます。
result = // 0 or 1 depending on the result of the event that was just generated
new_samples = samples + 1
average = (average * samples / new_samples) + (result * 1 / new_samples)
// Essentially:
average = (average * samples / new_samples) + (result / new_samples)
// You might want to limit this to, say, 100.
// Leaving it to carry on increasing can lead to unfairness
// if the game draws on forever.
samples = new_samples
ここで、PRNGからサンプリングした値の結果にバイアスをかける必要があります。これは、RTSでランダムな量のダメージを与えるなど、物事がはるかに簡単になる可能性があるためです。これは「ちょうど私に起こった」ため、説明するのは難しいでしょう。平均値が低い場合は、イベントが発生する可能性とその逆の可能性を高める必要があることを意味します。いくつかの例
average = 0.1
desired = 0.5
corrected_chance = 83%
average = 0.2
desired = 0.5
corrected_chance = 71%
average = 0.5
desired = 0.5
corrected_change = 50%
さて、「私に起こったこと」は、最初の例では83%がちょうど「0.6から0.5」(つまり「0.5から0.5プラス0.1」)だったことです。ランダムなイベント用語では、次のいずれかを意味します。
procced = (sample * 0.6) > 0.1
// or
procced = (sample * 0.6) <= 0.5
したがって、イベントを生成するには、基本的に次のコードを使用します。
total = average + desired
sample = rng_sample() * total // where the RNG provides a value between 0 and 1
procced = sample <= desired
したがって、あなたは私が要点に置いたコードを取得します。このすべてがランダムダメージのケースシナリオで使用できると確信していますが、それを理解するのに時間がかかりませんでした。
免責事項:これはすべて自国の統計であり、私はこの分野で教育を受けていません。私の単体テストはパスします。
samples
最大値(この場合は100)で初期化するというKylotanの提案を理解しています。そうすれば、RNGが安定するのに99回の反復は必要ありません。いずれにせよ、この方法で見られる欠点の1つは、公平性を保証せず、単に一定の平均を保証することです。
total = (average / 2) + desired
。
あなたが求めているのは、実際にはほとんどのPRNGの反対であり、非線形分布です。1.0xを超えるすべてが何らかの「クリティカルヒット」であると仮定して、ある種の減少リターンロジックをルールに入れて、各ラウンドでクリティカルが発生する可能性がXになるまで、そのポイントがYにリセットされます。その後、各ラウンドで2つのロールを実行します。
Sid Meierは、このトピックとCivilizationゲームについてGDC 2010で素晴らしい講演を行いました。後でリンクを見つけて貼り付けようとします。本質的に-知覚されるランダム性は、真のランダム性とは異なります。物事を公平に感じるためには、以前の結果を分析し、プレイヤーの心理に注意を払う必要があります。
すべてのコストで不運の連続を避けます(前の2ターンが不運だった場合、次のターンは運が良いと保証されます)。プレイヤーは常にAIの対戦相手よりも運がいいはずです。