The advantage of advantage

Published on December 10, 2024 20 minute read
Back to homepage

Last time we developed a tool to determine the exact damage distribution of attacks in games like Dungeons & Dragons and Pathfinder. And while a lot could be modeled with it, many mechanics were missed as well. Today, we're going to look at one specifically: Dungeons & Dragons 5th Edition's advantage system.

Having advantage on a roll with a twenty-sided die (called a d20) means you roll it twice, and take the highest result (also called 2d20 drop lowest), increasing your chances of success. Conversely, having disadvantages means you roll twice, and take the lowest result (also called 2d20 drop highest). While most players praised the system's simplicity, some still advocated for a system where they would be replaced by a flat bonus or penalty. But can we even find a bonus and penalty that are sensible replacements?

Source: https://xkcd.com/244/

Well that's easy, right? We just look at the expected value of rolling a d20 with and without advantage, and add a bonus equal to the difference. The expected values turn out to be as follows (where max(d20,d20) denotes rolling with advantage):

E[d20]=10.5, E[max(d20,d20)]=13.825.

This yields a difference of 3.325, and since fractional modifiers don't exist in Dungeons & Dragons, we simply say that advantage amounts to an effective bonus of +3 or +4 and we're done.

Not quite. By just looking at the expected value, we're losing something that's fundamentally different about rolling with advantage; it skews the distribution towards higher results. It turns out that depending on which target number you need to beat with your roll, having advantage is equivalent to a different bonus:

P(max(d20,d20)1)P(d20+01), P(max(d20,d20)2)P(d20+12), P(max(d20,d20)3)P(d20+23), P(max(d20,d20)4)P(d20+34), P(max(d20,d20)5)P(d20+35), P(max(d20,d20)6)P(d20+46), P(max(d20,d20)7)P(d20+47), P(max(d20,d20)8)P(d20+58), P(max(d20,d20)9)P(d20+59), P(max(d20,d20)10)P(d20+510), P(max(d20,d20)11)P(d20+511), P(max(d20,d20)12)P(d20+512), P(max(d20,d20)13)P(d20+513), P(max(d20,d20)14)P(d20+514), P(max(d20,d20)15)P(d20+415), P(max(d20,d20)16)P(d20+416), P(max(d20,d20)17)P(d20+317), P(max(d20,d20)18)P(d20+318), P(max(d20,d20)19)P(d20+219), P(max(d20,d20)20)P(d20+120).

So having advantage is equivalent to up to a +5 bonus depending on which number you are trying to beat!

But for damage rolls, even this still isn't the full picture. Advantage doesn't just make you less likely to miss, but also makes it much more likely to deal critical damage. Recall that last time we used probability generating functions to model damage distributions:

Gattack(x)=pmissx0+phitGhit(x)+pcritGcrit(x).

Using the same model, we can exactly account for advantage by simply appropriately changing the miss, hit, and crit probabilities pmiss, phit and pcrit. Afterward, we can directly compare the resulting distribution with a distribution where advantage is replaced by a flat bonus, and find the specific bonus that causes the distributions to be the most similar!

Adjusted probabilities

To determine these new probabilities, we need to consider all 400 combination of two d20 rolls, and determine the outcome of each pair. Below are two tables where the rows and columns represent the roll outcomes of both dice, and the corresponding cell shows the final outcome. Hovering over (or, on mobile, clicking on) a cell highlights all cells with the same outcome. Can you spot how (dis)advantage affects your odds?

1234567891011121314151617181920
11234567891011121314151617181920
22234567891011121314151617181920
33334567891011121314151617181920
44444567891011121314151617181920
55555567891011121314151617181920
66666667891011121314151617181920
77777777891011121314151617181920
88888888891011121314151617181920
99999999991011121314151617181920
101010101010101010101011121314151617181920
111111111111111111111111121314151617181920
121212121212121212121212121314151617181920
131313131313131313131313131314151617181920
141414141414141414141414141414151617181920
151515151515151515151515151515151617181920
161616161616161616161616161616161617181920
171717171717171717171717171717171717181920
181818181818181818181818181818181818181920
191919191919191919191919191919191919191920
202020202020202020202020202020202020202020
Advantage
1234567891011121314151617181920
111111111111111111111
212222222222222222222
312333333333333333333
412344444444444444444
512345555555555555555
612345666666666666666
712345677777777777777
812345678888888888888
912345678999999999999
101234567891010101010101010101010
111234567891011111111111111111111
121234567891011121212121212121212
131234567891011121313131313131313
141234567891011121314141414141414
151234567891011121314151515151515
161234567891011121314151616161616
171234567891011121314151617171717
181234567891011121314151617181818
191234567891011121314151617181919
201234567891011121314151617181920
Disadvantage

There are multiple equivalent ways to arrive at the right formulae. One way would be to simply count the number of combinations leading to a specific outcome, and noting that it is linearly increasing for advantage, and linearly decreasing for disadvantage (with slope 2 and -2 respectively), and that for advantage there should be only 1400 probability of landing a 1, while for disadvantage this is the case for a 20.

Another way would be to consider that the different rolls that leads to a specific outcome n can be grouped into three categories. For advantage this would be: one where the first die lands on n and the second is strictly lower (there are n-1 such pairs of rolls), one where the second die lands on n and the first is strictly lower (again, there are n-1 such pairs of rolls), and on where both are exactly equal to n (there is only 1 pair of rolls that leads to this). These correspond to the highlighted row, column and corner piece when hovering over a specific outcome.

Finally, you could consider that, for advantage, the number of outcomes corresponding to getting n or less form a square, so there are n2-(n-1)2 outcomes out of 400 to get exactly n.

Each of these lines of reasoning would lead you to the following equations for the probability distributions of advantage and disadvantage on a d20 roll:

P(max(d20,d20)=n)=2n-1400, P(min(d20,d20)=n)=41-2n400.

For regular d20 rolls, we can use d20Outcomes to determine the miss, hit and crit probabilities. It works by simply checking each outcome, and counting them uniformly (each outcome just counts as "one"):

