スペルキャスト-毎秒のダメージを最適化する方法


23

いくつかの呪文を知っているウィザードがあると想像してください。各スペルには、ダメージ、クールダウン時間、キャスト時間の3つの属性があります。かなり標準的なRPGのもの。

クールダウン時間:その呪文を再び唱えることができるまでにかかる時間(t)。呪文は、キャストを開始した瞬間に「クールダウン」します。

キャスト時間:呪文を使用するのにかかる時間(t)。ウィザードが何かを唱えている間、別の呪文は唱えられず、キャンセルすることもできません。

質問は次のとおりです。異なるスペルのセットを与えられた場合、どのようにダメージを最大化するのでしょうか?

キャストタイムごとの最大ダメージを計算するのは簡単です。しかし、はるかに高い呪文が利用可能な場合、低ダメージの呪文を「スタック」してキャストするのを待つ方が良い状況ではどうでしょうか?

例えば、

  1. ファイアボール:3000ダメージ、3秒のキャスト時間、6秒のクールダウン。

  2. フロストボルト:ダメージ20、キャスト時間4秒、クールダウン4秒。

  3. ファイヤーブラスト:ダメージ3、キャスト時間3秒、クールダウン3秒。

この場合、フロストボルトではなく、DPCTの低いスペル(ファイアブラスト)を選択すると、1秒あたりのダメージが大きくなります。そのため、呪文を選択した結果を考慮する必要があります。 代替テキスト

次の例は、「オーバーキャスト」と「待機」の場合です。 代替テキスト


この状況で1-3-1を行うのはなぜですか?なぜ1-2-1ではないのですか?1-3-1だけではターゲットを殺せない場合、1-2-3-1よりも1-3-1-Xの方が効率的です。

@Joe Wreschnig:それを指摘してくれてありがとう。私の例では間違いでした。現在、2つのケースに単純化されています。
aaronfarr

1
貪欲、可能な限り最高のdpsスペルを選択するように。他のロジックを無視します。待っています。
aaronfarr

1
水を濁らせるだけです。∞のダメージを与えるが、キャストに50秒かかる呪文を考えてください。dps / dpctは∞ですが、ターゲットが他の手段で50秒以内に殺される可能性がある場合、決して選択されるべきではありません。
deft_code

回答:


23

すべてのAIは検索です!

AIの根性に触れると、そのどれだけが本当に検索であるかが驚くべきことです。

  • state:利用可能なすべてのスペルの残りのクールダウン。
  • フィットネス:合計ダメージ
  • コスト:合計所要時間
  • :既知の呪文。呪文がまだクールダウン中の場合、その値をそのキャスト時間に追加するだけです。
  • 目標:ターゲットの合計ヘルス。目標は有限量のダメージでなければならないので、未知のターゲットの場合、可能な限り最大の健康を選びます。
    代わりに、目標は50秒未満を費やすことができ、検索は50秒以内に行われる可能性のある最大の損傷を見つけます。

これらのパラメーターをUniform Cost Search(UCS)に接続し、事前に保証された最適な戦闘計画を作成します。ヒューリスティック、A *、またはIDA * で検索できれば、同じ答えがはるかに速く得られます。

UCSを使用するもう1つの利点は、3つの変数のみを提供した場合よりもはるかに複雑な状況で最適なキャスト順序を見つけることができることです。簡単に追加できるその他の側面:

  • 経時的な損傷
  • 他の呪文のクールダウンを減らすために呪文を更新する
  • 他の呪文をより速く唱える速攻呪文。
  • ダメージブースターにより、他のスペルのダメージが増加します。

UCSは全能ではありません。保護呪文の利点をモデル化することはできません。そのためには、アルファベータ検索またはミニマックスにアップグレードする必要があります。
また、影響範囲とグループの戦いをうまく処理しません。これらの状況では、UCSを調整して適切なソリューションを提供できますが、最適なソリューションを見つけることは保証されていません。


2

これは、特殊な組み合わせ最適化問題です。呪文の数が増えると、呪文の最適な組み合わせ/パターンを見つけるのが難しくなります。ナップザック問題に使用されるものと同様のヒューリスティックは、この問題を解決するのに役立ちます。


1

「キャスト時間の単位あたりのダメージ」(DPCT)の観点から考える必要があります。たとえば、3秒のキャストで3000ダメージを与えるファイアボールは、1000 DPCTになります。

クールダウンをキャストする前に3秒待たなければならなかった場合、500 DPCT(3000ダメージ、待機を含む合計6秒で割った値)に減少します。

したがって、クールダウンまでの残りの待機時間を含めて、各スペルのキャストごとのダメージ時間を決定する必要があります。DPCTが最も高いものを選択し、必要に応じて待機してからキャストします。ボスが死ぬまで繰り返します:)


問題は、DPCTが非常に誤解を招く可能性があることです。たとえば、ミックスにさらに2つのスペルを追加するとします。Fireball:3000ダメージ、3秒のキャスト、6秒のクールダウン、DPCT:1000スペル#2:20ダメージ、4秒のキャスト、4秒のクールダウン、DPCT:5スペル#3:3ダメージ、3秒のキャスト、3秒のクールダウン、DPCT:1(スペルがキャストされた瞬間にクールダウンが始まることを思い出してください)スペル#3のDPCTが低い場合でも、DPSは高くなります(1-3-1-3 .. 。)スペル#2(1-2-1-2 ...)より。
aaronfarr

