4
\$\begingroup\$

How do you calculate the probability of success when success is two different criteria/probabilities?

I'm theorizing a resolution system for a homebrewed mechanic that involves rolling a pool of 6d10's and triggering abilities based on some criteria of results (all in 2-die pairs). The remaining pool of value still matters as it's allocated for other things but for the probability calculation the dice may be any result. All dice are rolled at once and order does not matter.

A player rolls 6d10 and attempts to use single die or a combination of two dice to derive a final value. If using two dice, combinations may be made based on the face values of the pair to trigger increases in the sum. Conditions to increase the value are defined as abilities and have variable success criteria.

How do I calculate the probabilities of different abilities defined as 2 dice in 6d10 with disparate success conditions like:

  • 9 (1/10); any odd number lower than 9 (4/10)

  • any even number X (5/10); X/2 (1/10)

  • 1 or 2 (2/10); 10[if 1] 9[if 2] (1/10)

  • etc.

I had originally assumed that being only pair combinations would yield an equal probability for every discrete option and that the total probability would be the combination of all possible pairs that trigger success. While I've done some reading into how to do this, I haven't fully learned the math as I still have doubts whether this is fundamentally accurate, or how to combine each discrete probability correctly to generate the final result.

I'm also not sure how to test my findings without an incredibly large sample size.

If all else fails I suppose I could produce a table of all \$10^6\$ combinations and find some way to query the rows for matching criteria. (As a dyslexic artist I tend to shy away from programming, and I haven't found any function in Excel to do what I need without getting into SQL).

Using the first example (9 and any odd<9) would the probability be calculated as such?

$$ \left[ \frac 1 {10} \times \frac 4 {10} \times \frac {10} {10} \times \frac {10} {10} \times \frac {10} {10} \times \frac {10} {10} \right] \times 15=0.6 $$

15 being the result of 6choose2

I'm a frequent user of Anydice for which I've looked at the distribution of the highest 2 dice of 6d10. Looking at the result for a perfect 20- 2×10's in 6d10, I had assumed this would be the probability for any pair of 1/10 rolls. When I calculate it this way I get 15% instead of AnyDice's 11.43%.

What am I doing incorrectly?

The the resolution system I have in mind is:

  1. Player rolls 6d10 producing their "Hand"

  2. Player allocates "Bins" of value with 1 or 2 dice (meaning a player can produce 1-6 Bins, as they may choose not to use available die) from their available Hand

    • If the Player chooses to allocate dice in pairs defined by their abilities, they increase the value of the Bin by the ability's effect.
  3. Bins are resolved based on their final value whether affected by an ability or the natural face value of the die. (Players seek the highest possible final Bin value)

Ultimately I am trying to determine how to balance possible die combinations to build abilities.

\$\endgroup\$
  • 1
    \$\begingroup\$ "for my continuing education, how does one generate problems in mathematical notation and produce them in this text field?" That formatting can be done in LaTeX via MathJax. You can find more information on it and how to do it in this meta \$\endgroup\$ – Rubiksmoose Mar 27 at 16:23
  • 1
    \$\begingroup\$ specifically this tutorial linked from there is probably what you want. :) Also welcome to the site! I hope you stick around and keep contributing. \$\endgroup\$ – Rubiksmoose Mar 27 at 16:28
7
\$\begingroup\$

No, you don't need the binomial distribution

Just the complement rule and the chain rule.

Your system will be slow ...

Putting aside the issue of probability, it is going to take a longer time than most dice mechanics to go from what is showing on the table to a "pass" or "fail".

Quick! You rolled 3, 10, 7, 4, 4, 6 - which of your rules did you pass with?

... and non-intuitive

You, the game designer, have given us three targets to which you can't calculate the probability because you don't have sufficient math skills. I can do it but it will take me at least 15-20 minutes or I can get a computer to brute force the answer.

How do you expect players of the game to look at the rule and know if they have a decent chance of succeeding or not in the 30 seconds it takes them to consider what they want their character to do?

How to calculate the probabilities

