进攻VS防守,谁是赢家?[关闭]


12

我正在用手机创建一个新的简单游戏,并且在以下部分上花了几天的时间。

为了简单起见,假设我有两个战士。他们的唯一属性是攻击和防御。第一次进攻时,唯一重要的是他的进攻和对手的防守。反之亦然。

他们没有设备,物品,耐力或健康。只是进攻还是防守。

例:

  • 战斗机1:

    进攻:50,防守:35

  • 战斗机2:

    进攻20,防守:80

战斗过程将只取决于一次攻击即可确定获胜者。因此,没有多重攻击或回合。我不想使其具有确定性,而是添加一个意想不到的简单版本。具有较低攻击力的战斗机将能够赢得具有较高防御力的另一架战斗机(但并非每次都可以)

我的第一个想法是使其线性化,并调用统一的随机数生成器。

If Random() < att1 / (att1 + def2) {
    winner = fighter1
} else {
    winner = fighter2
} 

以进攻50和防御80为例,进攻战斗机将有大约38%的胜率。但是,在我看来,意料之外的事情太遥不可及,最差的战斗机将赢得很多。

我想知道您如何处理类似情况。

PS:我在此QnA和其他资源中进行了大量搜索,发现相似的问题对于SE来说范围太广。但是它们具有许多属性,武器,物品,类等,可能会使它变得过于复杂。我认为我的版本要比SE的QnA风格简单得多。


1
您在寻找什么情况?您正在查看攻击和防御的值范围是多少,这些范围内的任何两个数字都应该有固定的结果吗?例如,攻击为10的战斗机能否在防御90下击败战斗机?
尼尔斯

@ user2645227我可以说范围在1到400之间。不,我不想有任何确定性的决定,也不希望攻击1赢得防御400,但是在极少数情况下。
Tasos

1
因此,如果您采用Att(min)-def(max)和Att(max)-def(min),则可以得到800的范围(从-400到+400)。您需要随机范围覆盖整个范围。防御-攻击将以门槛的形式为您提供扩展余量,您必须达到此门槛才能赢取。这样可以减少随机性。为了进一步集中结果,您可以使用Philipps示例或在任何骰子中随意摆弄,直到找到所需的曲线。
尼尔斯

Answers:


24

如果您希望战斗结果更可预测但又不能完全确定,则最好使用n系统。

重复战斗n时间(n应该是一个不均匀的数字),并宣布战斗人员是获胜次数更多的获胜者。您的价值越大,获得n的意外损失就越少。

const int FIGHT_REPETITONS = 5 // best 3 of 5. Adjust to taste.

int fighter1wins = 0;
int fighter2wins = 0;

for (int i = 0; I < FIGHT_REPETITONS; I++) {

    If (Random() < att1 / (att1 + def2)) {
        fighter1wins++;
    } else {
        fighter2wins++;
    } 

}

If (fighter1wins > fighter2wins) {
    winner = fighter1
} else {
    winner = fighter2
} 

该系统仅在特殊情况下才起作用,而战斗是赢或输的简单二进制结果。当战斗的结果更为复杂时,例如获胜者仍然根据胜利的多寡而失去一些生命值时,这种方法将不再起作用。一个更通用的解决方案是更改生成随机数的方式。当您生成多个随机数然后取平均值时,结果将聚集在范围中心附近,更极端的结果将更加罕见。例如:

double averagedRandom3() {
    return (Random() + Random() + Random()) / 3.0;
}

将具有如下分布曲线:

分配3d20 / 3

(图片来自anydice-设计涉及随机性的游戏机制公式的真正有用的工具,而不仅仅是桌面游戏)

在我当前的项目中,我正在使用一个辅助函数,该函数可以设置任意样本大小:

double averagedRandom(int averageness) {
     double result = 0.0;
     for (var i = 0; i < averageness; i++) {
         result += Random();
     }
     return result / (double)averageness;
}

似乎是更好的方法。一个问题。在averagedRandom3()函数中,您应该使用+代替*还是误解了它的作用?
Tasos

@Tasos是的,应该为+,而不是*。我还有一个将多个样本相乘的随机函数。这为您提供了一个随机数函数,对较低的值有很强的偏见,这在某些情况下也很有用。
菲利普

1
我会将问题开放1-2天,如果没有其他答案,我会选择您的答案。我赞成,但是如果您不介意的话,也想给其他答案一个机会。
Tasos

我认为该答案已经获得足够的投票,因此有资格将此答案标记为答案:P
Hamza Hasan

1
如果有人提出替代方法,我也很好奇。有人拒绝了这个答案。也许他们想提供另一种选择。
菲利普

8

这就是我用来确定“征服之王”模仿者小程序中战斗胜利者的方法。在此游戏中,与您的情况类似,只有攻击值和防御值。攻击者获胜的概率越大,攻击者拥有的得分就越多,而防御者拥有的得分就越少,而相等的值估计攻击成功的机会为50%。

算法

  1. 翻转随机硬币。

    1a。负责人:防守失分。

    1b。尾巴:头丢了一点。

  2. 如果防御者和攻击者双方仍然有分数,请返回步骤1。

  3. 下降到0分的人将输掉这场战斗。

    3a。攻击者降至0:攻击失败。

    3b。防御降至0:攻击成功。

我用Java编写了它,但是它应该可以轻松翻译成其他语言。

Random rnd = new Random();
while (att > 0 && def > 0)
{
    if (rnd.nextDouble() < 0.5)
        def--;
    else
        att--;
}
boolean attackSucceeds = att > 0;

一个例子

例如,假设att = 2和def = 2,只是为了确保概率为50%。