1

あなたの例を使用すると、おそらく2つの呪文の有効性をより近づけたいと思うでしょうが、異なる利点を与える可能性があります。キャスト時間を短くする(または、その時間をキャストしない)ことは非常に役立つので、ダメージが少なく、再使用に時間がかかる場合でも使用する価値があります。

方程式には別の要素を常に課すことができます。マナ/マジックポイントは、プレイヤーがこれらのポイントを使用する価値があるかどうかを判断できるようにすることで、この目的に役立ちます。

全体的に、bluescrnが言ったように、DPCT(または最高のミックスを求めているプレイヤーによって高度に調整され議論されている多くのゲームで呼ばれているDPS)は、特に何らかの種類がある場合、特にバランスを取る必要がある主な要素ですさまざまなプレイヤーがさまざまなスキルで進歩することを可能にする技術/スキルツリー。ただし、ゲーム内の特定の位置で同程度のダメージを与えることができます。


0

私の目的に適したこのアルゴリズムを見つけました。

人々はいくつかの素晴らしい点を持ち出しました。究極の目標パラメーターを与えることで、通常の検索アルゴリズムがそれらを実行できるようになります。すなわち。t秒で最適なダメージを与え、最適な時間でxダメージを与えます。

私のアルゴリズムは、DPSが最も高い一連の呪文を返すだけです。トラバースするセットのサイズを削減するため、高速アルゴリズムであり、他の検索ツリー技術の知識は必要ありません。

最初のステップは、キャストタイムごとのダメージが最も大きいスペルを特定することです。この呪文は、毎秒最高のダメージを保証するため、「ベースライン」呪文になります。つまり、次の2つの条件が満たされている場合は、常にこの呪文を唱える必要があります。1)ベースライン呪文が利用可能です(クールダウンではありません)。2)現在、呪文を唱えていません。

そのため、ベースラインスペルがクールダウン中に他のスペルを埋める問題になります。(キャスト時間)と(クールダウン-キャスト時間)の間。ただし、オーバーラップが発生する可能性があります(上記のルール2は誤りです)。

その後、2つのルールに違反しないすべての一連のスペルを見つけるために、すべての非ベースラインスペルを再帰的に調べる問題になります。

重複する呪文の場合、ベースラインの呪文が与える可能性のある潜在的なダメージ(最大ダメージまで)に対してペナルティを科さなければなりません。

たとえば、2呪文

1:ダメージ300、キャスト時間3秒、クールダウン10秒

2:290のダメージ、3秒のキャスト時間、3秒のクールダウン

最も大きなダメージは、シーケンス1-2-2-2から生じます。これにより、潜在的な#1キャストに2秒のオーバーラップが生じます。ただし、3つ目のスペル(1-2-2)をキャストしないと、1秒の余裕で880のダメージを与えるため、これは依然として有益です。余分な#2スペルをキャストすると、1170-#1の2秒(200)になります。したがって、970ダメージが相対的なダメージになります。


-2

単純な「セキュリティレベル」スタイルのスイッチケースを実行できます。

これは私の頭上にあるので、疲れた状態の思考レベルを超える論理エラーに注意してください。

あなたの時間がブロック整数で行われていると仮定します-

// after casting spell
int remainingTime = (coolDown - castTime);
switch(spellJustCast)
{
  // assuming the cast method will have some input validation for whether the spell
  // is off cooldown or not, pass the time as a parameter
  case 3 : castSpell1(remainingTime);
           castSpell2(remainingTime);
           break;
  case 1 : castSpell2(remainingTime);
           castSpell3(remainingTime);
           break;
  case 2 : castSpell1(remainingTime);
           castSpell3(remainingTime);
           break;
  default: System.out.println("Debug!");
           break;
}

一部のメソッド呼び出しは、スペルタイムのために不要ですが、この方法で更新する余地は常にあります。

編集:私はちょうど、新しいスペルがキャストされた後の残り時間をリセットする必要があると思いました。おそらくそれをクラス属性/フィールドにして、castSpellメソッド内の呼び出しから設定するのが最善です。


ここで何を取得しようとしているのか本当にわかりませんが、castSpell1やcastSpell2のような機能を備えた最新のゲームエンジンはありません。

1
@Joe Wreschnig私はそれらを彼のカスタムゲームクラスの彼自身のメソッドであると言っていました。これは抽象的な例であり、詳細な例ではありません。
キムリー

1
そうです、それは現代のエンジンでの呪文の働きではありません。ファイルからフィールドが読み取られるオブジェクトを受け取るcastSpell関数が1つあります。このようなswitchステートメントを実際のエンジンで維持することは不可能であり、ある種の計画アルゴリズムが必要です。

@Joe Wreschnigわかりました。私は単に問題を解決する方法を与えていました。この例はJavaで書かれており、エンジンや特定のフレームワークを対象とするものではありません。しかし、あなたが言うように実装できない場合、私の答えは無効です。
キムリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.