There is a straightforward simplification you can use here - there are 15 ways of making pairs from 6 dice - \${6 \choose 2}=15\$.

You need to (1) work out the chance of it happening in a single pair, (2) get the chance of it not happening from the complement rule, (3) use the multiplication rule to work out the chance of it not happening in 15 pairs, and (4) use the complement rule again.

For example, for a 9 \$({1 \over 10})\$ and any odd number lower than 9 \$({4 \over 10})\$ has a \${4 \over 100} \times 2 = {8 \over 100}\$ chance in a pair of dice because order doesn't matter. So it has a \$1- {8 \over 100}={92 \over 100}\$ of not happening. Over 15 pairs it has a \$\left({92 \over 10}\right)^{15}\approx0.2863\$ of not happening or a \$0.7137\$ of happening.

The same technique gives \$0.7941\$ and \$0.4579\$ for the next two - all that changes is the original probability of the pair.

In the end, for all probabilities on 2 dice from 0 to 1 (in increments of 0.01) your pool probability is:

enter image description here

\$\endgroup\$
  • 1
    \$\begingroup\$ Thank you! The Chain rule is what I was unaware of! I have never studied statistics/probability in a formal setting and googling around I never found it. This helps immensly! The only thing I'm unsure of is what the X axis of the graph is indicating regarding the probability curve. \$\endgroup\$ – l3rokenwing Mar 28 at 15:24
2
\$\begingroup\$

The probabilities for your success criteria are quite complex

Some of your criteria, such as "9 on one die, a different odd number on the other die", are simple enough, since you can compute the probabilities for the two dice independently. Others, such as "even number X on one die, X/2 on the other die", are more complex, since the probability of getting X/2 on the second die depends on the value of the first (i.e. the probability is 1/10 if the first die is even and 0 if the first die is odd).

However, even for the simple conditions, you have the additional problem that the player has a pool of 6 dice from which they can choose any 2 to satisfy the condition. If the player's final score for their hand depends on multiple bins (e.g. sum of all bin scores), then there is now an additional interdependency between bins, since allocating a die to a bin excludes if from being allocated to any other bin, and this even further complicates the probability calculations.

Ultimately, the most expedient solution may be to write a program as you suggested: enumerate all 1 million possible rolls of 6d10 and evaluate each roll against your scoring criteria. (Technically some rolls will be equivalent since order doesn't matter, but you don't need to worry about that.) This saves you from needing to work out the precise probability theory behind each scoring criterion. Or, to put it another way, writing the code to evaluate whether a given hand satisfies each given criterion is probably easier than manually working out the probabilities for each criterion.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your answer! I assume that the complexity of trying to assign probability to the entire allocation of the 6d10 hand is astronomical. Ultimately I'm not worried about the final effect of the entire Hand, just producing the probability of an Ability's trigger conditions. Even still, learning some programming may be the way to go. Thanks again! \$\endgroup\$ – l3rokenwing Mar 27 at 18:42
  • \$\begingroup\$ @l3rokenwing Actually, the total possible outcomes for 6d10 is only 1 Million discrete possible outcomes. High to count by hand, but trivial for a computer to count. I wrote a program that could do it pretty quickly. \$\endgroup\$ – Xirema Mar 27 at 19:26
  • \$\begingroup\$ @Xirema, I would be very interested in such a program :D Also I use astronomical as hyperbole mostly, but I do think it gets pretty crazy once you're trying to assess the one million combinations for more specific data analysis. Admittedly it would be awesome to be able to generate probabilities for the ability triggers and still be able to look at what the remaining values are after the principle 2-die Bin allocation. I think the ability building gets fun as you consider the equal probabilities of getting any two numbers, but the effect those numbers have on the now-weighted average 6d10 \$\endgroup\$ – l3rokenwing Mar 27 at 19:33
  • \$\begingroup\$ @Xirema Yeah, most of the coding difficulty will be in writing the code to evaluate each success criterion. \$\endgroup\$ – Ryan Thompson Mar 27 at 19:53
2
\$\begingroup\$

