7
$\begingroup$

In D&D players roll a 20 sided die trying to beat a set number to determine if an attack hits the target. Players often can add modifiers to this roll to help the odds in reaching this target number. If the target number (enemy armor level) is passed by the attack roll, damage is done to the target which requires a different roll depending on the power of the attack. If the armor level is matched for that attack roll, damage is calculated like normal (rolling dice, adding modifiers) and then halved, rounding down to the nearest integer.

One of my spells allows me to do three attacks at once on one creature, and on a hit deals 2 6-sided dice +2 damage.

It so happened I used this spell on my "Ally" and I'm trying to figure out what my chances of actually killing him were.

I roll a 1d20 (1 20-sided die) and add +4 to the roll and if that result is greater than or equal to 16 (my 'Allies' Armor level), I roll damage which is 2d6+2 (halfing then rounding down the damage if the attack equaled Armor level). That damage result is then subtracted from my 'Allies' starting Health which is 20.

I repeat this 2 more times, adding damage on each hit. If his health reduces to 0 then he is dead. If his health is 1 or more by the end of this attack he lives and gets to attack me.

I want to know, statistically what percentage of time will my Ally be reduced to 0 health after doing this spell?

$\endgroup$
  • $\begingroup$ So all these answers are more than sufficient, but I just realized I forgot to mention the chance of a critical hit. If anyone wants to look into that for funzies: if a d20 rolls a 20 for the attack (ignoring the +4 modifier) damage is 2*(2d6+2) $\endgroup$ – Phillip Byram Sep 26 '14 at 13:29
  • $\begingroup$ Recalculation done in my answer; it increases the chance to about 23.3%. But whuber's approach would have let you do that for yourself almost instantly $\endgroup$ – Glen_b Sep 27 '14 at 0:35
  • $\begingroup$ @Glen_b Thank you for pointing that out. One solution--in full, assuming the code for the die class is available and the variables have been initialized as in my answer--is round <- conditional(op.die(function(h,a) sign(h-a) + (h==max.die(hit)), hit, armor), list(nothing, half(damage), damage, rep(damage,2)); x <- health - rep(round, n.rounds); x <= nothing. It reports $\frac{18772122907}{80621568000}\approx 23.38424\%$. The Normal approximation gives $22.35\%$. $\endgroup$ – whuber Sep 28 '14 at 14:56
  • $\begingroup$ @whuber - thanks. Our answers differ in the third digit (23.38% vs 23.28%). I assume I've made a small error along the way somewhere. $\endgroup$ – Glen_b Sep 28 '14 at 23:23
  • 1
    $\begingroup$ @glen Nope; just a typo on my part: the decimal expression should be 23.28...%. $\endgroup$ – whuber Sep 29 '14 at 3:36
9
$\begingroup$

What you really want to know is how to do this calculation quickly--preferably in your head--during game play. To that end, consider using Normal approximations to the distributions.

Using Normal approximations, we can easily determine that two rolls for damage with a 2d6+2 have about a $30\%$ chance of equalling or exceeding $20$ and three rolls for damage have about a $95\%$ chance.

Using a Binomial distribution, we can estimate there is about a $108/343$ chance of rolling twice for damage and $27/343$ chance of rolling three times. Therefore, the net chance of equaling or exceeding $20$ is approximately

$$(0.30 \times 108 + 0.95 \times 27) / 343 \approx (32 + 25)/343 = 57/343 \approx 17\%.$$

(Careful consideration of errors of approximation suggested, when I first carried this out and did not know the answer exactly, that this number was likely within $2\%$ of the correct value. In fact it is astonishingly close to the exact answer, which is around $16.9997\%$, but that closeness is purely accidental.)

These calculations are relatively simple and easily carried out in a short amount of time. This approach really comes to the fore when you just want to know whether the chances of something are small, medium, or large, because then you can make approximations that greatly simplify the arithmetic.


Details

Normal approximations come to the fore when many activities are independently conducted and their results are added up--exactly as in this situation. Because the restriction to nonnegative health (which is not any kind of a summation operation) is a nuisance, ignore it and compute the chance that the opponent's health will decline to zero or less.

There will be three rolls of the 1d20 and, contingent upon how many of them exceed the opponent's armor, from zero to three rolls of the 2d6+2. This calls for two sets of calculations.

  1. Approximating the damage distribution. We need to know two things: its mean and variance. An elementary calculation, easily memorized, shows that the mean of a d6 is $7/2$ and its variance is $35/12 \approx 3$. (I would use the value of $3$ for crude approximations.) Thus the mean of a 2d6 is $2\times 7/2 = 7$ and its variance is $2\times 35/12 = 35/6$. The mean of a 2d6+2 is increased to $7+2=9$ without changing its variance.

    Therefore,

    • One roll for damages has a mean of $9$ and a variance of $35/6$. Because the largest possible damage is $14$, this will not reduce a health of $20$ to $0$.
    • Two rolls for damages have a mean of $2\times 9=18$ and a variance of $2\times 35/6=35/3\approx 12$. The square root of this variance must be around $3.5$ or so, indicating the health is approximately $(20-18)/3.5\approx 0.6$ standard deviations above the mean. I might use $0.5=1/2$ for a crude approximation.
    • Three rolls for damages have a mean of $27$ and variance of $35/2\approx 18$ whose square root is a little larger than $4$. Thus the health is around $1.5$ to $2$ standard deviations lower than the mean.

    The 68-95-99.7 rule says that about $68\%$ of the results lie within one SD of the mean, $95\%$ within two SDs, and $99.7\%$ within three SDs. This information (which everyone memorizes) is on top of the obvious fact that no results are less than zero SDs from the mean. It applies beautifully to sums of dice.

    Crudely interpolating, we may estimate that somewhere around $40\%$ or so will be within $0.6$ SDs of the mean and therefore the remaing $60\%$ are further than $0.6$ SDs from the mean. Half of those--about $30\%$--will be below the mean (and the other half above). Thus, we estimate that two rolls for damage has about a $30\%$ chance of destroying the enemy.

    Similarly, it should be clear that when the mean damage is between $1.5$ and $2$ standard deviations above the health, destruction is almost certain. The 68-95-99.7 rule suggests that chance is around $95\%$.

    This figure plots the true cumulative distributions of the final health (in black), their Normal approximations (in red), and the true chances of reducing the health to zero or less (as horizontal blue lines). These lines are at $0\%$, $33.6\%$, and $96.4\%$, respectively. As expected, the Normal approximations are excellent and so our approximately calculated chances are pretty accurate.

    figure

  2. Estimating the number of rolls for damages. The comparison of a 1d20 to the armor class has three outcomes: doing nothing with a chance of $11/20$, rolling for half damages with a chance of $1/20$, and rolling for full damages with a chance of $8/20$. Tracking three outcomes over three rolls is too complicated: there will be $3\times 3\times 3=27$ possibilities falling into $10$ distinct categories. Instead of halving the damages upon equalling the armor, let's just flip a coin then to determine whether there will be full or no damages. That reduces the outcomes to an $11/20 + (1/2)\times 1/20 = 23/40$ chance of doing nothing and a $40/40 - 23/40 = 17/40$ chance of rolling for damages.

    Since this is intended to be done mentally, note that the $23/40 = 8/20 + (1/2)\times 1/20 = 0.425$ is easily calculated and this is extremely close to a simple fraction $3/7 = 0.42857\ldots.$ We have placed ourselves in a situation equivalent to rolling an unfair coin with $3/7$ chance of success. This has a Binomial distribution:

    • We can roll for damages twice with a chance of $3\times (4/7)\times (3/7)^2= 108/343.$
    • We will roll for damages three times with a chance of $(3/7)^3 = 27/343.$

    (These calculations are very easily learned; all introductory statistics courses cover the theory and offer lots of practice with them.)


Code

To verify this result (which was obtained before many of the other answers appeared), I wrote some R code to carry out such calculations in very general ways. Because they can involve nonlinear operations, such as comparisons and truncation, they do not capitalize on the efficiency of convolutions, but just do the work with brute force (using outer products). The efficiency is more than adequate for smallish distributions (having only a few hundred possible outcomes, more or less). I found it more important for the code to be expressive so that we, its users, could have some confidence that it correctly carries out what we want. Here for your consideration is the full set of calculations to solve this (somewhat complex) problem:

round <- conditional(sign(hit-armor), list(nothing, half(damage), damage))
x <- health - rep(round, n.rounds) # The battle
x <= nothing                       # Outcome distribution

The output is

    FALSE      TRUE 
0.8300265 0.1699735 

showing a 16.99735% chance of success (and 83.00265% chance of failure).

Of course, the data for this question had to be specified beforehand:

hit <- d(1, 20, 4)            # Distribution of hit points
damage <- d(2, 6, 1)          # Distribution of damage points
n.rounds <- 3                 # Number of attacks
health <- as.die(20)          # Opponent's health
armor <- as.die(16)           # Opponent's armor
nothing <- as.die(0)          # Result of no hit

This code reveals that the calculations are lurking in a class I have named die. This class maintains information about outcomes ("value") and their chances ("prob"). The class needs some basic support for creating dice and displaying their values:

as.die <- function(value, prob) {
  if(missing(prob)) x <- list(value=value, prob=1)
  else x <- list(value=value, prob=prob)
  class(x) <- "die"
  return(x)
}
print.die <- function(d, ...) {
  names(d$prob) <- d$value
  print(d$prob, ...)
}
plot.die <- function(d, ...) {
  i <- order(d$value)
  plot(d$value[i], cumsum(d$prob[i]), ylim=c(0,1), ylab="Probability", ...)
}
rep.die <- function(d, n) {
  x <- d
  while(n > 1) {n <- n-1;  x <- d + x}
  return(x)
}
die.normalize <- function(value, prob) {
  i <- prob > 0
  p <- aggregate(prob[i], by=list(value[i]), FUN=sum)
  as.die(p[[1]], p[[2]])
}
die.uniform <- function(faces, offset=0) 
  as.die(1:faces + offset, rep(1/faces, faces))
d <- function(n=2, k, ...) rep(die.uniform(k, ...), n)

This is straightforward stuff, quickly written. The only subtlety is die.normalize, which adds the probabilities associated with values appearing more than once in the data structure, keeping the encoding as economical as possible.

The last function is noteworthy: d(n,k,a) represents the sum of n independent dice with values $1+a, 2+a, \ldots, k+a$. For instance, a 2d6+2 can be considered the sum of two d6+1 distributions and is created via the call d(2,6,1).

The heart of the code is the overloading of arithmetic operations. I implemented only those needed for this calculation, but did so in a way that is easy to extend, as should be evident by all the one-line definitions. The conditional function (a variant of switch) is especially useful.

op.die <- function(op, d1, d2)  {
  if(missing(d2)) {
    values <- op(d1$value)
    probs <- d1$prob
  } else {
    values <- c(outer(d1$value, d2$value, FUN=op))
    probs <- c(outer(d1$prob, d2$prob, FUN='*'))
  }
  die.normalize(values, probs)
}
"[.die" <- function(d1, i) sum(d1$prob[d1$value %in% i])
"==.die" <- function(d1, d2) op.die('==', d1, d2)
">.die" <- function(d1, d2) op.die('>', d1, d2)
"<=.die" <- function(d1, d2) op.die('<=', d1, d2)
"!.die" <- function(d) op.die(function(x) 1-x, d)
"+.die" <- function(d1, d2) op.die('+', d1, d2)
"-.die" <- function(d1, d2) op.die('-', d1, d2)
"*.die" <- function(d1, d2) op.die('*', d1, d2)
"/.die" <- function(d1, d2) op.die('/', d1, d2)
sign.die <- function(d) op.die(sign, d)
half <- function(d) op.die(function(x) floor(x/2), d)
conditional <- function(cond, dice) {
    values <- unlist(sapply(dice, function(x) x$value))
    probs <- unlist(sapply(1:length(cond$prob), 
             function(i) cond$prob[i] * dice[[i]]$prob))
    die.normalize(values, probs)  
}

(If one wanted to be efficient, which might be useful when working with large distributions, rep.die, +.die, and -.die could be specially rewritten to use convolutions. This is unlikely to be helpful in most applications, though, because the other operations would still need brute-force calculation.)

To enable study of the properties of distributions, here are some statistical summaries:

moment <- function(d, k) sum(d$value^k * d$prob)
mean.die <- function(d) moment(d, 1)
var.die <- function(d) moment(d, 2) - moment(d, 1)^2
sd.die <- function(d) sqrt(var.die(d))
min.die <- function(d) min(d$value)
max.die <- function(d) max(d$value)

As an example of their use, here is the health distribution for three damage rolls (the right hand plot in the first figure). The calculation of the total damage distribution is performed by x.3 <- health - rep(damage, 3) (pretty simple, right?) and the Normal approximation is computed via pnorm(x, mean.die(x.3), sd.die(x.3)).

plot(x.3 <- health - rep(damage, 3), type="b", xlim=l, lwd=2, xlab="Health", 
     main="After Three Hits")
curve(pnorm(x, mean.die(x.3), sd.die(x.3)), lwd=2, col="Red", add=TRUE)
abline(v=0, col="Gray")
abline(h = (x.3 <= nothing)[TRUE], col="Blue")

All this ought to port easily to C++.

$\endgroup$
  • 1
    $\begingroup$ I'd have hesitated to use a normal approximation for 2d6 (its a discrete triangular). For 4d6, sure (with a continuity correction). I find it quite interesting you got so very close to the exact answer simply assuming it was normal. $\endgroup$ – Glen_b Sep 26 '14 at 22:50
  • 1
    $\begingroup$ @Glen_b Even more surprisingly, the Normal approximation to the entire distribution yields a close answer (around 14%: execute pnorm(-mean(x)/sd.die(x)) in the code after computing x). Provided we stay out of the tails, a Normal approximation can work well--and some studied judgment concerning asymmetry and continuity corrections can further improve these mental approximations. $\endgroup$ – whuber Sep 26 '14 at 23:03
  • 2
    $\begingroup$ +1 It takes a while to appreciate just how much there is in this answer ... as usual, there's a lot of goodness packed into a surprisingly small space. $\endgroup$ – Glen_b Sep 27 '14 at 0:45
6
$\begingroup$

So if you roll 12 you exactly equal his armour class, and if you roll higher, you beat it.

That's 11/20 chance of 0 damage, 1/20 chance of $\lfloor\frac{_1}{^2}$(2d6+2)$\rfloor$ and 8/20 chance of 2d6+2.

                         Damage distribution (prob x 36)
Event       Prob     0    1    2    3    4    5    6    7    8    9   10   11   12   13   14 
 Hit        0.40                         1    2    3    4    5    6    5    4    3    2    1
Just hit    0.05               3    7   11    9    5    1
 Miss       0.55    36

So the unconditional distribution of per-attack damage is:

                         Damage distribution (prob x 36 x 20)
                     0    1    2    3    4    5    6    7    8    9   10   11   12   13   14 
(Prob x 36 x 20)   396    0    3    7   19   25   29   33   40   48   40   32   24   16    8 
    Prob %        55.0  0.0 0.42 0.97 2.64 3.47 4.03 4.58 5.56 6.67 5.56 4.44 3.33 2.22 1.11

The convolution of damage from three such attacks is:

Dam        0    1    2    3    4    5    6    7    8    9   10   11   12   13   14 
Prob%  16.64 0.00 0.38 0.88 2.40 3.16 3.71 4.29 5.32 6.55 5.82 5.17 4.61 4.11 3.58 

Dam       15   16   17   18   19   20   21   22   23   24   25   26   27   28   29 
Prob%   2.96 3.26 3.44 3.46 3.28 2.98 2.62 2.21 1.79 1.42 1.14 0.94 0.79 0.68 0.59 

Dam       30   31   32   33   34   35   36   37   38   39   40   41   42 
Prob%   0.50 0.41 0.31 0.23 0.16 0.10 0.06 0.03 0.02 0.01 0.00 0.00 0.00 

(While the convolution calculation is straightforward, this convolution was performed using the convolve function in R.)

enter image description here

The probability of doing 20 or more damage = 16.99735%

That is, the desired probability is essentially 17%.

(Interestingly, this is about the same chance as the chance of doing no damage at all.)

Average damage over three attacks is 11.44, median damage is 11.


Incorporating crits:

                         Damage distribution (prob x 36)

Event       Prob                         8   10   12   14   16   18   20   22   24   26   28 
 Crit       0.05                         1    2    3    4    5    6    5    4    3    2    1

Event       Prob     0    1    2    3    4    5    6    7    8    9   10   11   12   13   14 
 Hit        0.35                         1    2    3    4    5    6    5    4    3    2    1
Just hit    0.05               3    7   11    9    5    1
 Miss       0.55    36

The unconditional distribution of per-attack damage is:

                     Damage distribution (prob x 36 x 20)
          0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
(Px720) 396  0  3  7 18 23 26 29 36 42 37 28 24 14 11  0  5  0  6  0  5  0  4  0  3  0  2  0  1

The convolution for three attacks is:

Dam     0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   
Pr% 16.64 0.00 0.38 0.88 2.27 2.91 3.33 3.78 4.79 5.73 5.33 4.49 4.35 3.49 3.50 

       15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   
     2.42 3.29 2.80 3.60 2.73 3.18  2.30 2.54 1.74 1.88 1.31 1.44 1.07 1.14 0.91 

       30   31   32   33   34   35   36   37   38   39   40  >40
     0.86 0.74 0.68 0.55 0.51 0.39 0.37 0.26 0.26 0.18 0.19 0.78

enter image description here

So we get Prob(damage $\geq$ 20) = 23.28396 %

$\endgroup$
  • $\begingroup$ Nice. Going this distance and using R. $\endgroup$ – Micah Sep 26 '14 at 16:45
5
$\begingroup$

One way to get at this fairly simply is just through simulation - you won't get the exact percentage to the second decimal, but you can nail it down very closely. I've input some R code below that will simulate the rolls you're making and spit out the probability that your ally dies.

# Creating a hundred thousand sets of your three rolls to hit     
roll.1 <- sample(1:20, replace = TRUE, 100000)
roll.2 <- sample(1:20, replace = TRUE, 100000)
roll.3 <- sample(1:20, replace = TRUE, 100000)

# Creating a hundred thousand sets of three damage rolls
damage.1 <- replicate(100000, (sample(1:6, 1) + sample(1:6, 1) + 2))
damage.2 <- replicate(100000, (sample(1:6, 1) + sample(1:6, 1) + 2))
damage.3 <- replicate(100000, (sample(1:6, 1) + sample(1:6, 1) + 2))

# Here we calculate the damage of each roll. Essentially this line is saying
# "Apply the full damage if the hit roll was 13 or more (13 + 4 = 17), and 
# apply half the damage if the roll was 12." Applying zero damage when the roll
# was less than 12 is implicit here.
hurt.1 <- ((roll.1 >= 13) * damage.1 + floor((roll.1 == 12) * damage.1 * .5))
hurt.2 <- ((roll.2 >= 13) * damage.2 + floor((roll.2 == 12) * damage.2 * .5))
hurt.3 <- ((roll.3 >= 13) * damage.3 + floor((roll.3 == 12) * damage.3 * .5))

# Now we just subtract the total damage from the health
health <- 20 - (hurt.1 + hurt.2 + hurt.3)

# And this gives the percentage of the time you'd kill your ally.
sum(health <= 0)/1000000

When I run this, I consistently get between 16.8% and 17.2%. So you had about a 17% chance of killing your ally with this spell.

If you're interested, the below code also computes the exact probability using the method outlined in Micah's answer. It turns out the exact probability is 16.99735%

# Get a vector of the probability to hit 0, 1, 2, and 3 times. Since you can
# only kill him if you get 2 hits or more, we only need the latter 2 probabilities
hit.times <- (dbinom(0:3, 3, 9/20))

# We'll be making extensive use of R's `outer` function, which gives us all
# combinations of adding or multiplying various numbers - useful for dice 
# rolling
damage.prob <- table(outer(1:6, 1:6, FUN = "+") + 2)/36

damage.prob <- data.frame(damage.prob)
colnames(damage.prob) <- c("Damage", "Prob")
damage.prob$Damage <- as.numeric(as.character(damage.prob$Damage))

# Since we'll be multiplying by the probability to hit each number of times 
# later, we just use 8/9 as the probability to get full damage, and 1/9 as 
# the probability of half damage.
damage.prob.full <- data.frame("Damage" = damage.prob$Damage, "Prob" = damage.prob$Prob * 8/9)
damage.prob.half <- data.frame("Damage" = damage.prob$Damage * .5, "Prob" = damage.prob$Prob * 1/9)

# Rounding down the half damage
damage.prob.half$Damage <- floor(damage.prob.half$Damage)
damage.prob.half <- aggregate(damage.prob.half$Prob, by = list(damage.prob.half$Damage), FUN = sum)
colnames(damage.prob.half) <- c("Damage", "Prob")

damage.prob.total <- merge(damage.prob.full, damage.prob.half, by = "Damage", all.x = TRUE, all.y = TRUE)
damage.prob.total$Prob <- rowSums(cbind(damage.prob.total$Prob.x, damage.prob.total$Prob.y), na.rm=TRUE)

# Below I'm multiplying out all the damage probabilities for 2 and 3 hits, then
# summing the probabilities of getting each damage total that equals 20 or more.

damage.2 <- outer(damage.prob.total$Damage, damage.prob.total$Damage, FUN = '+')
prob.kill.2 <- sum(outer(damage.prob.total$Prob, damage.prob.total$Prob)[damage.2 >= 20])

damage.3 <- outer(outer(damage.prob.total$Damage, damage.prob.total$Damage, FUN = "+"), damage.prob.total$Damage, FUN = "+")

prob.kill.3 <- outer(outer(damage.prob.total$Prob, damage.prob.total$Prob), damage.prob.total$Prob)[damage.3 >= 20]

# Now we just multiply the probability of killing with 2 hits by the probability
# of hitting twice, and the same for 3 hits. Adding that together we get the 
# answer.

sum(prob.kill.2)*hit.times[3] + sum(prob.kill.3)*hit.times[4]
$\endgroup$
  • $\begingroup$ Hey when computing variables hurt 1-3 you do it for roll.1 only. Also in case of d12=12 shouldn't it be multiplied by 0.5 and then floor()ed? He said rounded down. $\endgroup$ – WojciechF Sep 26 '14 at 2:00
  • $\begingroup$ Take another look, I edited to fix those before your comment :) $\endgroup$ – Sean Murphy Sep 26 '14 at 2:58
  • $\begingroup$ does this look right for the simulation to add the critical? hurt.1 <- ((roll.1+4 >= 17) * damage.1 + floor((roll.1+4 == 16) * damage.1 * .5)+(roll.1 == 16)*damage.1*2) ..roll 2..3.. $\endgroup$ – Phillip Byram Sep 26 '14 at 13:54
  • $\begingroup$ You'd need to make it roll.1 == 20, not 16 (since you said a natural 20). You'd also need to specify that (roll.1+4 >= 17 & roll.1+4 != 20) so you're not double dipping in the 20's $\endgroup$ – Sean Murphy Sep 26 '14 at 15:41
  • $\begingroup$ not sure why I wrote 16 instead of 20. But I didn't realize the double 20 thing. I'm writing some C# code since I'm more familiar with it to do the simulation with some other factors I didn't realize my ally could do(my ally can also make me reroll an attack and take the lower one). I'll post it when I'm done for a logic check :) $\endgroup$ – Phillip Byram Sep 26 '14 at 16:45
