Questo è ciò che ho usato per determinare il vincitore di una battaglia nella mia applet di Lords of Conquest Imitator. In questo gioco, simile alla tua situazione, c'è solo un valore di attacco e un valore di difesa. La probabilità che l'attaccante vinca è maggiore maggiore è il numero di punti che l'attaccante ha, e minore è il numero di punti che la difesa ha, con valori uguali che valutano una probabilità del 50% dell'attacco riuscito.
Algoritmo
Lancia una moneta casuale.
1 bis. Teste: la difesa perde un punto.
1b. Code: la testa perde un punto.
Se sia la difesa che l'attaccante hanno ancora punti, torna al passaggio 1.
Chiunque è a 0 punti perde la battaglia.
3a. Attaccante fino a 0: l'attacco fallisce.
3b. Difesa fino a 0: l'attacco ha successo.
L'ho scritto in Java, ma dovrebbe essere facilmente traducibile in altre lingue.
Random rnd = new Random();
while (att > 0 && def > 0)
{
if (rnd.nextDouble() < 0.5)
def--;
else
att--;
}
boolean attackSucceeds = att > 0;
Un esempio
Ad esempio, supponiamo che att = 2 e def = 2, solo per assicurarsi che la probabilità sia del 50%.
La battaglia sarà decisa in un massimo di n = att + def - 1
lanci di monete, o 3 in questo esempio (qui è essenzialmente il migliore dei 3). Esistono 2 n combinazioni possibili di lanci di monete. Qui, "W" significa che l'attaccante ha vinto il lancio della moneta, e "L" significa che l'attaccante ha perso il lancio della moneta.
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
L'attaccante vince in 4/8, ovvero il 50% dei casi.
La matematica
Le probabilità matematiche derivanti da questo semplice algoritmo sono più complicate dell'algoritmo stesso.
Il numero di combinazioni in cui esattamente x L è dato dalla funzione di combinazione:
C(n, x) = n! / (x! * (n - x)!)
L'attaccante vince quando ci sono tra 0
e att - 1
Ls. Il numero di combinazioni vincenti è uguale alla somma delle combinazioni da 0
attraverso att - 1
, una distribuzione binomiale cumulativa:
(att - 1)
w = Σ C(n, x)
x = 0
La probabilità di vincita l'attaccante è w diviso per 2 n , una probabilità cumulativa binomiale:
p = w / 2^n
Ecco il codice in Java per calcolare questa probabilità per arbitrari att
e def
valori:
/**
* 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;
}
Codice di prova:
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));
}
Produzione:
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
osservazioni
Le probabilità sono 0.0
se l'attaccante ha 0
punti, 1.0
se l'attaccante ha punti ma la difesa ha 0
punti, 0.5
se i punti sono uguali, meno che 0.5
se l'attaccante ha meno punti della difesa e maggiore rispetto 0.5
se l'attaccante ha più punti della difesa .
Prendendo att = 50
e def = 80
, ho dovuto passare a BigDecimal
s per evitare trabocco, ma ho una probabilità di circa 0,0040.
È possibile avvicinare la probabilità a 0,5 modificando il att
valore in modo che sia la media dei valori att
e def
. Att = 50, Def = 80 diventa (65, 80), che produce una probabilità di 0,1056.