4
\$\begingroup\$

I've tried the following code

output [count {5..6} in [highest 2 of 10d6]]

Which yields

0: 99.90%
1:  0.10%

However, these results are implausible. In a 10d6 pool the chances of there being at least one 6 or 5 should be extremely high, and two of them should at least be expected once in a while.


I now understand that AnyDice is trying to find 5s and 6s in the sum of the top two dice of the pool, which is not what I want. I'm still no closer to finding out how to get what I'm looking for.

\$\endgroup\$
4
\$\begingroup\$

As A_S00 has already noted, your problem is that the built-in AnyDice function [highest NUMBER of DICE] returns the sum of the highest dice in the pool.

In fact, within the constraints of AnyDice's data model, that's all that it can do. AnyDice can only handle the following types of variables:

  • single integers ("numbers"),
  • finite lists of integers ("sequences"),
  • probability distributions over finite sets of integers (single "dice"), and
  • "pools" of two or more identical(!) dice ("dice collections").

(Actually, AnyDice makes no real difference between the last two types listed above; a single die is treated exactly like a one-die collection. But conceptually, it can be helpful to distinguish them.)

Notably, an AnyDice variable cannot store a probability distribution over sequences, nor can it store a collection or sequence of several different types of dice. So there is really no practical way for AnyDice to represent e.g. "the highest 2 numbers rolled with 10d6" without somehow collapsing it into one of those supported types.

Sure, it wouldn't be hard to write an AnyDice function that takes a sequence and returns another sequence containing the two (or any number of) highest numbers in it. In fact, here's one, just as an example:

function: highest NUMBER:n of sequence SEQUENCE:s {
  SORTED: [sort SEQUENCE]  \ could be skipped if SEQUENCE known to be already sorted \
  R: {}
  loop P over {1..NUMBER} { R: {R, P@SORTED} }
  result: R
}

And this will indeed work just fine as long as you use it only on normal, non-random sequences. For example, [highest 2 of sequence {3,2,1}] will indeed return the sequence {3,2}. But if you try to apply it to a dice collection instead, all you'll get is the sum of the highest results, exactly the same as what you'd get from the built-in [highest NUMBER of DICE] function.

This happens because, any time you try to pass a dice collection to a function expecting a sequence, AnyDice will invoke the function for each possible outcome of rolling the dice, and then collect the results into a custom die (i.e. a probability distribution) where the probability of each result is the total probability of all the rolls that yield that result. But as I noted above, AnyDice can only handle integer-valued probability distributions; a die in AnyDice cannot have a side labelled "{3, 2}". So if any of the results returned by the function happen to be sequences instead of single numbers, AnyDice must somehow turn them into numbers — and the way it does that, by default, is by summing up the numbers in the sequence.


The standard and, really, the only way to work around that limitation is to do whatever you want to do with the result sequence inside the function, before returning it. For example, like this:

function: count VALUES:s in highest NUMBER:n of ROLL:s {
  R: {}
  loop P over {1..NUMBER} { R: {R, P@ROLL} }
  result: [count VALUES in R]  \ here, R is still a sequence \
}

output [count {5..6} in highest 2 of 10d6]

