Tables of damage reduction from ac and ev
Posted: Friday, 9th January 2015, 05:21
I've written some code to test how much ac and ev reduce incoming damage. For instance, in the second table below, the entry for 9 ev and 19 ac for a player in fire dragon armor is 3.29. That means that incoming damage will on average be reduced by a factor of 3.29. So instead of dealing an average of 30/2 = 15 damage per turn, a death yak will deal 15 / 3.29 = 4.56 dmg per turn against such a character, in the long run average. This can help you plan builds and it shows the relative value of AC and EV.
From my limited testing in fsim, my numbers seem pretty close. They aren't spot on so I probably have an off by one error somewhere, but they seem close enough to be useful.
Caveats: use only with enemies who don't wield weapons, and who attack at normal speed.
To test my numbers for yourself if you don't believe them, what you do is first set up a character with AC and EV the same as in the table entry. Then, run fsim against the enemy and note the AvDam. Sum up the monster's attack damages (for a death yak, the sum is 30), then divide by 2, and then divide by my table entry, and the result should be within 0.5 of AvDam. If it's not let me know! Or also let me know if you see a bug in my code.
Against a death yak with GDR 0:
Against a death yak with GDR 0.34 (chain mail or fire dragon armor):
And here's my Python source code
I'll take a few requests if you want to see some specific GDR and enemy, or some specific AC/EV range.
From my limited testing in fsim, my numbers seem pretty close. They aren't spot on so I probably have an off by one error somewhere, but they seem close enough to be useful.
Caveats: use only with enemies who don't wield weapons, and who attack at normal speed.
To test my numbers for yourself if you don't believe them, what you do is first set up a character with AC and EV the same as in the table entry. Then, run fsim against the enemy and note the AvDam. Sum up the monster's attack damages (for a death yak, the sum is 30), then divide by 2, and then divide by my table entry, and the result should be within 0.5 of AvDam. If it's not let me know! Or also let me know if you see a bug in my code.
Against a death yak with GDR 0:
- Code:
>>> crawl.ev_ac_table(1,35,2,1,40,2,0,crawl.deathyak)
ac\ev 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39
1 1.07 1.12 1.18 1.25 1.33 1.41 1.51 1.62 1.75 1.91 2.09 2.31 2.56 2.84 3.15 3.49 3.86 4.26 4.69 5.14
3 1.14 1.20 1.26 1.34 1.42 1.51 1.61 1.74 1.88 2.04 2.24 2.47 2.73 3.03 3.37 3.73 4.13 4.56 5.01 5.49
5 1.22 1.28 1.35 1.43 1.52 1.62 1.73 1.86 2.01 2.18 2.39 2.64 2.93 3.25 3.60 3.99 4.42 4.88 5.37 5.88
7 1.31 1.37 1.45 1.53 1.63 1.73 1.85 1.99 2.15 2.34 2.56 2.83 3.14 3.48 3.86 4.28 4.74 5.23 5.75 6.30
9 1.40 1.48 1.56 1.64 1.75 1.86 1.99 2.14 2.31 2.51 2.75 3.04 3.37 3.73 4.14 4.59 5.08 5.61 6.17 6.76
11 1.51 1.59 1.67 1.77 1.87 2.00 2.14 2.29 2.48 2.70 2.96 3.26 3.62 4.01 4.45 4.93 5.46 6.03 6.63 7.27
13 1.62 1.70 1.80 1.90 2.02 2.15 2.30 2.47 2.67 2.90 3.18 3.51 3.89 4.31 4.79 5.31 5.87 6.48 7.13 7.81
15 1.74 1.83 1.93 2.05 2.17 2.31 2.47 2.66 2.87 3.12 3.42 3.78 4.18 4.64 5.15 5.71 6.32 6.98 7.68 8.41
17 1.88 1.98 2.08 2.20 2.34 2.49 2.66 2.86 3.09 3.36 3.69 4.07 4.51 5.00 5.55 6.15 6.81 7.51 8.27 9.06
19 2.02 2.13 2.24 2.37 2.52 2.68 2.87 3.08 3.33 3.62 3.97 4.38 4.86 5.39 5.98 6.63 7.33 8.09 8.91 9.76
21 2.18 2.29 2.42 2.56 2.71 2.89 3.09 3.32 3.59 3.90 4.28 4.72 5.23 5.80 6.44 7.14 7.90 8.72 9.59 10.51
23 2.35 2.47 2.60 2.75 2.92 3.11 3.33 3.57 3.86 4.20 4.61 5.08 5.63 6.25 6.93 7.69 8.51 9.39 10.33 11.32
25 2.53 2.66 2.80 2.96 3.14 3.35 3.58 3.84 4.15 4.52 4.95 5.47 6.06 6.72 7.46 8.27 9.15 10.10 11.11 12.17
27 2.71 2.85 3.01 3.18 3.37 3.59 3.84 4.13 4.46 4.85 5.32 5.87 6.50 7.21 8.00 8.87 9.82 10.84 11.92 13.07
29 2.90 3.05 3.22 3.40 3.61 3.84 4.11 4.42 4.77 5.19 5.69 6.28 6.96 7.72 8.57 9.50 10.51 11.60 12.77 13.99
31 3.10 3.26 3.43 3.63 3.85 4.10 4.39 4.71 5.09 5.54 6.07 6.70 7.42 8.24 9.14 10.13 11.21 12.38 13.62 14.92
33 3.29 3.46 3.65 3.86 4.09 4.36 4.66 5.01 5.41 5.89 6.45 7.12 7.89 8.75 9.71 10.77 11.91 13.15 14.47 15.86
35 3.48 3.66 3.86 4.08 4.33 4.61 4.93 5.30 5.73 6.23 6.83 7.54 8.35 9.27 10.28 11.40 12.62 13.92 15.32 16.79
Against a death yak with GDR 0.34 (chain mail or fire dragon armor):
- Code:
>>> crawl.ev_ac_table(1,35,2,1,40,2,0.34,crawl.deathyak)
ac\ev 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39
1 1.07 1.12 1.18 1.25 1.33 1.41 1.51 1.62 1.75 1.91 2.09 2.31 2.56 2.84 3.15 3.49 3.86 4.26 4.69 5.14
3 1.16 1.22 1.29 1.36 1.44 1.54 1.64 1.77 1.91 2.08 2.28 2.51 2.78 3.09 3.43 3.80 4.20 4.64 5.10 5.59
5 1.27 1.33 1.40 1.49 1.58 1.68 1.80 1.93 2.08 2.27 2.49 2.74 3.04 3.37 3.74 4.15 4.59 5.07 5.57 6.11
7 1.39 1.46 1.54 1.63 1.73 1.84 1.97 2.11 2.28 2.49 2.72 3.01 3.33 3.70 4.10 4.55 5.03 5.55 6.11 6.69
9 1.53 1.61 1.69 1.79 1.90 2.02 2.16 2.33 2.51 2.73 3.00 3.31 3.67 4.07 4.51 5.00 5.54 6.11 6.72 7.37
11 1.69 1.78 1.87 1.98 2.10 2.24 2.39 2.57 2.78 3.02 3.31 3.66 4.05 4.49 4.99 5.53 6.12 6.75 7.43 8.14
13 1.87 1.97 2.08 2.20 2.33 2.48 2.66 2.85 3.08 3.35 3.68 4.06 4.50 4.99 5.53 6.14 6.79 7.49 8.24 9.04
15 2.09 2.20 2.32 2.45 2.60 2.77 2.96 3.18 3.44 3.74 4.10 4.53 5.01 5.56 6.17 6.84 7.57 8.36 9.20 10.08
17 2.34 2.47 2.60 2.75 2.92 3.11 3.32 3.57 3.86 4.20 4.60 5.08 5.62 6.24 6.92 7.67 8.49 9.37 10.31 11.30
19 2.64 2.78 2.93 3.10 3.29 3.50 3.75 4.02 4.35 4.73 5.19 5.72 6.34 7.04 7.81 8.65 9.58 10.57 11.63 12.74
21 3.00 3.15 3.32 3.52 3.73 3.97 4.25 4.57 4.93 5.37 5.88 6.49 7.19 7.98 8.86 9.82 10.86 11.99 13.19 14.46
23 3.21 3.38 3.56 3.77 4.00 4.26 4.55 4.89 5.29 5.75 6.30 6.96 7.71 8.55 9.49 10.52 11.64 12.85 14.13 15.49
25 3.45 3.62 3.82 4.04 4.29 4.57 4.88 5.25 5.67 6.17 6.76 7.46 8.27 9.17 10.18 11.28 12.48 13.78 15.16 16.61
27 3.70 3.89 4.10 4.33 4.60 4.89 5.23 5.63 6.08 6.61 7.25 8.00 8.86 9.83 10.91 12.10 13.38 14.77 16.25 17.81
29 3.95 4.16 4.38 4.64 4.92 5.24 5.60 6.02 6.51 7.08 7.76 8.56 9.49 10.52 11.68 12.95 14.32 15.81 17.40 19.06
31 4.22 4.44 4.68 4.95 5.25 5.59 5.98 6.42 6.94 7.55 8.27 9.13 10.12 11.23 12.46 13.81 15.28 16.87 18.56 20.33
33 4.48 4.71 4.97 5.25 5.58 5.94 6.35 6.82 7.37 8.02 8.79 9.70 10.75 11.93 13.23 14.67 16.23 17.92 19.72 21.61
35 4.75 4.99 5.26 5.56 5.90 6.29 6.72 7.22 7.81 8.49 9.31 10.27 11.38 12.63 14.01 15.53 17.19 18.97 20.87 22.88
And here's my Python source code
- Code:
# calculates the average factor by which incoming dmg is reduced by AC
def ACReduction(ac, dam, gdr = 0):
totreduction = 0
gdr_amt = int(gdr * dam)
if gdr_amt > ac / 2:
gdr_amt = ac / 2
totdam = 0
for roll1 in xrange(dam + 1): # damage roll
for roll2 in xrange(ac + 1): # ac roll
if roll2 < gdr_amt:
roll2 = gdr_amt
if roll2 > roll1:
roll2 = roll1
totreduction += roll2
totdam += roll1
if totreduction == totdam:
return float("inf") # 100% damage reduction, infinite reduction factor
return float(totdam) / (totdam - totreduction)
# calculates the average factor by which incoming dmg is reduced by EV
# provide _either_ hitdice and fighter, _or_ tohit, but not both
def EVReduction(ev, hitdice = 0, fighter = False, tohit = None):
if tohit == None:
tohit = 18
if fighter:
tohit += int(25 * hitdice / 10)
else:
tohit += int(15 * hitdice / 10)
totreduction = 0
totdam = 0
for roll1 in xrange(tohit + 1):
for roll2 in xrange(2 * ev):
for roll3 in xrange(2 * ev):
if roll1 < (roll2 + roll3) / 2:
totreduction += 1 # 1 damage dodged
totdam += 1
# normalize to 1.0 totdam and acct for the fact the preceding calculation only applied to 95% of cases
totreduction = 0.95 * (float(totreduction) / totdam)
# now account for 2.5% automatic hits and 2.5% automatic misses
return 1.00 / (1.00 - (totreduction + 0.025))
# monsters
# dam, hitdice, fighter
hydra = [18, 13, False]
deeptroll = [[27, 20, 20], 10, False]
direelephant = [[40, 15], 15, False]
deathyak = [30, 14, False]
import sys
def ev_ac_table(fromAC, toAC, stepAC, fromEV, toEV, stepEV, gdr, monster, ofile = None):
dam, hitdice, fighter = monster
colwidth = 6
sys.stdout.write("ac\ev".ljust(colwidth))
for ev in xrange(fromEV, toEV+1, stepEV):
sys.stdout.write(str(ev).ljust(colwidth))
sys.stdout.write("\n")
for ac in xrange(fromAC, toAC+1, stepAC):
sys.stdout.write(str(ac).ljust(colwidth))
for ev in xrange(fromEV, toEV+1, stepEV):
acreduc = 0
if ac == 0:
pass
elif type(dam) == list:
treduc = 0
for d in dam:
treduc += d - (float(d) / ACReduction(ac, d, gdr))
acreduc = sum(dam) / (sum(dam) - treduc)
else:
acreduc = ACReduction(ac, dam, gdr)
# in case of multiple damages
sys.stdout.write(("%.2f" % (acreduc * EVReduction(ev, hitdice, fighter))).ljust(colwidth))
sys.stdout.write("\n")
sys.stdout.write("\n")
I'll take a few requests if you want to see some specific GDR and enemy, or some specific AC/EV range.