Foi isso que eu usei para determinar o vencedor de uma batalha no meu applet Lords of Conquest Imitator. Neste jogo, semelhante à sua situação, há apenas um valor de ataque e um valor de defesa. A probabilidade de o atacante vencer é maior, mais pontos o atacante tem e menos pontos a defesa tem, com valores iguais avaliando a chance de 50% do ataque ter êxito.
Algoritmo
Jogue uma moeda aleatória.
1a. Cabeças: a defesa perde um ponto.
1b. Caudas: as cabeças perdem um ponto.
Se a defesa e o atacante ainda tiverem pontos, volte para a etapa 1.
Quem está abaixo de 0 pontos perde a batalha.
3a. Atacante até 0: o ataque falha.
3b Defesa até 0: O ataque foi bem-sucedido.
Eu escrevi em Java, mas deve ser facilmente traduzível para outras línguas.
Random rnd = new Random();
while (att > 0 && def > 0)
{
if (rnd.nextDouble() < 0.5)
def--;
else
att--;
}
boolean attackSucceeds = att > 0;
Um exemplo
Por exemplo, digamos que att = 2 e def = 2, apenas para garantir que a probabilidade seja de 50%.
A batalha será decidida no máximo de n = att + def - 1
lançamentos de moedas, ou 3 neste exemplo (é essencialmente o melhor de 3 aqui). Existem 2 n combinações possíveis de moeda vira. Aqui, "W" significa que o atacante ganhou o lançamento da moeda e "L" significa que o atacante perdeu o lançamento da moeda.
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
O atacante vence em 4/8, ou 50% dos casos.
A matemática
As probabilidades matemáticas decorrentes desse algoritmo simples são mais complicadas do que o próprio algoritmo.
O número de combinações em que exatamente x Ls é dado pela função de combinação:
C(n, x) = n! / (x! * (n - x)!)
O atacante vence quando há entre 0
e att - 1
Ls. O número de combinações vencedoras é igual à soma das combinações de 0
through att - 1
, uma distribuição binomial cumulativa:
(att - 1)
w = Σ C(n, x)
x = 0
A probabilidade de o atacante vencer é w dividida por 2 n , uma probabilidade binomial cumulativa:
p = w / 2^n
Aqui está o código em Java para calcular essa probabilidade para valores arbitrários att
e def
:
/**
* 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;
}
Código de teste:
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));
}
Resultado:
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
Observações
As probabilidades são: 0.0
se o atacante tiver 0
pontos, 1.0
se o atacante tiver pontos, mas a defesa tiver 0
pontos, 0.5
se os pontos forem iguais, menor que 0.5
se o atacante tiver menos pontos que a defesa e maior que 0.5
se o atacante tiver mais pontos que a defesa .
Tomando att = 50
e def = 80
, eu precisava mudar para BigDecimal
s para evitar transbordamento, mas tenho uma probabilidade de cerca de 0,0040.
Você pode tornar a probabilidade mais próxima de 0,5 alterando o att
valor para ser a média dos valores att
e def
. Att = 50, Def = 80 se torna (65, 80), o que gera uma probabilidade de 0,1056.