I'm trying to think of the answer to a certain problem, but I'm finding it hard to calculate.

How much of a statistical edge do you have in a scenario where all of the following are true?

• You engage in a contest with an opponent
• Your modifier to your roll is within ±15 of your opponent's modifier to their own roll.

This question does not consider non-static modifiers, such as Bardic Inspiration, or other advantage mechanics, such as Luck. Keeping it simple.

Instead of showing the calculations for the entire range, an Anydice program, or a general formula, are also acceptable.

First considering without modifiers

When you have advantage the probability of getting a result of X can be governed by the equation

$$P(x)= \frac {2x-1} {400}$$

When you have disadvantage the probability of getting a result of X can be governed by the equation

$$P(z)= \frac {41-2z} {400}$$

The overall Probability of winning when you roll x, with advantage vs disadvantage, that you roll X is given by:

$$P(\text{Winning by rolling } x) = P(x) \times P(z < x)$$

$$P(z < x) = f(x)$$

With a little looking we see that each probability for $P(z)$ has the common thread of a reduction by 0.005

Such that $P(z=2) = P(z=1)-0.005$ and $P(z=3) = P(z=2)-0.005$

For any particular $x$ we want to sum P(z) over z=1 to z=x-1 From this we get that:

$$P(z < x) = 0.0975(x-1) - 0.005 \dfrac {(x-1)(x-2)} {2} \qquad \text{where 1 =< x =< 20}$$

(The $\frac{(x-1)(x-2)}{2}$ term simply denotes multiplying by a series of triangle numbers, starting at $1$ when $x=3$)

Hence we get:

$$P(\text{Winning by rolling } x)= \dfrac{\left(2x-1\right)\left(0.0975(x-1)-\left(0.005 \dfrac{(x-1)(x-2)}{2}\right)\right)} {400}$$

or alternately:

$$P(x)=-0.0000125(x-40)(x-0.5)x$$

Now we can sum over all values of $x$ to get $P(\text{Winning})$ for all $x$

$$P(\text{Winning}) = 0.8158125$$

Now we consider modifiers

By including modifiers we're no longer summing from $z=1$ to $z=x-1$.

Instead we have to sum from $z=2-m$ to $z=x+m-1$. Here we hit trouble. If $2-m < 1$ then those terms won't be valid. If $x+m-1 > 20$ then, again those terms won't be valid.

We, however, want that if $z < 1$ that the formula $f(z) = 0$ and that if $z > 20$ that $f(z) = 1$.

$2-m < 1$ occurs when $m > 1$ and that $x+m-1 > 20$ when $m > 2$

Without a good way to enforce these restrictions in-formula we're forced to give the following:

\begin{align} f(z) &= \dfrac{41-2z}{400} & \text{for 1 =< z =< 20} \\ f(z) &= 0 & \text{for z < 1} \\ f(z) &= 1 & \text{for z < 20} \end{align}

Hence we have:

$$P(\text{Winning by rolling x})= \sum\limits_{a=2-m}^{a=x+m-1} f(a) \times \frac{2x-1}{400}$$

Previously from here we could produce a formula for the P(Winning). Here, however, we cannot algebraically sum for all values of x in a way that can be simplified. Instead we get the following:

$$P(\text{Winning})= \sum\limits_{x=1}^{x=20} (\sum\limits_{a=2-m}^{a=x+m-1} f(a) \times \frac{2x-1}{400})$$

However, since our sums behave nicely for $m < 0$ we can create the following:

$$P(\text{Winning}) = (783180+34399 m-2278 m^2-82 m^3+m^4)/960000 \qquad \text{ for m < 0}$$

Graph of Probability of Winning as function of m: This is sort of brute-force, but the computers nowadays...:

>>> all = [(i,j,k,l) for i in range(20) for j in range(20) for k in range(20) for l in range(20)]
>>> p = lambda X: 1.0*len([t for t in all if max(t,t) + X > min(t,t)])/len(all)
>>> print '\n'.join("%3d: %.4f" % (X, p(X)) for X in range(-15,16))
-15: 0.0854
-14: 0.1235
-13: 0.1664
-12: 0.2133
-11: 0.2635
-10: 0.3160
-9: 0.3702
-8: 0.4253
-7: 0.4805
-6: 0.5352
-5: 0.5887
-4: 0.6402
-3: 0.6894
-2: 0.7354
-1: 0.7777
0: 0.8158
1: 0.8492
2: 0.8778
3: 0.9021
4: 0.9226
5: 0.9397
6: 0.9538
7: 0.9653
8: 0.9745
9: 0.9817
10: 0.9873
11: 0.9915
12: 0.9945
13: 0.9967
14: 0.9981
15: 0.9990
>>>

(Python ftw.)

Plotting it is of course possible with few more libraries, in particular pyplot. The following plots a nice graph:

>>> import matplotlib.pyplot as plt
>>> Xrange = range(-15,16)
>>> plt.plot(Xrange, map(p, Xrange))
>>> plt.show() Short explanation:

• define all as a list of all possible 4 times d20 rolls (two for your advantage, two for the opponent's disadvantage)
• p is a function returning (relative) count of all winning results in the sequence all, modified by X (passed to p as a parameter)
• Xrange is just a range (list) -15..+15
• to plot it, just use the range and all values of p applied (mapped) to the values in the range.
• I find this brute force beautiful. Can you graph this with Python by any chance, and a little more commenting/explanation of your algorithm for us non-Python users?
– user27327
May 10 '16 at 9:33
• Just nitpicking but could you add more significant figures in your results? Winning is not certain unless you have a +20 (at +15, it "only" 0.99903125). May 10 '16 at 10:07
• updated: added graph, explanation, and two more decimal places May 10 '16 at 10:28
• The most interesting point I find here is that the "50% point" is between -6 and -7. This means a -7 can offset the simultaneous advantage/disadvantage scenario. Inversely, +7 gap between you and your opponent is just as good as simultaneous advantage/disadvantage.
– user27327
May 10 '16 at 10:41

Everyone is making it hard on themselves - first ignore the modifier and use anydice to calculate output [lowest 1 of 2d20] - [highest 1 of 2d20] and display it as at most, giving this:

#   %
-19 0.95
-18 2.75
-17 5.32
-16 8.54
-15 12.35
-14 16.64
-13 21.33
-12 26.35
-11 31.60
-10 37.02
-9  42.53
-8  48.05
-7  53.52
-6  58.87
-5  64.03
-4  68.94
-3  73.54
-2  77.77
-1  81.58
0  84.92
1  87.78
2  90.21
3  92.27
4  93.97
5  95.38
6  96.53
7  97.45
8  98.17
9  98.73
10  99.15
11  99.45
12  99.67
13  99.81
14  99.90
15  99.96
16  99.98
17  100.00
18  100.00
19  100.00

You can then treat the numbers as the modifier and the result is that of tying or beating the opponent with that modifier (read the line above for strictly beating). So, for -15, you will win 8.54% of the time; for +15 , its 99.9%.

Here's an AnyDice program to do what you want:

function: greater A:n or B:n{
if A > B {result: 1}
if A < B {result: -1}
result: 0
}

loop MOD over {-15..15}{
output [greater [highest 1 of 2d20]+MOD or [lowest 1 of 2d20]] named "[MOD]"
}

Setting the data view to "Transposed" shows the chances to lose as "-1", a tie as "0" and the chances to win as "1". As a graph: 