战斗将以最多n = att + def - 1掷硬币而决定,在此示例中为3(在此基本上是3的最佳成绩)。掷硬币有2 n种可能的组合。在此,“ W”表示攻击者赢得了掷硬币,而“ L”表示攻击者失去了掷硬币。

L,L,L - Attacker loses
L,L,W - Attacker loses
L,W,L - Attacker loses
L,W,W - Attacker wins
W,L,L - Attacker loses
W,L,W - Attacker wins
W,W,L - Attacker wins
W,W,W - Attacker wins

攻击者在4/8或50%的情况下获胜。

数学

由这种简单算法产生的数学概率比算法本身更复杂。

确切有x Ls的组合数由组合函数给出:

C(n, x) = n! / (x! * (n - x)!)

当介于0att - 1Ls 之间时,攻击者获胜。获胜组合的数量等于从0到的组合总和att - 1,即累积的二项式分布:

    (att - 1)
w =     Σ     C(n, x)
      x = 0

攻击者获胜的概率用w除以2 n,即累积的二项式概率:

p = w / 2^n

这是Java中用于计算任意值attdef值的概率的代码:

/**
 * Returns the probability of the attacker winning.
 * @param att The attacker's points.
 * @param def The defense's points.
 * @return The probability of the attacker winning, between 0.0 and 1.0.
 */
public static double probWin(int att, int def)
{
    long w = 0;
    int n = att + def - 1;
    if (n < 0)
        return Double.NaN;
    for (int i = 0; i < att; i++)
        w += combination(n, i);

    return (double) w / (1 << n);
}

/**
 * Computes C(n, k) = n! / (k! * (n - k)!)
 * @param n The number of possibilities.
 * @param k The number of choices.
 * @return The combination.
 */
public static long combination(int n, int k)
{
    long c = 1;
    for (long i = n; i > n - k; i--)
        c *= i;
    for (long i = 2; i <= k; i++)
        c /= i;
    return c;
}

测试代码:

public static void main(String[] args)
{
    for (int n = 0; n < 10; n++)
        for (int k = 0; k <= n; k++)
            System.out.println("C(" + n + ", " + k + ") = " + combination(n, k));

    for (int att = 0; att < 5; att++)
        for (int def = 0; def < 10; def++)
            System.out.println("att: " + att + ", def: " + def + "; prob: " + probWin(att, def));
}

输出:

att: 0, def: 0; prob: NaN
att: 0, def: 1; prob: 0.0
att: 0, def: 2; prob: 0.0
att: 0, def: 3; prob: 0.0
att: 0, def: 4; prob: 0.0
att: 1, def: 0; prob: 1.0
att: 1, def: 1; prob: 0.5
att: 1, def: 2; prob: 0.25
att: 1, def: 3; prob: 0.125
att: 1, def: 4; prob: 0.0625
att: 1, def: 5; prob: 0.03125
att: 2, def: 0; prob: 1.0
att: 2, def: 1; prob: 0.75
att: 2, def: 2; prob: 0.5
att: 2, def: 3; prob: 0.3125
att: 2, def: 4; prob: 0.1875
att: 2, def: 5; prob: 0.109375
att: 2, def: 6; prob: 0.0625
att: 3, def: 0; prob: 1.0
att: 3, def: 1; prob: 0.875
att: 3, def: 2; prob: 0.6875
att: 3, def: 3; prob: 0.5
att: 3, def: 4; prob: 0.34375
att: 3, def: 5; prob: 0.2265625
att: 3, def: 6; prob: 0.14453125
att: 3, def: 7; prob: 0.08984375
att: 4, def: 0; prob: 1.0
att: 4, def: 1; prob: 0.9375
att: 4, def: 2; prob: 0.8125
att: 4, def: 3; prob: 0.65625
att: 4, def: 4; prob: 0.5
att: 4, def: 5; prob: 0.36328125
att: 4, def: 6; prob: 0.25390625
att: 4, def: 7; prob: 0.171875
att: 4, def: 8; prob: 0.11328125

观察结果

概率是:0.0如果攻击者拥有0分数,1.0如果攻击者具有分数但防御者具有0分数,0.5如果分数相等,则小于(0.5如果攻击者具有的分数小于防御者的分数)则大于(0.5如果攻击者的分数大于防御者的分数) 。

采用att = 50def = 80,我需要切换到BigDecimals以避免溢出,但是我得到的概率约为0.0040。

通过将att值更改为attdef值的平均值,可以使概率接近0.5 。Att = 50,Def = 80变为(65,80),得出的概率为0.1056。


1
另一个有趣的方法。该算法也可以很容易地可视化,这看起来很令人兴奋。
菲利普

5

您可以通过从正态分布中采样的随机数来修改攻击。这样,大多数情况下,结果将达到您的预期,但偶尔,较高的攻击会在防御较低的情况下失败,或者较低的攻击会在防御较高的情况下获胜。随着攻击和防御之间的差异增加,发生这种情况的可能性将变小。

if (att1 + norm(0, sigma) - def2 > 0) {
  winner = fighter1;
}
else {
  winner = fighter2;
}

该函数norm(x0, sigma)返回从以x0为中心的正态分布采样的浮点,具有标准偏差sigma。大多数编程语言都为库提供了这样的功能,但是如果您想自己使用它,请看看这个问题。您必须调整sigma,使其“感觉正确”,但是10-20的值可能是一个不错的起点。

对于一些sigma值,给定的获胜概率att1 - def2如下所示: 胜利的可能性


可能值得指出的是,正态分布的值没有实际的界限,因此在游戏中使用正态分布的随机值时,有必要对结果进行钳位,以避免产生不太可能但并非不可能的极端值的情况,可能会破坏游戏。
菲利普
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.