[RPG] How to simulate dependent dice rolls in anydice

anydicednd-5e

I'm attempting to find the damage distribution (given a target with stats) of a series of two 5e attacks, mind sliver and then two melf's minute meteors. But it seems I'm having trouble modeling dice rolls that have other dice rolls that they are dependent on. For instance:

function: MOD saving throw dc DC {
 result: d20+MOD < DC
} 

function: true or half SUCCESS DAMAGE {
 result: DAMAGE / (2 - SUCCESS)
}

function: true or none SUCCESS DAMAGE {
 result: SUCCESSdDAMAGE
}

TARGET_INT:1
TARGET_DEX:1
SPELL_SAVE:15
CANTRIP_DICE:2
INT_FAIL_CHANCE:[TARGET_INT saving throw dc SPELL_SAVE]
DEX_FAIL_CHANCE:[TARGET_DEX saving throw dc SPELL_SAVE]
SLIVER_DEX_FAIL_CHANCE:[TARGET_DEX-1d4 saving throw dc SPELL_SAVE]

ONE_METEOR: [true or half DEX_FAIL_CHANCE 2d6]
TWO_METEOR: ONE_METEOR + ONE_METEOR
ONE_SLIVERED_METEOR:[true or half SLIVER_DEX_FAIL_CHANCE 2d6]
TWO_METEOR_SLIVERED:ONE_SLIVERED_METEOR + ONE_METEOR
MIND_SLIVER: [true or none INT_FAIL_CHANCE CANTRIP_DICEd6]

function: sliver meteor {
 if INT_FAIL_CHANCE > 0 {
  result: CANTRIP_DICEd6 + TWO_METEOR_SLIVERED
 } else {
  result: TWO_METEOR
 }
}

output [sliver meteor]

At the very end, I need to use a slightly different "hit chance" if mind sliver "hit" the target before the first meteor. The only way I could imagine this working is if it branched on success or fail of the first roll, or mapped different outcomes (0 or 1) to different dice roll values. But it seems anydice is unable to do this. Although I might be missing some contrived solution that technically makes this possible.

The above code results in this error:

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

So it seems like the function conditional code doesn't simulate dice rolls and retabulate in the way I was expecting. Though this type of calculation would seem to fit right in with anydice, I just can't seem to figure out how to implement it with the language.

It's hard to know exactly how things are implemented under the hood, but it seems like maybe this would work as a language extension:

CONDITIONAL ? EXPRESSION_TRUE : EXPRESSION_FALSE

Where the conditional needs to be a boolean value or a 0-1 dice roll, and the result would be similar to an existing valid anydice expression in the dice case:

CONDITIONALdEXPR_ONE + (1-CONDITIONAL)dEXPR_TWO

Except it would take into account that the rolls are dependent rather than independent. Although to be fair, these might be equivalent by some statistical reasoning that currently escapes me.

So, how do I do this with the current version of anydice?

EDIT: Now that I know the answer, I can say with confidence that the independent and dependent versions of the resulting distribution vary a lot, most notably with the fact that treating the first roll as two independent rolls has a decent chance of being 0, whereas the dependent version has no chance of ever being zero. Which makes sense, if no term gets "selected".

Best Answer

You're trying to do a conditional on a value which is a die, which anydice doesn't like. In order to do a conditional comparison on the result of a die, you have to instead cast it to a number in the function definition. Try changing the last part of your program:

function: sliver meteor FAIL:n {
 if FAIL > 0 {
  result: CANTRIP_DICEd6 + TWO_METEOR_SLIVERED
 } else {
  result: TWO_METEOR
 }
}

output [sliver meteor INT_FAIL_CHANCE]

The FAIL:n parameter in the definition indicates that a number value is expected, and as per the documentation on functions:

If a sequence is provided, then the sequence will be summed. If a die is provided, then the function will be invoked for all numbers on the die – or the sums of a collection of dice – and the result will be a new die.

So rather than referring to the INT_FAIL_CHANCE external variable, provide it as a parameter to the function converted to a number, which anydice will then accept.