/** * Determines miss, hit and crit probabilities for a regular d20 roll */ function d20Outcomes(totalAttackBonus, critRange, armorClass) { let hitOutcomes = 0 let critOutcomes = 0 for(let d20Roll = 1; d20Roll <= 20; d20Roll++) { if (d20Roll == 1) { // Critical failure continue } else if (d20Roll >= critRange) { // Critical hit critOutcomes++ } else if (d20Roll + totalAttackBonus >= armorClass) { // Regular hit hitOutcomes++ } } return [(20-hitOutcomes-critOutcomes)/20, hitOutcomes/20, critOutcomes/20] }

Implementing advantage and disadvantage is very straightforward: rather than counting each outcome just once, we add a bias to each outcome. Each outcome counts as many times as they occurred according to the formulae from above:

/** * Determines miss, hit and crit probabilities for a d20 roll with advantage */ function d20OutcomesWithAdvantage(totalAttackBonus, critRange, armorClass) { let hitOutcomes = 0 let critOutcomes = 0 for(let d20Roll = 1; d20Roll <= 20; d20Roll++) { if (d20Roll == 1) { // Critical failure continue } else if (d20Roll >= critRange) { // Critical hit critOutcomes += 2*d20Roll - 1 else if (d20Roll + totalAttackBonus >= armorClass) { // Regular hit hitOutcomes += 2*d20Roll - 1 } } return [(400-hitOutcomes-critOutcomes)/400, hitOutcomes/400, critOutcomes/400] } /** * Determines miss, hit and crit probabilities for a d20 roll with disadvantage */ function d20OutcomesWithDisadvantage(totalAttackBonus, critRange, armorClass) { let hitOutcomes = 0 let critOutcomes = 0 for(let d20Roll = 1; d20Roll <= 20; d20Roll++) { if (d20Roll == 1) { // Critical failure continue } else if (d20Roll >= critRange) { // Critical hit critOutcomes += -2*d20Roll + 41 } else if (d20Roll + totalAttackBonus >= armorClass) { // Regular hit hitOutcomes += -2*d20Roll + 41 } } return [(400-hitOutcomes-critOutcomes)/400, hitOutcomes/400, critOutcomes/400] }

Comparing distributions

So we can now determine the damage probability of making an attack with advantage, but when can we consider another distribution to be approximately equal to it? We will consider two simple methods:

Let Xadv. and Xbonus(B) denote the stochastic variables describing the damage when rolling with advantage and rolling with an additional +B attack bonus. For the first comparison method, we find the bonus B such that the difference in expected values |E[Xadv.]-E[Xbonus(B)]| is minimized. This can be simply be calculated through:

/** * Calculates the expected value of the given distribution */ function expectedValue(distribution) { return distribution.reduce((accumulator, outcome, index) => accumulator + outcome*index, 0) } /** * Calculates the difference in expected value between two distributions */ function expectationDifference(distribution1, distribution2) { const expectedValue1 = expectedValue(distribution1) const expectedValue2 = expectedValue(distribution2) return expectedValue1 - expectedValue2 }

For the second comparison method, we want to find the bonus B such that difference in probability of either variable being greater than the other |P(Xadv.>Xbonus(B))-P(Xadv.<Xbonus(B))| is minimized. This can be evaluated in a single pass over the state space using probabilityDifference, by considering each outcome k of distribution1 and checking the portion of distribution2 lying above and below k:

/** * Calculates where distribution1 is more likely to be greater than distribution2 or vice versa */ function probabilityDifference(distribution1, distribution2) { let difference = 0 let distribution2LessThanN = 0 let distribution2GreaterThanN = 1 zipWithDefault(distribution1, distribution2, 0).forEach(pair => { const [p1, p2] = pair distribution2GreaterThanN -= p2 difference += p1 * (distribution2LessThanN - distribution2GreaterThanN) distribution2LessThanN += p2 }) return difference }

Equivalent flat bonus calculator

Now we are finally equipped to give a conclusive answer! So, what is the bonus equivalent to advantage? As we noted before, it depends. The tool below plots the equivalent flat bonus as a function of the Armor Class of the enemy you are facing.

Interestingly, the equivalent bonus increases linearly for Armor Classes larger than crit range+attack bonus. Since in those cases, you would only deal damage with a critical hit, which is a lot more likely when having advantage. Additionally, getting a higher attack bonus does not increase your chance of landing a critical hit, so the equivalent attack bonus needs to increase proportionally with the Armor Class to achieve the same effect as having advantage.

Attacks

Damage dice can be specified as sums of dice, e.g. 3d6+d4+2. The extra damage dice represent the dice that are not multiplied by critical hits, they are added after the crit multiplier is applied, and are specified in the same format as the normal damage dice. I.e., when a roll hits the critical hit range or higher, a critical hit is scored, multiplying the damage dice, but not the extra damage dice. A critical hit range of 21 represents not being able to score critical hits with that attack.

Enemy statistics

The Armor Class of the enemy can not be specified. It is varied in the output below from 0 to 50. The equivalent flat attack bonus depends on the Armor Class.

Output

Attack bonus equivalent to Advantage per Armor Class

The horizontal axis denotes the Armor Class of the enemy you are facing, and the vertical axis the bonus that is equivalent to advantage against an enemy of that Armor Class. The damage distributions can be compared by expected value (yielding the bonus that makes the expected values the most similar), or by probability (yielding the bonus that minimizes the probability of either distribution being greater than the other).

The damage distribution calculator now also supports advantage and disadvantage. 😊