1
$\begingroup$

You need to break it down. Start with one attempt. You have a 1/20 chance of rolling exactly at his armor class and you have an 8/20 change of beating it [13-20].

So then you have a probability distribution of damage which is a mixture of three cases: 0 with probability 11/20, 2d6+2 with probability 8/20 and 2d6+2/2 with probability 1/20.

Suppose you beat his AC, then the distribution of 2d6+2 can be broken down to

 p     dmg    total
1/36 : 2+2  = 4
2/36 : 2+3  = 5
3/36 : 2+4  = 6
4/36 : 2+5  = 7
5/36 : 2+6  = 8
6/36 : 2+7  = 9
5/36 : 2+8  = 10
4/36 : 2+9  = 11
3/36 : 2+10 = 12
2/36 : 2+11 = 13
1/36 : 2+12 = 14

You then have to adjust those probalities for the fact that you have an 8/20 chance of it happening:

 dmg prob   
 4   0.011111 
 5   0.022222 
 6   0.033333 
 7   0.044444 
 8   0.055556 
 9   0.066667 
 10  0.055556 
 11  0.044444 
 12  0.033333 
 13  0.022222 
 14  0.011111 

If you hit for half, then you have to again multiply by 1/20

   dmg  prob     
     2  0.0013889  
     2  0.0027778  
     3  0.0041667  
     3  0.0055556  
     4  0.0069444  
     4  0.0083333  
     5  0.0069444  
     5  0.0055556  
     6  0.0041667  
     6  0.0027778  
     7  0.0013889  