I wrote a [starter] program in C++ that can handle the task of finding all the possible outcomes for a 6d10 roll.

#include<map>
#include<vector>
#include<array>
#include<iostream>
#include<iomanip>
#include<algorithm>

using arr_t = std::array<int, 6>;

std::map<arr_t, int64_t> get_rolls() {
    std::map<arr_t, int64_t> map;
    for(int i = 1; i <= 10; i++) {
        //We intitalize with each possible value of a single d10. The remaining values
        //Are set to 11 to denote rolls that haven't happened yet. We use 11 so it won't
        //interfere with the array sorting we do later
        arr_t arr = {i, 11, 11, 11, 11, 11};
        map[arr]++;
    }
    //At this point, map looks something like this:
    //{1,11,11,11,11,11}: 1
    //{2,11,11,11,11,11}: 1
    //{3,11,11,11,11,11}: 1
    //{4,11,11,11,11,11}: 1
    //{5,11,11,11,11,11}: 1
    //{6,11,11,11,11,11}: 1
    //{7,11,11,11,11,11}: 1
    //{8,11,11,11,11,11}: 1
    //{9,11,11,11,11,11}: 1
    //{10,11,11,11,11,11}: 1

    for(int i = 1; i < 6; i++) {
        //Initializing the for-loop with 1 so that we understand we need 6 values,
        //BUT we've already initialized the first value for each roll, so each roll
        //only has 5 more to evaluate.
        std::map<arr_t, int64_t> temp;
        for(auto const& [arr, trials] : map) {
            //For each set of rolls and their associated trials in the map...
            for(int value = 1; value <= 10; value++) {
                //... we add the next roll to the set
                auto arr_copy = arr;
                arr_copy[i] = value;
                std::sort(arr_copy.begin(), arr_copy.end());
                temp[arr_copy] += trials;
            }
        }
        map = temp;
    }

    //At this point, the map looks something like this:
    //{1,1,1,1,1,1}: 1
    //{1,1,1,1,1,2}: 6
    //{1,1,1,1,1,3}: 6
    //...
    //{1,1,1,1,2,2}: 30
    //...
    //{9,10,10,10,10,10}: 6
    //{10,10,10,10,10,10}: 1

    return map;
}

int get_largest_even(arr_t const& arr) {
    int ret = -1;
    for(int i : arr) {
        //The modulo operator % returns 0 if the number is evenly divisible by the number 
        //being compared against, and some other number if it is not.
        if(i % 2 == 0) {
            //So if it's even (evenly divisible by 2)
            if(auto it = std::find(arr.begin(), arr.end(), i / 2); it != arr.end())
                //This is some C++ magic to find the index of a value in the array that is
                //the exact value divided by 2, and then validate whether we actually found one or not.
                ret = i;
        }
        //This checks up through a sorted array, so if we find a larger value later, it'll
        //override the previous found value
    }
    return ret;
}

auto filter_rolls(std::map<arr_t, int64_t> const& map) {
    std::map<int, int64_t> new_map;
    for(auto const& [arr, trials] : map) {
        if(int largest_even = get_largest_even(arr); largest_even != -1) {
            //We only keep the roll if it contained a valid pair matching the
            //"Any Even X; X/2" condition.
            //We also only store the largest even value, since we no longer care what the
            //other die rolls were
            new_map[largest_even] += trials;
        }
    }
    return new_map;
}

int main() {
    auto rolls = get_rolls();
    auto filtered_rolls = filter_rolls(rolls);
    int64_t total_trials = 0;
    for(auto const& [largest_even, trials] : filtered_rolls) {
        //Lots of formatting garbage. TBH C++ is a little backwards with how it handles this
        std::cout << 
        "Largest Even " << std::setw(2) <<  largest_even << 
        " " << std::setw(6) << trials << " trials (" << 
        std::setw(6) << std::setprecision(3) << std::fixed << (trials / 10'000.0) << "%)\n";
        total_trials += trials;
    }
    std::cout << "===\n";
    std::cout << "Total:          " << std::setw(6) << total_trials << 
    " trials  " << std::setw(6) << (total_trials / 10'000.0) << "%\n";
}

These are the important parts:

  • get_rolls(): This spits out a Map that relates every single unique permutation of a roll (order-independent) to the number of ways that permutation can be rolled. If all you want is a list of every possible roll and its relative probability, this is all you need.
  • filter_rolls(...): This takes the map from the previous function and removes any rolls we don't need. In this case, I programmed it to only keep rolls that satisfied the "Any Even X; X/2" condition, which is to say
    • It only keeps rolls if they contain at least one even value, AND
    • The roll also contains a value that is exactly half that even value
    • If the roll contains multiple such pairs (like 1,2,3,6,4,8), it will record the highest pair it found by recording its larger value (so the highest pair is 4,8 recorded as 8)
  • Then we print out the results in main. This only contains display code and doesn't contain any logic for creating or filtering the rolls.

So the output of this program looks like this:

Largest Even  2  90002 trials ( 9.000%)
Largest Even  4  90002 trials ( 9.000%)
Largest Even  6 153662 trials (15.366%)
Largest Even  8 176102 trials (17.610%)
Largest Even 10 199262 trials (19.926%)
===
Total:          709030 trials  70.903%

We can learn a few important things from this:

  • For the "Any Even X; X/2" condition, the person rolling has a 70.903% chance of fulfilling this condition on a 6d10 roll. So if this were the condition for an ability, the roller would have a 71% chance of success.
  • We can also see the probabilities of each possible outcome, and inductively deduce the sum of that pair (a Largest Even of 2 would have a sum of 3, because it would be paired with a 1)

If you wanted to test other pairs, you'd just have to change the logic of the get_largest_even function, since that one is doing the work of identifying the dice being rolled (maybe rename it?).

This logic should work in any programming language that supports the same basic features as C++ (which is most; C++ has a relatively bare-bones Feature Library...), provided you learn how to port it over. So if you're willing to learn a programming language (and I do recommend C++!) you'll be able to update/revise this logic to handle each condition you want to nominate for use in your homebrew system.

Alternately, pester me in chat and if I have spare time, I can run other conditions through this code.

\$\endgroup\$
  • \$\begingroup\$ This is awesome and will be incredibly helpful as an example of a logic to manage this kind of calculation! I won't pester you as the amount of permutations for possible combinations I need to run is too many, and I need to learn to do the work myself but I appreciate the offer (I also can't because I don't meet the minimum requirements as a new member). Any recommendations for a good program to write code in that can help me track individual lines to combat my Dyslexia? Then again I'm sure I can just google this. Thank you again for your effort, you've given me a great starting point. \$\endgroup\$ – l3rokenwing Mar 27 at 20:53
  • \$\begingroup\$ @l3rokenwing I don't have experience coding as someone with Dyslexia, or first-hand knowledge of a coder who has dyslexia. So yeah, you'll need to google that. \$\endgroup\$ – Xirema Mar 27 at 20:55
2
\$\begingroup\$

This is an interesting problem in probability. I'm only going to examine the chance of success for the first two dice bin from the pool. The complications that arise in this calculation will just get more complex for later bins.

Terminology: I'm going to refer to conditions that the dice need to meet for success as tests. So in your example of the 6d10 pool, one test is 9 and the other test is odd less than 9. If I'm understanding the system, the bin succeeds if each dice passes a different test.

It was a big help to scale down the problem to something manageable. A pool of 3d5 has only 125 possible outcomes and is big enough to cover the important variations. In each variation, there are two tests. The first test has a 3/5 chance of success on a single dice. The second has a 2/5 chance of success on a single dice. I played around with these in Excel, looking at all the possibilities until I had an instinct for what was actually going on.

For a pool, a test is succeeded if at least one dice succeeds the test. This is calculated as the complement of the chance of all dice failing the test. So if \$P(T_k,1)\$ is the probability of succeeding Test k with one dice, then the probability of succeeding Test k with a pool of n dice is $$ P(T_k,n) = 1 - [1 - P(T_k,1)]^n $$

So with my specific tests \$P(T_1,1) = \frac {3}{5} \$ yields \$P(T_1,3) = 0.936\$ and \$P(T_2,1) = \frac {2}{5}\$ yields \$P(T_2,3) = 0.784 \$.

Now let's start with an analog to your example. Your two tests did not overlap. A dice can yield a 9, or it can yield an odd number less than 9, or it can fail to do either. It can't do both. The probability calculation is the simplest in this case. The bin succeeds if both tests succeed. Since the tests are exclusive, they must be passed by different dice. So if both tests are passed individually, there is no possibility that the combination will fail. The probability of the bin succeeding is easiest to view as the complement of the bin failing, which in turn is just the probability of either test 1 failing or test 2 failing. $$ P(B) = 1 - [(1 - P(T_1)) + (1 - P(T_2))]$$ An example of this in my 3d5 toy problem would be test 1 is passed by an odd number and test 2 is passed by an even. Plugging in the numbers, you get \$ P(B) = 0.72 \$.

Things get more complicated if the tests aren't exclusive. There are two ways this can happen. First, one test is a subset of the other. For example test 1 succeeds for odd numbers, while test 2 succeeds for odd numbers less than 5. If a dice passes test 2, it automatically passes test 1, although the reverse is obviously not true.

Now to enumerate the way the bin fails. We have to pay attention to overlap so we don't count a failed case twice. First, test 1 fails; this automatically implies that test 2 fails. Second, test 2 fails, but test 1 succeeds; that is to say, the only odd numbers are 5. Finally, both tests succeed but only on one dice; so one dice is a 1 or 3 and the other two are evens. The math for writing this out as a formula is getting complicated to the point where I don't think churning through the formula adds anything. Anyway, this drops the probability of getting a successful bin down to \$ P(B) = 0.592 \$.

The last possibility is that two tests have a partial overlap. So some results are a success for both tests, but some results are successes for only one or the other. For example test 1 succeeds (once again) for odd numbers and test 2 succeeds for perfect squares. So a roll of 1 succeeds both tests, rolls of 3 and 5 succeed only test 1, 4 succeeds only test 2, and 2 fails both tests.

So the bin fails if (a) test 1 fails (only even numbers), (b) test 1 succeeds but test 2 fails (only non-square numbers including at least one odd), (c) both tests fail (only even non-square numbers, i.e. 2), or both tests succeed on only one die (roll one 1 and the rest 2's). I didn't even try to figure the formula, but just running through all the possibilities gives a bin success probability of \$ P(B) = .704 \$.

As you look at the possibility of succeeding in a second bin in a pool, you compound the effects of the overlap in test success values, so it gets pretty complicated.

If you're going to go for this, I'd advise sticking strictly to having non-overlapping tests in a single bin. You can then at least calculate the odds of a single pair of tests in the first bin of the pool. Probability of success for later bins of the pool will drop in ways that are difficult to predict, but you can at least have a basis of comparison.

\$\endgroup\$
  • \$\begingroup\$ Thanks for taking the time to go through this. In my vision of the system the first pair of tests is the only one I want to balance around because the object is to focus on the primary allocation of resources. If a second pair of tests can be met by the same pool of dice that's serendipitous and would be akin to low probability criticals. Do you know what the probability of a pair of 1/10 tests would be as a baseline? If you codified all 50 combinations of 1/10 tests and imagined picking them to combine them, do you have a sense for it's affect on the probability growth (linear or exponential) \$\endgroup\$ – l3rokenwing Mar 28 at 14:51
  • \$\begingroup\$ I assume the growth is exponential. To clearify, I'm asking about building these tests from a core building block of their simplest 2-die combinations. So in the test you simplified of 9 and an Odd<9, I'm thinking of balancing a cost as though you had chosen 9/7, and 9/5, and 9/3, and 9/1 (order doesn't matter). I assume the probability is the same when expressed this way, but I guess I'd like some confirmation. \$\endgroup\$ – l3rokenwing Mar 28 at 14:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.