Sign up ×
Role-playing Games Stack Exchange is a question and answer site for gamemasters and players of tabletop, paper-and-pencil role-playing games. It's 100% free, no registration required.

I'm playing a home made game, and it uses a slightly complicated system with regards to training.

It works rolling 1d10+bonuses against a variable target number, minus "headway". Headway is gained if you fail to reach the modified target number, and how much is determined by rolling a specific dice determined by how good you are at that matter. The "headway" is cumulative, incidentally, and is lost upon success.

So, for example, training a specific ability the target number might be 10, you have a bonus of 1, and a headway dice 1d3. You get a 5 on the 1d10, for a total of 6, failing the roll. You then roll the 1d3, and get a 1. The next time you train that ability, 1d10+1 would require a roll of 9 for success (the +1 headway will push you to 10). If you fail that, you roll again, and get a 3, meaning that the next time you roll, you only need a roll of 6 (you now have headway of 1+3).

What formula would be needed so we can determine the odds of success from multiple such rolls?

share|improve this question
Hi, I've edited your word usage to say roll of 6/9 rather than total of 6/9. Total is generally used to indicate your roll plus any bonuses (headway in this case), but you appear to be referring to the roll itself before bonuses are applied. – doppelgreener Aug 19 '14 at 7:45
Is this the correct programatic formula? if( 1d10+mod >= target ) { Success! } else { target = target - 1d3;} repeat; ? – GMNoob Aug 19 '14 at 7:45
Actually, yeah: @BFldyq I've assumed, perhaps mistakenly, that headway adds to the roll and does not shift the target number. If headway lowers the target (same effect, different approach) my edit may be incorrect. – doppelgreener Aug 19 '14 at 7:47
I believe the headway lowers the target. There's a pre-existing +1 bonus in the example and the first roll is awkwardly noted as "roll of 5 on the 1d10 for a total of 6", which would be clearer as "total of 6 on 1d10+1" for consistency with the next iteration's notation, since we don't need to have how to use bonuses explained. – SevenSidedDie Aug 19 '14 at 8:18
Yes, the headway does indeed lower the target number. I have no idea why it's not a simple bonus, but that's how it is. I suspect it was for book-keeping purposes. – BFldyq Aug 19 '14 at 8:47

3 Answers 3


I tried the below program but it failed to function properly, the odds that it returns are not correct. I ran my own program with 1 million itterations and got the following results instead:

You can Run the program here:

Sample image

My previous attempted sollution is here But the results are not correct.

