8
\$\begingroup\$

Pretty much the question. I tried the following:

function: select INDICES:s from SEQUENCE:s {
  RESULT: {}
  loop INDEX over INDICES {
    RESULT: {RESULT, INDEX@SEQUENCE}
  }
  result: RESULT
}
X:3d6

if [select 2 from X] = 6 {
    output X + [explode 6]
}
else {
    output X
}

But I get the following:

Calculation error:

Boolean values can only be numbers, but you provided "d{?}".
Depending on what you want, you might need to create a function.

What I am doing wrong?

\$\endgroup\$
3
  • \$\begingroup\$ Hi Aralkis, welcome to RPG Stack Exchange! Take the tour if you haven't already, and check out the help center for additional help. \$\endgroup\$ Jan 8, 2023 at 19:11
  • 1
    \$\begingroup\$ It looks like you've found a solution, but you're not supposed to edit into your question. Instead, post it as an answer and accept that answer. This marks the question solved and shows people what the solution is. \$\endgroup\$ Jan 8, 2023 at 20:58
  • \$\begingroup\$ Ok, thanks. It's my first post at stock exchange. \$\endgroup\$
    – Aralkis
    Jan 8, 2023 at 21:00

2 Answers 2

11
\$\begingroup\$

The usual solution to "How do I count dice rolls in some special way in AnyDice?" is to:

  1. write a function that takes a sequence parameter (i.e. one with :s appended to the parameter name), and
  2. give your rolled dice pool as the parameter to that function.

AnyDice will then automatically call the function for each possible (sorted) combination rolled on the dice and collect whatever the function results into a custom die, weighted by the probability of rolling each of the combinations. Inside the function the value of the paramater will be just a fixed sequence, and so you can inspect and manipulate it any way you'd normally manipulate a sequence, including using if conditions based on it.

Here's a quick example that should do what you want:

function: evaluate ROLL:s {
  if [count 6 in ROLL] >= 2 {
    result: ROLL + [explode d6]
  } else {
    result: ROLL
  }
}

output [evaluate 3d6]

What was wrong with your code, then?

The main problem was that, in your code, you had if [select 2 from X] = 6 outside a function, where X is a dice pool (specifically 3d6), not a sequence.

The select INDICES from SEQUENCE function (which looks familiar) is meant to return a sequence, but it can only do that if both parameters given to it are actual sequences (or numbers, which AnyDice will happily convert into single-element sequences). As I noted in the answer linked above:

If [either parameter is] a pool of dice, the function will still technically work, but the results will get automatically summed back into a number before being collected into a die[.]

So, in your code, since X is a die (or, rather, a pool of dice — AnyDice terminology is sometimes a bit confusing here), [select 2 from X] will also be a die. And thus [select 2 from X] = 6 will also be a die — specifically, a die that rolls 1 if the second-highest roll in 3d6 is a 6, and 0 otherwise. And you can't pass a die to if in AnyDice.


So how could you have fixed your code, then?

The simplest way (in the sense of making the fewest changes to your code) would've been to just wrap the if statement in a function that takes X as a sequence parameter, like this:

function: select INDICES:s from SEQUENCE:s {
  RESULT: {}
  loop INDEX over INDICES {
    RESULT: {RESULT, INDEX@SEQUENCE}
  }
  result: RESULT
}

function: evaluate X:s {
  if [select 2 from X] = 6 {
    result: X + [explode d6]
  } else {
    result: X
  }
}

output [evaluate 3d6] 

(I also fixed a small bug in your code where you had [explode 6] instead of [explode d6]. With that change, this code gives the exact same output as my first example program above.)


Ps. There actually is a way to kind of make AnyDice support if conditions based on dice, using a helper function like this:

function: if CONDITION:n then A else B {
  if CONDITION { result: A } else { result: B }
}

However, if you try to rewrite your code using this trick, something like this:

output [if [select 2 from X] = 6 then X + [explode d6] else X]

or even like this:

output X + [if [select 2 from X] = 6 then [explode d6] else 0]

you'll find that these programs give subtly incorrect results: the average will in fact be correct, but the standard deviation and the shape of the distribution graph will be subtly off:

AnyDice screenshot showing correct and incorrect results calculated using the methods described above

Experienced AnyDice users will, of course, quickly realize what the problem is: in the expressions above, each X represents a separate independent 3d6 roll. So the actual dice rolling mechanic that ends up being modelled here isn't "roll 3d6, then add an exploding d6 if you rolled at least two sixes", but rather "roll 3d6, then roll 3d6 again and add an exploding d6 if you rolled at least two sixes on the first 3d6 roll".

Of course, wrapping the code above inside a function, so that X becomes a sequence instead of a die, does make it work as intended:

function: evaluate X:s {
  result: [if [select 2 from X] = 6 then X + [explode d6] else X]
}

output [evaluate 3d6]

Basically, any time you want to implement a mechanic in AnyDice where you roll some dice and use the result of the roll more than once, you do need to write a function and pass the roll into it as a sequence parameter. That's the only way in AnyDice to "freeze" the result of a dice roll into a fixed sequence (or number).

\$\endgroup\$
3
  • \$\begingroup\$ Thanks, that works. Yeah, I got that select from sequence somewhere here. I ended up finding a post with a custom conditional function. That way it accepts dice as input. \$\endgroup\$
    – Aralkis
    Jan 8, 2023 at 20:38
  • \$\begingroup\$ @Aralkis: Yeah, that helper function also looks familiar. :) You don't really need it, though — if you try to use it outside a function, where X is a die, it gives subtly wrong results, since each X in the expression represents a separate 3d6 roll. And while it does work correctly inside a function, where X is a sequence, a normal if statement will work there too. See the postscript I added to my answer above for details. \$\endgroup\$ Jan 8, 2023 at 21:04
  • \$\begingroup\$ Yeah, I figured that out. Thanks for the help and for being thorough. Now I understand anydice a little bit better. \$\endgroup\$
    – Aralkis
    Jan 8, 2023 at 21:08
2
\$\begingroup\$

Thanks for the help. As suggested, here and elsewhere, if you write your own conditional function, it works as one would expect:

function: if CONDITION:n then A else B {
  if CONDITION { result: A } else { result: B }
}

I ended up writing the following code:

function: if CONDITION:n then A else B {
  if CONDITION { result: A } else { result: B }
}
function: roll DICE:s {
   ROLL:[if (2@DICE = 6) then (DICE + [explode 1d6]) else DICE]
   result: ROLL
}
output [roll 3d6]
\$\endgroup\$

You must log in to answer this question.

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