(Here I've skipped sorting the ROLL sequence, since the sequences generated by AnyDice iterating over the possible outcomes of a dice roll are guaranteed to be already sorted anyway.)

It's also quite easy to generalize this function to allow selecting any positions in the sorted rolls to be counted, e.g. like this:

function: count VALUES:s in positions POSITIONS:s of ROLL:s {
  R: {}
  loop P over POSITIONS { R: {R, P@ROLL} }
  result: [count VALUES in R]
}

output [count {5..6} in positions {1..2} of 10d6] named "count 5..6 in highest two of 10d6"
output [count {5..6} in positions {3..4} of 10d6] named "count 5..6 in 2nd highest two of 10d6"
output [count {5..6} in positions {5..6} of 10d6] named "count 5..6 in middle two of 10d6"
output [count {5..6} in positions {7..8} of 10d6] named "count 5..6 in 2nd lowest two of 10d6"
output [count {5..6} in positions {9..10} of 10d6] named "count 5..6 in lowest two of 10d6"

Addendum: In the particular case of counting dice that roll above a given threshold among the \$N\$ highest dice in the pool, there are much simpler ways to get the same result. The key insight is that, if you roll any dice above the threshold at all, they will always be among the \$N\$ highest — unless, if course, you roll more than \$N\$ dice above the threshold, in which case you'll have some left over.

Thus, in particular, the following two lines produce the exact same output:

output [count {5..6} in highest 2 of 10d6]
output [lowest of 2 and [count {5..6} in 10d6]]

Of course, you don't even really need to limit the results to at most 2 in the code — you could just as well let AnyDice plot the whole distribution of [count {5..6} in 10d6] and just look at the first two columns of it.

In fact, you can also use the last two columns of the same plot to get the distribution of 5s and 6s among the lowest two dice in 10d6, and with some care you can even do the same for the middle intervals as well. Or you can just let AnyDice do the math for you:

function: middle of A:n and B:n and C:n {
  result: 2@[sort {A, B, C}]
}

output [middle of 0 and [count {5..6} in 10d6] - 0 and 2] named "count 5..6 in highest two of 10d6"
output [middle of 0 and [count {5..6} in 10d6] - 2 and 2] named "count 5..6 in 2nd highest two of 10d6"
output [middle of 0 and [count {5..6} in 10d6] - 4 and 2] named "count 5..6 in middle two of 10d6"
output [middle of 0 and [count {5..6} in 10d6] - 6 and 2] named "count 5..6 in 2nd lowest two of 10d6"
output [middle of 0 and [count {5..6} in 10d6] - 8 and 2] named "count 5..6 in lowest two of 10d6"

For even further optimization, we could use the "relabeling trick" and make a custom d6 with the sides 1–4 renumbered as 0 and the sides 5–6 as 1. This not only saves AnyDice from having to iterate over lots of different but equivalent rolls, but since we're now just counting the sides renumbered as 1, we don't even need to call any explicit function for it — just letting AnyDice implicitly sum the results of the roll is enough.

With this optimization, the code above can be equivalently written like this:

HIT: d6 >= 5  \ relabel sides 1..4 to 0 and 5..6 to 1 \

output [middle of 0 and 10dHIT - 0 and 2] named "count 5..6 in highest two of 10d6"
output [middle of 0 and 10dHIT - 2 and 2] named "count 5..6 in 2nd highest two of 10d6"
output [middle of 0 and 10dHIT - 4 and 2] named "count 5..6 in middle two of 10d6"
output [middle of 0 and 10dHIT - 6 and 2] named "count 5..6 in 2nd lowest two of 10d6"
output [middle of 0 and 10dHIT - 8 and 2] named "count 5..6 in lowest two of 10d6"

Of course, both of these tricks are somewhat limiting, since they would not work (without appropriate modifications) if we instead wanted to count, say, the number of ones and twos (or even, say, ones and threes and fives) among the highest (or lowest, or middle, etc.) two of 10d6. But when they do work, they can be very useful.

\$\endgroup\$
5
\$\begingroup\$

As you've surmised, the formula you're using is giving you the count of how many times the sum of [highest 2 of 10d6] is 5 or 6. Needless to say, this is extremely infrequent, since rolling 10 dice will almost always get you 2 rolls high enough to sum to 7 or more.

I don't know of an elegant way to get the built-in "highest" function to give you a sequence of the highest dice, rather than the sum of those highest dice...so here's a totally inelegant way to get it done:

function: truncate A:n to two {
 if A > 2 { result: 2 }
 result: A
}

output [truncate ((d6 >= 5) + (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)+ (d6 >= 5)) to two]

Try it here!

What this does is:

  • Rolls 1d6 ten times, independently.
  • For each one, checks if the result is greater than or equal to 5 (i.e., if it's a success), returning 1 if it's a success and 0 if not.
  • Sums the results (since we're returning 1 on success and 0 on failure, the sum of the results is the same as the count of successes).
  • Passes the sum through a user-defined function that rounds the total down to 2 if it's more than 2. This is equivalent to taking the best 2 of 10.

I would not be surprised if there's a less kludgey way to accomplish this, but this does what you want. The results are:

  • 1.73% chance of 0 successes
  • 8.67% chance of 1 success
  • 89.60% chance of 2 successes
\$\endgroup\$
  • 1
    \$\begingroup\$ FWIW, [truncate 10d( d6 >= 5 ) to two] or even [lowest of 10d( d6 >= 5 ) and 2] would do it a bit more elegantly. \$\endgroup\$ – Ilmari Karonen Nov 3 '18 at 23:59
  • \$\begingroup\$ @IlmariKaronen Huh, interesting. I would have expected the ( d6 >= 5 ) to be evaluated first, once, and then try to roll either 10d1 or 10d0, depending on the outcome. Anydice's handling of parentheses is weird. \$\endgroup\$ – A_S00 Nov 4 '18 at 0:39
  • \$\begingroup\$ It is evaluated first, and it evaluates to d{0,0,0,0,1,1}, i.e. a relabelled d6 (or, equivalently, a biased d2). AnyDice never actually rolls dice, it just does math on probability distributions. \$\endgroup\$ – Ilmari Karonen Nov 4 '18 at 1:04
3
\$\begingroup\$

Think about the problem not the physical dice. Anydice is not constrained to using “real” dice.

A success happens \$1 \over 3\$ times, so you don’t want a d6 you want a {0,0,1} dice.

Then roll this dice:

output [highest 2 of 10d{0,0,1}]

and you’ll get a probability of 0, 1, 2 successes.

\$\endgroup\$
  • \$\begingroup\$ I assume you meant something like output [highest 2 of 10d{0,0,1}], right? \$\endgroup\$ – Ilmari Karonen Nov 3 '18 at 6:05
  • \$\begingroup\$ No, I mean what I wrote \$\endgroup\$ – Dale M Nov 3 '18 at 22:35
  • \$\begingroup\$ Ah... I see what you mean now. Since 5 and 6 are the highest-numbered sides of a d6, any fives and sixes that you roll will always be among the highest N numbers rolled (unless you roll more than N, in which case you'll have some extras left over). So the number of 5s and 6s among the highest 2 of 10d6 equals the total number of 5s and 6s in the 10d6 rolled, capped at 2. Pretty obvious, in hindsight. \$\endgroup\$ – Ilmari Karonen Nov 3 '18 at 22:45
  • \$\begingroup\$ @IlmariKaronen that’s not what it does at all. The three sided dice is simply a “success” dice that’s 1 if you succeed and 0 if you don’t. That means the sum of n of these dice is simply the number of success. You just read it off?o is no successes, 1 is 1 success, 2 2, 3 3 and so on. \$\endgroup\$ – Dale M Nov 3 '18 at 22:48
  • \$\begingroup\$ Yeah, sure, that's just a standard relabeling trick. But the OP could just as well have used output [count {5,6} in 10d6] to get the same plot as your output 10d{0,0,1} gives, and read the results off of that just as easily. After all, all these are equivalent. I'd say the key insight here is that, when counting the number of dice that roll above some threshold, counting only the N highest dice in the roll just caps the result at N. \$\endgroup\$ – Ilmari Karonen Nov 3 '18 at 23:02

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.