set "maximum function depth" to 30
function: traintarget of TT:n with roll R:n rollnumber N:n {
  if R>=TT {result: N}
  else {
    NUM_OF_ROLLS: N + 1
    TARGET: TT - 1d3
    result: [traintarget of TARGET with roll R rollnumber NUM_OF_ROLLS] 

output [traintarget of TARGET with roll (1d10+BONUS) rollnumber NUM_OF_ROLLS] named "Number of Rolls to Reach Target"
share|improve this answer
Thank you very much for the help! – BFldyq Aug 19 '14 at 8:52

I just couldn't get it to work on AnyDice, so here's a Python 3 script that calculates the number of tries iteration times and yields the results in a nice table. It's far slower than GMNoob's AnyDice answer, and the results do not match.

from math import sqrt
import random

bonus = 1
iterations = 10000000

def d(x): return random.randint(1,x)

results = [0]*(10-bonus)
for i in range(iterations):
    target = 10
    tries = 1
    success = False
    while not success:
        success = (d(10)+bonus>=target)
        if success:

average = sum([results[i]*(i+1) for i in range(len(results))])/iterations
deviation = sqrt(sum([results[i]*(i-average)**2 for i in range(len(results))])/(iterations-1))

print("Average: {:.3f}+-{:.3f}".format(average,deviation))
print("Number of Tries | "+" | ".join([str(x).rjust(6) for x in range(1,1+len(results))]))
print("Chance          | "+" | ".join([str(round(x/iterations*100,3)).rjust(6) for x in results]))

Try it online at Ideone.

I ran it with 10000000 iterations on my machine, with the following results:

Average: 2.532+-1.509
Number of Tries |      1 |      2 |      3 |      4 |      5 |      6 |      7 |      8 |      9
Chance          | 19.997 | 32.006 | 28.262 | 14.904 |  4.198 |  0.591 |  0.042 |  0.001 |    0.0
share|improve this answer
I wrote my own program and can confirm these numbers are correct. – GMNoob Aug 19 '14 at 11:51

Here's a faster Python routine that gives exact results (up to the limits of numerical precision, anyway):

# Target number and initial bonus + headway:
target = 10
bonus = 1
headway = 0

# Die sizes:
attempt_die = 10  # 1d10
headway_die = 3   # 1d3

# print header:
print "target: {0}, bonus {1:+d}, initial headway {2}".format(
    target, bonus, headway)
print "rolling 1d{0} for success, 1d{1} for headway".format(
    attempt_die, headway_die)

# initial headway distribution:
distribution = [0.0] * (max(target - bonus, headway) + 1)
distribution[headway] = 1.0

def clamp(probability):
    return min(1, max(0, probability))

cumulative = 0.0  # total success probability so far
expected = 0.0  # expected number of attempts needed

for attempt in xrange(1, 1 + max(1, target - bonus - headway)):
    success = 0.0  # total success probability for this attempt

    # loop through possible current headway values (in reversed order,
    # to avoid having to copy the distribution list):
    for curr_hw in reversed(xrange(len(distribution))):
        headway_prob = distribution[curr_hw]
        if headway_prob == 0: continue

        # probability of rolling below the target:
        failure_prob = clamp(float(target - bonus - curr_hw - 1) / attempt_die)
        success += headway_prob * (1 - failure_prob)

        # if the initial roll fails, increment headway (if headway exceeds
        # target - bonus, set it to that value so that next roll will
        # automatically succeed):
        distribution[curr_hw] = 0
        for roll in xrange(1, 1 + headway_die):
            new_hw = min(curr_hw + roll, len(distribution) - 1)
            distribution[new_hw] += failure_prob * headway_prob / headway_die

    cumulative += success
    expected += success * attempt
    print "success on attempt {0:2d}:".format(attempt), \
        "{0:9.5f}%".format(success * 100), \
        "(cumulative {0:9.5f}%)".format(cumulative * 100)

    # print "headway:", ", ".join(
    #     "{0}: {1:g}%".format(hw, 100 * distribution[hw]) \
    #     for hw in range(len(distribution))
    # )

print "expected number of attempts needed =", expected

Here's the output for target = 10, bonus = 1 and headway = 0:

target: 10, bonus +1, initial headway 0
rolling 1d10 for success, 1d3 for headway
success on attempt  1:  20.00000% (cumulative  20.00000%)
success on attempt  2:  32.00000% (cumulative  52.00000%)
success on attempt  3:  28.26667% (cumulative  80.26667%)
success on attempt  4:  14.90370% (cumulative  95.17037%)
success on attempt  5:   4.19664% (cumulative  99.36701%)
success on attempt  6:   0.58975% (cumulative  99.95677%)
success on attempt  7:   0.04180% (cumulative  99.99857%)
success on attempt  8:   0.00142% (cumulative  99.99998%)
success on attempt  9:   0.00002% (cumulative 100.00000%)
expected number of attempts needed = 2.53240636049

This code works by maintaining the probabilities of all possible current headway values in the distribution array, and updating them based on the probabilities of the various possible outcomes of the die rolls.

If desired, the code above can be made to output the distribution of different headway values after each attempt by un-commenting the extra print statement at the end of the outer loop. (Note that this is not, strictly speaking, a proper probability distribution, since it does not include the probability that the training has already succeeded. Thus, for later attempts, the headway probabilities will sum to a value strictly less than 100%, although adding in the cumulative success probability will fix this.)

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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