So now you have several ways to get 4,5,6,7 dmg, since this is an OR relationship (4 OR 5 OR 6 OR 7) you sum up the probabilities of those things happening:

dmg prob     
 0                                   = 0.55
 2  0.0013889 + 0.0027778            = 0.0041667
 3  0.0041667 + 0.0055556            = 0.0097223
 4  0.0069444 + 0.0083333 + 0.011111 = 0.0263887
 5  0.0069444 + 0.0055556 + 0.022222 = 0.0347220
 6  0.0041667 + 0.0027778 + 0.033333 = 0.0402775
 7  0.0013889             + 0.044444 = 0.0458329
 8  0.055556                         = 0.055556
 9  0.066667                         = 0.066667
 10 0.055556                         = 0.055556
 11 0.044444                         = 0.044444
 12 0.033333                         = 0.033333
 13 0.022222                         = 0.022222
 14 0.011111                         = 0.011111
 SUM                                   1

To do this three times, you then need to figure out all possible ways to break the number you want (20) and sum up the probabilities of each of them happening. The probability of A AND B happening is the product of the (or 3) probabilities. So for instance one possibility is a 14 AND a 6, the probability of that happening is 0.11111*0.04. You can make it easier by assuming you get one number for the first roll then summing up the probabilities that will result in > 20). So you would assume you will get a 14 on the first roll and then multiply the probability of that happening AND the probability of getting greater than 6 on the next roll (6 OR 7 OR ...). So then it would be P_14*(P_6+P_7+P_8+...P_20), ie the probability of rolling a 14 AND (6 OR 7 OR ...). You would then need to say what is the probability of (2 AND 4 AND 14) OR (2 AND 5 AND (13 OR 14)) OR (2 AND 6 AND (12 OR 13 OR 14)) ... and so on. I'll leave that last part to you. I would guess you would want to do some programming.

Coming with a general formula is also possible, but it will be big.

$\endgroup$
  • $\begingroup$ Oh lord!! This is beautiful. I really appreciate the time you put into this! I'll dive into it when I have more time later tonight. Thank you so much! $\endgroup$ – Phillip Byram Sep 25 '14 at 22:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.