This post is also available in: 日本語 (Japanese)

In the solo-focused dungeon-crawl TTRPG “Ker Nethalas”,

I wanted “a hit-rate list to use as a build reference,”

so I tried running a simulation in Python.

Simulation conditions

  • Based on the attacker’s and defender’s Weapon Skill or Combat values, I ran 1,000,000 simulations each.
  • The attacker’s +10 bonus is assumed to be included.
  • In the table, the attacker is A and the defender is D, and the numbers show each side’s skill value.

Simulation results

All values are percentages. Truncated after the first decimal place.

A30 A40 A50 A60 A70 A80 A90 A100
D10 29.6 39.5 49.5 59.5 69.579.5 89.5 99.5
D2028.038.148.157.968.078.1 88.1 98.1
D3025.335.6445.655.6 65.775.6 85.5 95.6
D4022.231.842.152.1 62.272.1 82.1 92.1
D5019.3527.837.247.7 57.767.777.8 87.7
D6016.323.732.3 41.7 52.262.472.2 82.2
D7013.319.827.2 35.6 45.155.865.775.8
D8010.315.722.129.3 38.147.558.368.4
D907.311.717.223.731.139.549.059.9
D1004.37.812.217.724.131.540.149.5

What you can tell from the simulation

Because individual success checks happen before the opposed check, hit rate drops noticeably—especially when the attacker’s skill is low.

Baseline hit-rate guideline (early game)

With a starting weapon skill of 60 plus the attack bonus, you’ll hit at roughly a 50–60% rate. If you raise your skill with a weapon that has the Simple trait or with Weapon Master passive skills, you get roughly +10% hit rate per +10 skill.

The tougher the enemy, the more value you get from stacking skill

Because skill increases are closer to adding to hit rate than multiplying it, the higher the enemy’s Combat (i.e., the lower your base hit rate), the bigger the relative benefit of stacking hit rate.

Against a Combat 40 enemy, raising weapon skill from 70 to 90 increases hit rate by 1.31×.

Against a Combat 70 enemy, raising weapon skill from 70 to 90 increases hit rate by 1.456×.

Baseline dodge-rate guideline (early game)

Even with Dodge 10 at the start, at level 1 you can avoid nearly half of incoming attacks. The benefit of raising it up to Dodge 40 is, at least from a damage-mitigation standpoint, not that large. (Dodge heavily affects escape success rate and trap avoidance, so those benefits are probably the bigger deal.)

Equipping a parry weapon has about the same expected damage reduction as wearing Armor across your whole body

The expected reduction rate from blocking a 1D6 attack with Armor 1 is:

[(0/6+1/6+2/6+3/6+4/6+5/6)/6]/[(1/6+2/6+3/6+4/6+5/6+6/6)/6]=0.7142[(0/6 + 1/6 + 2/6 + 3/6 + 4/6 + 5/6)/6] / [(1/6 + 2/6 + 3/6 + 4/6 + 5/6 + 6/6)/6] = 0.7142

So, to neutralize attacks from an enemy with attack skill 60 to a similar degree via dodging or parrying, you’d need to raise Dodge from 10 to Dodge 60. That’s tough to do with Dodge, but it’s achievable even on a freshly made character if you use a parry weapon.

If you have a parry weapon and run a Weapon Master + Tracher build, and pick up Hunter’s Mark at level 2, you can avoid over 60% of attacks even from a Combat 70 enemy. Converting that reduction rate into Armor, it’s:

[(0/6+0/6+1/6+2/6+3/6+4/6)/6]/[(1/6+2/6+3/6+4/6+5/6+6/6)/6]=0.428[(0/6 + 0/6 + 1/6 + 2/6 + 3/6 + 4/6)/6] / [(1/6 + 2/6 + 3/6 + 4/6 + 5/6 + 6/6)/6] = 0.428

which means it’s about the same as gearing your whole body in Armor 2.

How hard it is to secure hit rate

To maintain an 80%+ hit rate, even if the enemy’s Combat is low, you need an effective weapon skill of 90 including modifiers.

Also, even with an effective weapon skill of 100 including modifiers, against an enemy whose Combat is 80 due to Overseer, your hit rate drops below 70%.

Code used for the simulation (Python)

Attack = 70

Defense =100

Double = [11,22,33,44,55,66,77,88,99,100]

HitCount =0

for i in range(1,1000000):

AttackCritical = False

DefenseCritical = False

AttackFumble = False

DefenseFumble = False

AttackSuccess = False

DefenseSuccess = False

AttackWin =False

DefenseWin =False

Attackroll = random.randint(1,100)

if Attack >= Attackroll:

AttackSuccess = True

if AttackSuccess == True and Attackroll in Double:

AttackCritical =True

if AttackSuccess == False and Attackroll in Double:

AttackFumble = True

Defenseroll = random.randint(1,100)

if Defense >= Defenseroll:

DefenseSuccess =True

if DefenseSuccess == True and Defenseroll in Double:

DefenseCritical =True

if DefenseSuccess == False and Defenseroll in Double:

DefenseFumble = True

if AttackSuccess == True and DefenseSuccess == False:

AttackWin = True

if AttackSuccess == False and DefenseSuccess == True:

DefenseWin = True

if AttackCritical ==True and DefenseCritical == False:

AttackWin = True

DefenseWin = False

if DefenseCritical == True and AttackCritical == False:

AttackWin = False

DefenseWin = True

if AttackSuccess == True and DefenseSuccess == True:

if Attackroll > Defenseroll:

AttackWin = True

DefenseWin = False

elif Defenseroll > Attackroll:

AttackWin = False

Defensewin = True

elif Attackroll == Defenseroll:

if Attack > Defense:

AttackWin = True

DefenseWin = False

elif Defense > Attack:

AttackWin = False

DefenseWin = True

if DefenseFumble ==True and AttackFumble == False:

AttackWin = True

DefenseWin = False

if AttackFumble == True and DefenseFumble == False:

AttackWin = False

DefenseWin = True

if AttackWin == True:

HitCount +=1

HitRate =100 * HitCount /1000000

print(HitRate)

In closing

Ah… simulations are the best!!

It feels like my soul is being cleansed.

So basically, if you skip bathing but run simulations, you get the same effect as taking a bath.

Or so they say.

With that, happy gaming!

Thank you for reading to the end!

On X, I share article updates, impressions while playing, and my attempts at game development.

About 1.3x louder than the blog posts (according to our own metrics).