How do I calculate average damage for WFRPG using AnyDice?

In First Edition of WFRPG Damage during combat works as follows:

You roll 2d10 as a perctile die. If you get lower than your skills you hit. You then roll a 1d6 + damage modifier. If you roll a 6, you explode the dice, and roll again.

What is the best way to solve this problem, so that I can help my DM tweak the monster stats to make combat more of a challenge?

(We have advanced about 5-6 more times than the module thinks we have)

• Question was answered here: chat.stackexchange.com/transcript/message/11127766#11127766 – GMNoob Sep 8 '13 at 14:24
• A better question is "Why do you need to use AnyDice for that calculation?". – user9281 Sep 9 '13 at 0:41
• @RickyDemer Do you know of a better way to get an accurate picture of the impact of these changes? – GMNoob Sep 9 '13 at 7:29
• Solve (1+2+3+4+5+(x+x))/6 = x to get the average total from a single exploding die, add the damage modifier, and then multiply that by the hit probability. – user9281 Sep 9 '13 at 8:03
• rpg.stackexchange.com/questions/3274/… is the possible duplicate. – Vatine Sep 11 '13 at 0:52

Following AnyDice code will generate probability of a defender dying on each of the rounds 1-10, as well as probabilities of taking various amounts of damage from each attack:

WS: 30
STRENGTH: 3
TOUGHNESS: 3
WOUNDS: 8
loop N over {1..10}
{
HITCHANCE: (1d100 < WS)
DAMAGE: HITCHANCE*([explode d6] + STRENGTH)
DAMAGETAKEN: [highest of (DAMAGE - TOUGHNESS) and 0]
WOUNDS: WOUNDS-DAMAGETAKEN
output WOUNDS<=0 named "dead in [N] round(s)"
}
output DAMAGETAKEN named "Damage taken"


Values for WS and STRENGTH should be set to the attacker's parameters, while TOUGHNESS and WOUNDS to those of the defender. The average damage value from the last graph can be used for quick estimation of the average number of rounds the defender will live.

• won't be correct, since you have to confirm the open end on the first damage die, by remaking the to hit check. Note also that you don't confirm further 6's; they just continue to open end. You need a custom explode routine. – aramis Sep 9 '13 at 13:36
• @aramis Arg, I forgot about that rule, thanks! – GMNoob Sep 11 '13 at 12:06

A more technical answer.... I used Python to write a simple enough code that can calculate this. For the non-coders, I am sorry... (I have put the code at the end of the answer so not to get in the way)

I used the variables I got from the chat to get these results:

>>> calculate()
most common: [(6, '8.5%'), (7, '8.26%'), (5, '8.02%'), (4, '7.89%'), (8, '7.22%'), (3, '7.05%'), (9, '6.64%'), (10, '6.41%'), (2, '5.74%'), (11, '4.87%')]
minimum: 1, chance: 4.44%
maximum: 40, chance: 0.01%
average: 8.5942, nearest: [9.0, 8.0], chance: ['6.64%', '7.22%']


Basically, this is a specific answer, but using the calculate function in this Python code will provide the answers if you give it the right variables. Good luck.

Code:

from random import randint
from collections import Counter
import math

def roll_dice(expression, explode=False):
"""
Rolls X dice of Y sides (XdY) - explode means max result is another dice
"""
amount, sides = map(int, expression.split('d'))
result = 0
roll = 0
while roll < amount:
r = randint(1, sides)
result += r
if explode and r == sides:
continue
else:
roll += 1
return result

def calculate_rounds(ws, toughness, strength, wounds):
"""
Simulates combat to calculate number of rounds
"""
n = 0
while True:
n += 1
if roll_dice('1d100') > ws:
continue
damage = roll_dice('1d6', explode=True) + strength
wounds -= max(damage - toughness, 0)
if wounds <= 0:
return n

def calculate(ws=30, toughness=3, strength=3, wounds=8, times=10000):
"""
Performs multiple combat simulations to get statistics
"""
r = [calculate_rounds(ws, toughness, strength, wounds) for _ in range(times)]
c = Counter(r)
d = {k:'%s%%'%((float(v)/len(r))*100) for k,v in c.iteritems()}
minimum = min(d)
maximum = max(d)
most_common = [(k, d[k]) for k,_ in c.most_common(10)]
average = float(sum(r))/len(r)
nearest = [math.ceil(average), math.floor(average)]
chance = [d[x] for x in nearest]
print 'most common: %s' % most_common
print 'minimum: %s, chance: %s' % (minimum, d[minimum])
print 'maximum: %s, chance: %s' % (maximum, d[maximum])
print 'average: %s, nearest: %s, chance: %s' % (average, nearest, chance)
return c # counter, dictionary of rounds:times for each number of rounds