This article is from the Puzzles FAQ, by Chris Cole chris@questrel.questrel.com and Matthew Daly mwdaly@pobox.com with numerous contributions by others.
What are the odds when tossing dice in Risk?
competition/games/risk.s
Odds calculated with program by David Karr (karr@cs.cornell.edu):
Attacker rolls 3 dice, defender rolls 2 dice:
Attacker Defender Probability
loses loses
0 2 2890/7776 = 0.3716563786
1 1 2611/7776 = 0.3357767490
2 0 2275/7776 = 0.2925668724
Attacker Defender Probability
loses loses
0 1 855/1296 = 0.6597222222
1 0 441/1296 = 0.3402777778
Attacker Defender Probability
loses loses
0 2 295/1296 = 0.2276234568
1 1 420/1296 = 0.3240740741
2 0 581/1296 = 0.4483024691
Attacker Defender Probability
loses loses
0 1 125/216 = 0.5787037037
1 0 91/216 = 0.4212962963
Attacker Defender Probability
loses loses
0 1 55/216 = 0.2546296296
1 0 161/216 = 0.7453703704
Attacker Defender Probability
loses loses
0 1 15/36 = 0.4166666667
1 0 21/36 = 0.5833333333
---------------------8<------snip here--------8<--------------------
/*
* riskdice.c -- prints Risk dice odds
*
* This program calculates probabilities for one roll of the dice in Risk.
* For each possible number of dice that the attacker might roll, for each
* possible number of dice that the defender might roll, this program
* lists all the possible outcomes (number of armies lost by attacker
* and defender) and the probability of each outcome.
*
* Copyright 1993 by David A. Karr.
*/
#define MAX_ATTACK 3 /* max # of dice attacker may roll */
#define MAX_DEFEND 2 /* max # of dice defender may roll */
#define MAX_DICE MAX_ATTACK + MAX_DEFEND
void main()
{
int a; /* number of dice rolled by attacker */
int d; /* number of dice rolled by defender */
void calc();
for (a = MAX_ATTACK; a > 0; --a) {
for (d = MAX_DEFEND; d > 0; --d) {
calc( a, d );
}
}
}
void calc( a_dice, d_dice )
/*
* Purpose: Print odds for the given numbers of dice rolled
*/
int a_dice; /* number of dice rolled by attacker */
int d_dice; /* number of dice rolled by defender */
{
int num_dice; /* total number of dice rolled */
int num_armies; /* # armies that will be lost by both sides, total */
int kill_count[MAX_DEFEND + 1];
/* entry [i] counts # of times attacker loses i armies */
int roll[MAX_DICE + 1]; /* holds one roll of the dice */
int a_roll[MAX_ATTACK]; /* holds attacker's dice */
int d_roll[MAX_DEFEND]; /* holds defender's dice */
int n; /* cursor into the arrays */
int num_lost; /* # of armies lost by the attacker */
int cases; /* total # of events counted */
void dsort();
/*
* The method is pure brute force. roll[] is set successively to
* all possible rolls of the total number of dice; for each roll
* the number of armies lost by the attacker (the outcome) is
* computed and the event is counted.
* Since all the counted events are equiprobable, the count of each
* outcome merely needs to be scaled down by the total count to
* obtain the probability of that outcome.
*/
/* The number of armies at stake is min(a_dice, d_dice) */
num_armies = a_dice < d_dice ? a_dice : d_dice;
/* initialize event counters */
for (n = 0; n <= num_armies; ++n)
kill_count[n] = 0;
/*
* The roll[] array is treated as a funny odometer whose wheels each
* go from 1 to 6. Each roll of the dice appears in roll[0] through
* roll[num_dice - 1], starting with all 1s and counting up to all 6s.
* roll[num_dice] is used to detect when the other digits have
* finished a complete cycle (it is tripped when they go back to 1s).
*/
num_dice = a_dice + d_dice;
for (n = 0; n <= num_dice; ++n)
roll[n] = 1;
while (roll[num_dice] == 1) {
/* examine a new possible roll of the dice */
/*
* copy attacker's and defender's dice so as not to disturb
* the "odometer" reading.
*/
for (n = 0; n < a_dice; ++n)
a_roll[n] = roll[n];
for (n = 0; n < d_dice; ++n)
d_roll[n] = roll[n + a_dice];
/*
* sort attacker's and defender's dice, highest first.
*/
dsort(a_roll, a_dice);
dsort(d_roll, d_dice);
/*
* compare attacker's and defender's dice, count attacker's loss
*/
num_lost = 0;
for (n = 0; n < num_armies; ++n)
if (d_roll[n] >= a_roll[n])
++num_lost;
++kill_count[num_lost];
/*
* Find next roll values (bump the "odometer" up one tick).
*/
n = 0;
roll[0] += 1;
while (roll[n] > 6) {
/* place [n] rolled over */
roll[n] = 1;
/* Carry 1 into the next place (which may in turn roll over) */
++n;
roll[n] += 1;
}
}
cases = 0;
for (n = 0; n <= num_armies; ++n)
cases += kill_count[n];
printf( "Attacker rolls %d dice, defender rolls %d dice:\n\n",
a_dice, d_dice );
printf( "Attacker Defender Probability\n" );
printf( " loses loses\n" );
for (n = 0; n <= num_armies; ++n)
printf( "%5d %5d %5d/%d = %12.10lf\n",
n, num_armies - n, kill_count[n], cases,
((double) kill_count[n]) / ((double) cases) );
printf( "\n\n" );
}
void dsort( array, length )
/*
* Sort the given array in descending order.
*/
int *array;
int length; /* number of slots in the array */
{
int level, n, tmp;
/* Use bubble sort since the array will be tiny in this application */
for (level = 0; level < length - 1; ++level) {
/*
* Slots [0] through [level - 1] are already "stable."
* Bubble up the value that belongs in the [level] slot.
*/
for (n = length - 1; n > level; --n) {
if (array[n - 1] < array[n]) {
/* swap them */
tmp = array[n - 1];
array[n - 1] = array[n];
array[n] = tmp;
}
}
}
}
 
Continue to: