8
\$\begingroup\$

I'm trying to get AnyDice to

  1. roll a dice pool of dNs
  2. if any result = N
  3. roll one more dN
  4. output 1 if that die roll is greater than variable a target number (set by the count of successes e.g. one 'result = N' on the initial roll sets the follow up target to X, two 'result = N' has a target of Y etc.)

I tried writing a custom function (with help from this answer), but I didn't do it right:

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


function: explode N:d target P:s {
 EXTREME: [maximum of N]
 COUNT: [count EXTREME in N]
 if COUNT = 1 {
  result: [if d{N} > 1@P then 1 else 0]
 }
 if COUNT = 2 {
  result: [if d{N} > 1@P then 1 else 0]
 }
 result: 0
}

output [explode 2d12 target {7,4}] named "crit check"

I can't see how to make d{N} > 1@P a boolean in my AnyDice programe, what did I do wrong?

\$\endgroup\$
7
  • \$\begingroup\$ d{N} will give you a uniform roll across all possible values of the dice expression N. dN will give you the expected distribution of that dice roll expression. For example, if N: 4d12, you'll get a flat distribution from 4 to 48. This likely isn't what you want, though it doesn't seem to solve your question. \$\endgroup\$
    – Axoren
    Commented Jul 29, 2022 at 13:01
  • \$\begingroup\$ @Axoren for N = 4d12 I want it roll a d12, is that right? Or should it be d{EXTREME}? \$\endgroup\$ Commented Jul 29, 2022 at 13:03
  • \$\begingroup\$ If you want it to roll 4d12, dN. If you want it to roll 1d12, dEXTREME. Avoid using {} unnecessarily as those establish a new dice kind. For example d{1,4,7} is a three-sided die with faces 1, 4, and 7. d{2d6} is an 11-sided die with faces 2 through 12. \$\endgroup\$
    – Axoren
    Commented Jul 29, 2022 at 13:06
  • \$\begingroup\$ dEXTREME is equivalent to 1d12 where as d{EXTREME} is a 1-sided die whose only face is 12. \$\endgroup\$
    – Axoren
    Commented Jul 29, 2022 at 13:07
  • \$\begingroup\$ Is the 1@P supposed to be 2@P in the COUNT = 2 block? Otherwise, you're not actually doing anything different between the two count results. \$\endgroup\$
    – Someone_Evil
    Commented Jul 29, 2022 at 13:23

2 Answers 2

6
\$\begingroup\$

As a minor addendum to Someone_Evil's answer, their program will run a lot faster if you replace the line:

output [explode 2d12 on 12 target {7, 4}]

with:

output [explode 2d{0:11, 12} on 12 target {7, 4}]

The only difference is that I've relabeled the sides 1–11 on the initial dice with the number 0. The reason this makes the code faster is that, instead of having to call the function for all the \$\frac{12 \cdot 13}2 = 78\$ possible ordered 2d12 rolls, AnyDice now has to call it for only three possible rolls: {0, 0}, {12, 0} and {12, 12}. Since the function only counts the number of 12s in the roll, the results are still the same.


BTW, here's another solution using (almost) no functions:

N: 2   \ number of dice in the initial pool \
X: 12  \ number of sides on the dice rolled \
TARGETS: {12, 7, 4}

function: P:n at S:s { result: P@S }
output dX > [1 + Nd(dX = X) at TARGETS]

Unfortunately, it seems that AnyDice will raise an error if you try to evaluate the expression P@S where P is a die instead of a number, even though it has a perfectly meaningful and useful interpretation consistent with AnyDice's usual handling of dice in numeric expressions (i.e. evaluate the expression for every possible roll of the dice and collect the results into a custom die). Wrapping the expression in a trivial helper function works around this seemingly arbitrary limitation.

So, how does it work?

  • dX = X yields, in effect, a custom dX with the highest side relabeled "1" and all other sides relabeled "0".
  • Nd(dX = X) rolls N of these dice, effectively giving the number of X rolls on NdX.
  • This number can range from 0 to N, but since 0 is not a valid sequence index in AnyDice, we add one to it, making it range from 1 to N+1 instead. Then we use the resulting number as an index to the TARGETS sequence (via the helper function, since AnyDice refuses to let us do it otherwise), thus mapping each possible value of Nd(dX = X) to a distinct element of the sequence: in this case 0 → 12 (which is an impossible target to exceed with a d12), 1 → 7 and 2 → 4.
  • Finally, we compare the resulting target number to a single dX roll, returning 1 (true) if the roll is greater than the target and 0 (false) otherwise.

Note that we had to include the impossible target value 12 at the beginning of the TARGETS sequence to account for the possibility of the player rolling no twelves on any of the initial dice. Of course, if you wanted to experiment with the possibility of letting the player have a chance to crit even in this case, you could specify some lower (and thus actually achievable) target value here instead.

\$\endgroup\$
9
+50
\$\begingroup\$

You're slightly wrong about what's wrong

But it's slightly Anydice's fault. It doesn't have good debugging tools. The specific issue Anydice has here is that you're trying to compare a die to a value. Or to anything, really. The die in question is COUNT. Now, COUNT a die because it is generated from another die. It also happens to be d{0}, so it doesn't even begin to have the information you're looking for.

But I think the full solution starts even before that. I assume you're casting to a die and using [maximum of N] so you can dynamically have the function find N for you. Except it doesn't work like that for multiple dice. Specifically, Anydice combines 2d12 into a single die with the equivalent distribution. So the [maximum of 2d12] is 24. As a bit of an aside, the built in explode does use maximum, but it also doesn't explode quite the way you want. It explodes when you roll max for the roll (=the max sum). You can see the difference between [explode 2d12] and 2[explode 1d12].

And after you've tried to find the die size, you're using the pool as though it were a sequence even though you cast it to a die. A key thing to remember with Anydice is that if you want to do complicated introspection on a die pool, you need to cast it to a sequence.

So to the solution. First, let's give up trying to just find N, we know what input we're giving, so we can just also give it that number. Then, since we want to do introspection stuff we cast it to a sequence. That means things like count work the way we expect it to. Then you assume we want to compare and roll against the COUNT target of P, so let's just do that more dynamically. Of note, when we roll another dN we need to generate it to be a normal die: d{1..N}. Since we're just looking for the greater than chance, we can have return that. If you wanted to do more complicated conditionals with the new die, you'll probably have to pass it to a helper function, and you need to pass it before you try to compare it.

function: explode DIE:s on N:n target P:s {
  COUNT: [count N in DIE]
  if COUNT > 0 {result: d{1..N} > COUNT@P}
  result:0
}
output [explode 2d12 on 12 target {7, 4}]

Oh, and if you want to disentangle the size of the "explode" critera to the size of the new die, you'll probably have to set up a new parameter (or hard-code it I suppose).

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This works, and is going to give me hours of fun! \$\endgroup\$ Commented Jul 29, 2022 at 17:30

You must log in to answer this question.

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