8
\$\begingroup\$

If I have a given set of character stats from rolling with "4d6, drop lowest", how can I calculate the probability of getting that specific result, or better values for each of them?

For example, one of the players in my campaign got 15, 15, 15, 14, 13, 13. In particular, any one of the values is lower than the highest expected value from rolling 4d6, drop lowest (16), but the average is very high. (The order of rolling does not matter, you can sort them highest to lowest or assign them to stats freely afterwards).

How likely is it to get that array or better (in the sense for each die separately, to get a result as high, or higher)?

I'd like to know this die by die, because typically, it is better to have some high dice and some low dice than a higher average of middling dice or just a higher total of points overall. (For example, the player here plays a wizard, and while high Dex and Con etc. are all nice, he really would have liked a 16 so he could get to 18 Int out of the gate).

How would a formula or anydice program look like, where I can just plug in the rolled numbers, and find out how likely it was to get them? (Anydice is my preferred tool, but other tools are OK too).

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Does the order matter? Note that 16 is not the value with the highest probability. \$\endgroup\$
    – Eddymage
    Commented Nov 9, 2023 at 6:49
  • 4
    \$\begingroup\$ The odds of getting that roll "or better" may need some clarification. Better how? Would trading a 15 and a 13 for two 14s be better? How about trading them for a 16 and a 12? (I'd generally prefer the latter, but some players might not.) There's probably a way to get the odds of the exact roll, but the "better" rolls need to be better defined before anyone can compute their odds. \$\endgroup\$
    – Blckknght
    Commented Nov 9, 2023 at 6:51
  • \$\begingroup\$ Order of rolling does not matter, you can sort them highest to lowest afterwards. Better in the sense this result or higher in each case, so 15 or higher for the hightest die, 14 or higher for the 4th highest die in the example. Let me know if there is a better way to think about "how likely is it to get a result as good or better ". Because of the issues @Blckknght points out, and because often having a few very high and a few very low dice is preferable to a higher total from or average of middling dice I'd go with better for each specific die. \$\endgroup\$ Commented Nov 9, 2023 at 6:53
  • \$\begingroup\$ I believe that you have to clarify the question: title and body ask two different things. \$\endgroup\$
    – Eddymage
    Commented Nov 9, 2023 at 7:08
  • 1
    \$\begingroup\$ @Eddymage Is that better? The title neccesarily must be a bit shorter than the full explanation \$\endgroup\$ Commented Nov 9, 2023 at 7:10

2 Answers 2

12
\$\begingroup\$

AnyDice can calculate this pretty easily. All we need to do is write a custom sequence comparison function, since AnyDice compares sequences lexicographically* by default. Here's the code:

function: are all elements in A:s at least as good as in B:s {
  loop P over {1..#B} {
    if P@A < P@B { result: 0 }
  }
  result: 1
}

TARGET: [sort {15, 15, 15, 14, 13, 13}]
STAT_ROLL: [highest 3 of 4d6]

output [are all elements in (#TARGET)d(STAT_ROLL) at least as good as in TARGET]
  named "1 = rolled at least as well as target"

FWIW, the chance of rolling each stat at least as well as in your example target array is apparently about 0.76%.


Ps. Note that this comparison function may or may not be a good definition of rolling "better than" a target stat array. In particular, note that it considers e.g. (18, 18, 18, 18, 18, 12) not to be "as good as" (15, 15, 15, 14, 13, 13), since the last stat is lower! In practice, if given a choice, you'd probably still rather take the former array than the latter.

Also note that, according to this comparison method, neither of the two arrays above is "at least as good as" the other, since both contain a stat that's lower than the corresponding stat in the other array. In fact, this is quite common, so you shouldn't be surprised that the chance of rolling better than even quite average arrays on all stats can be quite low. For example, the probability of rolling at least 12 on every stat is only about 5.49%!

For an alternative comparison method, you might want to consider comparing stat arrays by their point buy cost instead. Luckily I already have some AnyDice code for that (see the linked answer), so I could quickly check that e.g. the D&D 5e point buy cost of your target array is 44 points, whereas a standard 6 stat array rolled with 4d6 drop lowest has a 13.62% chance of being worth at least that many points.


*) This fancy term means that it first compares the first elements of the sequences, and only those are equal, moves on to compare the second elements, and so on. This is exactly the same way that we sort words in alphabetical order by comparing their letters one at a time.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for an - as usual - wonderful answer, especially for pointing out that using aggregate point buy values may be a more useful approach to defining a good array. \$\endgroup\$ Commented Nov 10, 2023 at 7:44
6
\$\begingroup\$

A similar question was previously asked on mathematics. You can use the same script, just with a fixed set of scores for one side.

from icepool import d, MultisetEvaluator, Order

single_ability = d(6).highest(4, 3)

class StrictlyBetter(MultisetEvaluator):
    def next_state(self, state, outcome, a, b):
        advantage, a_had_one_up, b_had_one_up = state or (0, False, False)
        advantage += a - b
        if advantage > 0:
            a_had_one_up = True
        if advantage < 0:
            b_had_one_up = True
        return advantage, a_had_one_up, b_had_one_up
        
    def final_outcome(self, final_state):
        _, a_had_one_up, b_had_one_up = final_state
        if a_had_one_up and not b_had_one_up:
            return 'a strictly better'
        elif b_had_one_up and not a_had_one_up:
            return 'b strictly better'
        elif not (a_had_one_up or b_had_one_up):
            return 'exactly the same'
        else:
            return 'mixed result'
    
    def order(self):
        return Order.Descending
    
evaluator = StrictlyBetter()
output(evaluator.evaluate([15, 15, 15, 14, 13, 13], single_ability.pool(6)),
       'fixed vs. variable')
output(evaluator.evaluate(single_ability.pool(6), single_ability.pool(6)),
       'variable vs. variable')

Result:

fixed vs. variable

Die with denominator 4738381338321616896

Outcome Quantity Probability
a strictly better 2034207168637998592 42.930423%
b strictly better 35336500568217600 0.745750%
exactly the same 638472231782400 0.013474%
mixed result 2668199196883618304 56.310352%

variable vs. variable

Die with denominator 22452257707354557240087211123792674816

Outcome Quantity Probability
a strictly better 4696617436843743365666704597889926627 20.918241%
b strictly better 4696617436843743365666704597889926627 20.918241%
exactly the same 5069150678806043464398031458429484 0.022577%
mixed result 13053953682988264465289403896554392078 58.140940%

Given two random players, there's more than a 40% chance that one will roll strictly better than the other!

You can run this in your browser here.

This uses my Icepool Python probability package. The MultisetEvaluator uses dynamic programming to efficiently compute solutions to dice pool problems; next_state sees how many 18s were rolled by each side, then how many 17s, then 16s, and so forth. If you'd like to know more about the algorithm, you can read my paper here.

You may also be interested in my ability score rolling method calculator, which will graph sorted ability scores and point buy equivalents for a variety of configurable methods.

Related questions

Probability for completely unfair stats. That question investigates the more extreme case of comparing the lowest high score in a group to the highest low score in the group. In other words, where this question asks whether the lucky player can pair up their scores with the unlucky player so that they meet or exceed the unlucky player in all six, that question asks whether the lucky player does so regardless of assignment.

The "snake draft". Personally I dislike the pick order effects this introduces.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thank you so much. I +1ed, and again picked HDR's answer to accept, because a formulation in AnyDice saves me from having to learn yet another system for playing around with the results. Thank you for providing an easy to access way to try out Icepool however, which if I understand it right, it has superior implementation properties compared to AnyDice. Your ability score calculator is also pretty nifty and easy to use. I've downloaded your paper to learn a bit more about this. \$\endgroup\$ Commented Nov 10, 2023 at 7:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .