Here's a function to count duplicates in a roll:
function: count highest dupes in DICE:s {
MAX: 0
loop X over {1..1@DICE} {
COUNT: X = DICE
if COUNT > 1 & COUNT > MAX { MAX: COUNT }
}
result: MAX
}
Let's step through the function.
function: count highest dupes in DICE:s {
The function declaration takes one parameter, the dice pool rolled, and casts it to a fixed sequence for inspection.
MAX: 0
Initialises a counter variable, MAX
, to remember what the maximum number of duplicates is.
loop X over {1..1@DICE} {
Loops a variable X
over the sequence of values from 1 to the highest individual die result in the pool. Thanks to the automatic sorting of dice cast to a sequence, we know that the first number in the sequence (1@DICE
) will be the highest die, and we don't need to check any numbers higher than that.
COUNT: X = DICE
if COUNT > 1 & COUNT > MAX { MAX: COUNT }
For each value of X, count the number of matches in the pool. If there is more than one match and the number of matches is higher than the currently known maximum, set that to be the new maximum.
}
result: MAX
}
When the loop ends, the result is whatever the maximum number of duplicates was.
Your specific case of rolling 5d6 would be invoked by:
output [count highest dupes in 5d6]
This would be easy to do if we could just separate the lowest roll from the rest of the rolls, add the bonus to it, merge them back together and the count the results.
Unfortunately AnyDice's sequence manipulation kind of sucks. In particular, there's no simple built-in way to extract just a part of a sequence without summing it. But we can do it with a custom helper function, like this:
function: select INDICES:s from SEQUENCE:s {
RESULT: {}
loop INDEX over INDICES {
RESULT: {RESULT, INDEX@SEQUENCE}
}
result: RESULT
}
(Note that this function really needs to be called with actual sequences. If you try to call it with dice as either parameter, the results will just be summed together, just as if you'd simply used INDICES@SEQUENCE
.)
With this helper function, everything is pretty straightforward:
function: evaluate ROLL:s {
if (ROLL >= 8) >= 2 { result: 3 } \ crit success \
if (ROLL >= 8) >= 1 { result: 2 } \ normal success \
if (ROLL >= 5) >= 1 { result: 1 } \ success with setback \
result: 0 \ failure \
}
function: evaluate ROLL:s with bonus BONUS:n {
ROLL_WITHOUT_LOWEST: [select {1 .. #ROLL-1} from ROLL]
LOWEST: (#ROLL)@ROLL
ROLL_WITH_BONUS: [sort {ROLL_WITHOUT_LOWEST, LOWEST + BONUS}]
result: [evaluate ROLL_WITH_BONUS]
}
(The [sort]
is technically not necessary, since nothing in [evaluate ROLL]
as I've implemented it cares about how the roll is sorted. But AnyDice normally sorts dice pools from highest to lowest, and functions that expect to be called with dice rolls often assume that, so having that extra sort there eliminates a hidden "trap". As long as the code doesn't time out, I'd rather waste a bit of time with an extra sort than leave it out and risk something breaking because I forgot the sequence wasn't sorted.)
Ps. Somewhat remarkably, a bonus of less than 5 points doesn't seem to change the probability of a "success with setback" at all! That kind of makes intuitive sense, since the bonus is approximately as likely to convert a failure into a setback than a setback into a success, but the fact that the probabilities balance exactly is slightly surprising.
Best Answer
The quickstart guide has a table...
2LM has provided a quickstart guide (ZIP) on the official Household website. Page 60 has a table of probabilities:
...but the probabilities are not correct.
For example, consider the possibilities for rolling a triple on 3 dice:
Total: 29 ways out of 216, or about 13.43%, versus the table's 12%.
Or, consider the possibilities for not rolling anything on 3 dice:
Total: 76 ways out of 216, or about 35.19% chance of no success. That's considerably less than the 42% implied by the table.
One could try to interpret the clause
as not counting pure-Joker rolls, but this isn't enough to repair the probabilities, and it also causes the 2-pool probabilities to diverge from the table.
Computed probabilities
Here are probabilities computed using my Icepool Python package:
You can run the script in your browser using this JupyterLite notebook.
Some other ambiguities in the quickstart