D&D 5E GWF vs TWF: The Math

Kobold Stew

Last Guy in the Airlock
Supporter
I would have liked to see more flexibility generally in Rogue design.

For example, if the sneak attack rules referred to ranged attacks (rather than ranged weapons), then a high elf rogue could have used a cantrip. That could have allowed an entirely Int-based rogue. As it is, Dex and Str rogues are the only ones supported, and some people are even doubting whether the Str build is able to use sneak attack (with a finesse weapon). [there will be a charisma/dex build in the PHB at least; beyond that, though I don't know.]
 

log in or register to remove this ad

Stalker0

Legend
Agree that it's a good design decision to open up more rogue alternatives. I just wish they'd defined sneak attack to work with all rogue weapons -- it would encourage more thuggish rogues to go the longsword route.

One thing to factor in. The rogue will not always get to use sneak attack, and 5e makes switching weapons decently easy. Further, with no assumption of magic weapons, a rogue could pull out a longsword for a straight up fight, and then switch to do short swords for a quick sneak attack...and both would be reasonable choices.
 

sidonunspa

First Post
Agree that it's a good design decision to open up more rogue alternatives. I just wish they'd defined sneak attack to work with all rogue weapons -- it would encourage more thuggish rogues to go the longsword route. As it stands, it would be foolish for a rogue to use a longsword, since you can't sneak attack with it, nor can you dual wield a second weapon that allows sneak attack.

Sounds like a feat to me
 


GrumpyGamer

First Post
I was fiddling around and wrote a monte carlo sim for all 4 fighter fighting styles in Python.

It takes the arguments:
nrolls - number of times to run the sim. For example 100000 would be the same as sitting down and recording the results of a 100000 attack rounds.
apr - this is the number of attacks the fighter has in a round (for twf it adds in the extra attack)
cn - critical hit number. For the starter set martial archetype this is 20 at 1st level, 19 at 3rd level, 18, at 15th level.
pn - proficiency bonus
an - ability bonus
ac - target AC you are attacking
ndice - number of dice your weapon has
dicetype - dice type your weapon uses

It returns:
h - number of hits
t - total damage
a - average damage per round

Code:
import random


def dicesim_gwf(nrolls, apr, cn, pn, an, ac, ndice, dicetype):
    t = 0
    h = 0
    for i in range(nrolls):        # repeat N experiments
      for j in range(apr):  
        att = 0
        matt = 0
        att = random.randint(1,20)
        matt = att+pn+an
        if matt >= ac:
          h = h+1
          t = t+an    
          if att >= cn:
            for k in range(ndice): #critroll 1
              r = random.randint(1, dicetype)  # roll die dicetype
              if r <= 2: # reroll 1 or 2
                 r = random.randint(1, dicetype)
              t = t+r
            for l in range(ndice): #critroll 2
              r = random.randint(1, dicetype)  # roll die dicetype
              if r <= 2: # reroll 1 or 2
                 r = random.randint(1, dicetype)
              t = t+r
          else:
            for m in range(ndice):
              r = random.randint(1, dicetype)  # roll die dicetype
              if r <= 2: # reroll 1 or 2
                 r = random.randint(1, dicetype)
              t = t+r


    a = float(t)/nrolls  # average dpr
    return h,t,a


def dicesim_twf(nrolls, apr, cn, pn, an, ac, ndice, dicetype):
    t = 0
    h = 0
    for i in range(nrolls):        # repeat N experiments
      for j in range(apr+1):
        att = 0
        matt = 0
        att = random.randint(1,20)
        matt = att+pn+an
        if matt >= ac:
          h = h+1
          t = t+an     
          if att >= cn:
            for k in range(ndice): #critroll 1
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r
            for l in range(ndice): #critroll 2
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r 
          else:
            for m in range(ndice):
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r


    a = float(t)/nrolls  # average dpr
    return h,t,a
    
def dicesim_arch(nrolls, apr, cn, pn, an, ac, ndice, dicetype):
    t = 0
    h = 0
    for i in range(nrolls):        # repeat N experiments
      for j in range(apr):  
        att = 0
        matt = 0
        att = random.randint(1,20)
        matt = att+pn+an+2
        if matt >= ac:
          h = h+1
          t = t+an     
          if att >= cn:
            for k in range(ndice): #critroll 1
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r
            for l in range(ndice): #critroll 2
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r 
          else:
            for m in range(ndice):
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r


    a = float(t)/nrolls  # average dpr
    return h,t,a
    
def dicesim_duel(nrolls, apr, cn, pn, an, ac, ndice, dicetype):
    t = 0
    h = 0
    for i in range(nrolls):        # repeat N experiments
      for j in range(apr):  
        att = 0
        matt = 0
        att = random.randint(1,20)
        matt = att+pn+an
        if matt >= ac:
          h = h+1
          t = t+an+2
          if att >= cn:
            for k in range(ndice): #critroll 1
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r
            for l in range(ndice): #critroll 2
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r 
          else:
            for m in range(ndice):
              r = random.randint(1, dicetype)  # roll die dicetype
              t = t+r


    a = float(t)/nrolls  # average dpr
    return h,t,a

Output 1st level:
>>> dicesim_duel(100000,1,20,2,3,14,1,8)
(59802, 590842, 5.90842)
>>> dicesim_arch(100000,1,20,2,3,14,1,10)
(69995, 622456, 6.22456)
>>> dicesim_twf(100000,1,20,2,3,14,1,6)
(119667, 813139, 8.13139)
>>> dicesim_gwf(100000,1,20,2,3,14,2,6)
(59745, 718465, 7.18465)

Output 3rd Level:
>>> dicesim_duel(100000,1,19,2,3,14,1,8)
(60419, 619405, 6.19405)
>>> dicesim_arch(100000,1,19,2,3,14,1,10)
(69815, 646214, 6.46214)
>>> dicesim_twf(100000,1,19,2,3,14,1,6)
(119790, 849033, 8.49033)
>>> dicesim_gwf(100000,1,19,2,3,14,2,6)
(59976, 762115, 7.62115)

Output 5th Level:
>>> dicesim_duel(100000,2,19,3,4,16,1,8)
(120056, 1352018, 13.52018)
>>> dicesim_arch(100000,2,19,3,4,16,1,8)
(140024, 1280541, 12.80541)
>>> dicesim_twf(100000,2,19,3,4,16,1,6)
(180287, 1457079, 14.57079)
>>> dicesim_gwf(100000,2,19,3,4,16,2,6)
(120257, 1651104, 16.51104)

Output 11th Level:
>>> dicesim_duel(100000,3,19,4,5,18,1,8)
(180125, 2206257, 22.06257)
>>> dicesim_arch(100000,3,19,4,5,18,1,8)
(210257, 2133683, 21.33683)
>>> dicesim_twf(100000,3,19,4,5,18,1,6)
(239982, 2179882, 21.79882)
>>> dicesim_gwf(100000,3,19,4,5,18,2,6)
(179996, 2651385, 26.51385)

I would say that the 11th level numbers are the fuzziest as you may very well be fighting mobs with less than 18 AC and you may have a magical item and/or a feat which will skew these numbers.

Python is free and easy to install if you want to play around with your own inputs or change the code.

TLDR: The Op is correct Fighter 1-4 does the most damage with TWF until 5th level where the fighter does the most damage with GWF. Outstanding wildcards that could change this recommendation are feats and magical weapons.
 
Last edited:

Rathrain

First Post
I was fiddling around and wrote a monte carlo sim for all 4 fighter fighting styles in Python.

<snip>

Python is free and easy to install if you want to play around with your own inputs or change the code.

TLDR: The Op is correct Fighter 1-4 does the most damage with TWF until 5th level where the fighter does the most damage with GWF. Outstanding wildcards that could change this recommendation are feats and magical weapons.

Thanks Grumpy - I actually downloaded and installed Python based on your recommendation and to run your script. Fantastic stuff. A couple of questions...

It's not clear to me that the code registers a hit whenever a 20 is rolled (regardless of ac). Does it?

I used your code to simulate rogue damage and (assuming my code mods are correct - which is a stretch), rogues look to out-damage fighters through level 11. Did you discover the same?
 

GrumpyGamer

First Post
It does not model a 20 as an always hit or a 1 as an always miss. You could add an if/elif/else to check for these cases. If you are interested in creating a sim for a halfling you would need to do this anyway to model lucky.

I am not sure when the fighters extra attacks catch up with a rogue, but at first level the rogue should do more damage as a d6 is worth more than a starting ability modifier of 3. Also you get it evenif you miss with the second attack.

I am typing this on a mobile device so excuse the typos/brevity.
 

ZombieRoboNinja

First Post
I think it's fair to assume that these numbers will change drastically with different subclasses and especially feats factored in, so while these numbers are a good starting point, they may well not reflect the situation once the PHB is out. (For example, if I recall the great weapon feat gave a huge DPR boost in the open play test.)
 

Remove ads

Top