10
\$\begingroup\$

Let's say the rulebook says this:

Roll one 4-sided die, two 6-sided dice, and one 10-sided die. Add the highest two rolls together.

In a different environment, I might write something like this:

int[] results = {1d4, 1d6, 1d6, 1d10}
results.sort_high_to_low
return {results[1], results[2]}

How can I write that in AnyDice?


So far, this is the best I've come up with, but it doesn't do what I'm expecting:

X: [sort {1d4, 1d6, 1d6, 1d10}]
output 1@X + 2@X
\$\endgroup\$
  • 1
    \$\begingroup\$ If you're asking this because of Dogs in the Vineyard (you're rolling mostly d6s but also d4s and d10s, you might be rolling a different combination of dice other times, you're interested in the sum of the two highest dice-- what other system does this?) you should add the tag so I can lambast you for analyzing the actual probabilities involved in success and failure and fallout in my answer ;P \$\endgroup\$ – Please stop being evil Mar 5 '17 at 20:04
  • 3
    \$\begingroup\$ Possible duplicate of How should I emulate the highest in an irregular dice pool in AnyDice? \$\endgroup\$ – CruelCow Oct 13 '19 at 20:08
6
\$\begingroup\$

As usual with AnyDice, the solution is "write a function." Here's a simple example:

function: highest N:n of A:s B:s C:s {
    result: {1..N}@[sort {A, B, C}]
}
output [highest 2 of 3d4 2d6 1d8]

Of course, this technique can be easily extended to more than three types of dice simply by adding more parameters to the function. The trick here is that passing a die into a function that expects a number or a sequence will "freeze" the die roll, invoking the function for every possible outcome of the roll in parallel, and summing up the results (weighted by their respective probabilities). Thus, inside the function, you can just manipulate the sequences of individual dice as you want.

Note that converting dice into sequences like this is a "brute force" solution, since it ends up invoking the function separately for every possible outcome of every roll. For large amounts of dice, it can run very slowly or even time out.

In particular, if you only want the single highest roll in the pool, the solution I presented in this earlier answer is a lot more efficient: by discarding all but the highest die of each type before calling the function, the number of combinations that AnyDice must iterate over becomes a lot smaller.

\$\endgroup\$
  • \$\begingroup\$ As usual with Anydice, another brilliant answer from Ilmari. \$\endgroup\$ – Miniman Mar 6 '17 at 2:02
3
\$\begingroup\$

You are butting up against a core problem in AnyDice-- the inability to create non-homogeneous dice collections. When you write sort {1d4,1d6,1d6,1d10} you are not sorting a collection of dice (as you may expect): you are sorting a sequence.

{1d4,1d6,1d6,1d10} yields {1,2,3,4,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,7,8,9,10}

Sorting this yields, predictably {10,9,8,...,1,1,1,1}

so 1@X + 2@X is 19. X is not a die.

You can turn X back into a die trivially, by using dX. Unfortunately, X is still not a collection of dice and so you can't use the highest N:n of D:d built-in to solve your problem. In fact, it is impossible to generate a collection of dice consisting of dice of different sizes in AnyDice and so you will have to brute force the logic for this functionality. You can look at the highest of X:n and Y:n built-in to see sort of where to start. You'll end up with something like:

A:d4
B:d6
C:d6
D:d10

and then a function expecting 4 numbers with a bunch of conditional branches handling the cases where each is larger(see below).

Alternatively, you can sometimes get really close to what you want just by using:

highest of 1d4 and 1d10 + highest 1 of 2d6

which fails when

[lowest of 1d4 and 1d10] > [highest 1 of 2d6]|[highest of 1d4 and 1d10] < [lowest 1 of 2d6]

which is 17.26% of the time according to AnyDice in your test case. The approximation improves the more spread out your die sizes are.

So you can see how awful this is in general, here's the solution for getting the highest two of four dice and summing them:

function: highest two of A:n and B:n and C:n and D:n
{
if A>B {
FIRST:A
if C>B {
if C>A {
FIRST:C
if D>A{ result: D+C}
result: A+C
}
FIRST:A
if D>C{ result: D+A}
result: A+C
}
if D>B {result: D+A}
result: A+B
}
FIRST:B
if C>A{
if C>B{
FIRST:C
if D>B{ result: C+D}
result: C+B
}
if D>C {result: B+D}
result:C+B
}
if D>A {result: D+B}
result: A+B
}

^
|
| Very Ugly D: We do not like it!

and for your example: output [highest two of d4 and d6 and d6 and d10]

Basically, AnyDice really struggles with Dogs in the Vineyard.

\$\endgroup\$
-1
\$\begingroup\$

Here's a partial answer.

The reason your best try hasn't worked is because AnyDice doesn't really treat dice as rollable. A die is a sequence of numbers. Let's take a look:

X: [sort {1d4, 1d6, 1d6, 1d10}]
output 1@X + 2@X

sort {1d4, 1d6, 1d6, 1d10} does NOT do thhis:

  1. Roll a d4 (result = 2)
  2. Roll a d6 (result = 5)
  3. Roll a d6 (result = 4)
  4. Roll a d10 (result = 7)
  5. Sort these results into a sequence {7,5,4,2}

Instead, it does this:

  1. Translate d4 into its sequence {1,2,3,4}
  2. Translate d6 into its sequence {1,2,3,4,5,6}
  3. Translate d6 into its sequence {1,2,3,4,5,6}
  4. Translate d10 into its sequence {1,2,3,4,5,6,7,8,9,10}
  5. Sort these sequences into a sequence {10,9,8,7,6,6,6,5,5,5,4,4,4,3,3,3,2,2,2,1,1,1}

Which is why your try returns 19 with a probability of 100% and deviation of 0. it's the highest two of that sequence: 10 + 9. Always.

I suppose the main problem here is: How can you make AnyDice do stuff, with dice, without turning the dice into sequences?

Like I said, this is a partial answer. I don't know how what you're asking can be accomplished, or if it even can be accomplished. My gut says yes.

\$\endgroup\$
  • 1
    \$\begingroup\$ Partial answers are actually ok (though our community frowns on them), but they should be posted as CW (that's one of the things it's for). However this question is actually pretty simple so I don't think collaboration is needed to address it. That said, what you've written is a pretty good explanation of the core problem with the OPs attempt so it might be worth using. \$\endgroup\$ – Please stop being evil Mar 5 '17 at 18:16
  • \$\begingroup\$ Sorry, what's a CW? And as we saw with your answer, the question wasn't very simple at all. \$\endgroup\$ – MGlacier Mar 5 '17 at 20:07
  • \$\begingroup\$ yeah my idea at the time of commenting was to use the highest N:n of D:d function because I hadn't realized that that wasn't possible yet ^^; It was a lot more complex than I thought. CW stands for Community Wiki. They are almost never useful but this was actually a rare situation where they could have been. The problem ended up being that I disagree with why the OPs attempt didn't work on a minor point, so I made my own answer rather than editing. A community wiki gives no-one rep points and used to have special editing privilledges but I think those are gone now. \$\endgroup\$ – Please stop being evil Mar 5 '17 at 20:19
  • \$\begingroup\$ The idea is that they enable partial answers where one person can do a bunch of work but not enough work to actually answer it, and then someone else can come along and add more, and so on till it's done. They work best in situations like this where what seems to be one problem is actually two complex but distinct and related problems. More than two and the questions get shut down by the community as ~too hard~ too broad :( In a situation like this theoretically I could have added the 'this is how you'd do it and why it's hard' part to your 'this is why it doesn't work' part. \$\endgroup\$ – Please stop being evil Mar 5 '17 at 20:22

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.