AnyDice

Legend of the Five Rings

Keeping Dice

Let's take a look at the odds for the Legend of the Five Rings RPG. The core mechanic consists of rolling up to ten exploding d10 and summing some of the highest dice rolled. Technically you could pick any of the dice rolled to sum, but everyone always wants the highest.

So how to do something like "6 keep 3" in AnyDice? You can start by using the [highest NUMBER of DICE] function.

output [highest 3 of 6d10] named "6k3"
6k3
6k3 without exploding.

This seems to work, but it doesn't use exploding dice. To add explosions, you can use the [explode DIE] function, which by default creates a die that exploded up to two times. Replace the regular d10 with that.

output [highest 3 of 6d [explode d10]] named "6k3"
6k3 exploded
6k3 with exploding.

This works, but unfortunately it takes a while to compute. If you're rolling only a few dice then this isn't a problem, but AnyDice might choke on more. This is because an exploding die is a lot bigger than a regular die, which makes selecting the highest of a pool a lot more expensive to compute.

The Best of Both Worlds

Can we get the results of exploding dice, with the performance of regular dice? It turns out that we can, if we postpone exploding dice after keeping the highest. While this is not exactly the same as exploding first and then selecting the highest exploded dice, the results are nearly identical. In practice that will be what you end up doing anyway: keep the highest and then explode the 10s.

So we have to keep track of which dice need to be exploded, take the highest of those and sum them, then add the explosions later. We can do that with a custom die that makes the 10s easy to track. A d{1..9, 1000} does the job.

output [highest 3 of 6d{1..9, 1000}] named "bogus"

Next, we need a function that converts those 1000s into exploded d10.

function: convert SUM:n {
 if SUM >= 1000 {
  TENS: SUM / 1000
  result: SUM - TENS * 990 + TENS d [explode d10]
 }
 result: SUM
}

output [convert [highest 3 of 6d{1..9, 1000}]] named "6k3 exploded after keeping"

This gets us what we want, but it's a convoluted way to say "6k3". So as a finishing touch we add a nice wrapper function.

function: ROLL_SIZE:n k KEEP_SIZE:n {
 result: [convert [highest KEEP_SIZE of ROLL_SIZE d{1..9, 1000}]]
}

output [6k3] named "handy 6k3"

We now have an easy to use roll-and-keep function, and it is fast too! Here is a demonstration:

loop POOL over {3..10} {
 output [POOL k3] named "[POOL]k3"
}
3k3 to 10k3
From 3k3 to 10k3.

Comparison

Finally, here's a rather extreme example illustrating the difference between the three approaches.

output [highest 1 of 6d10] named "6k1 not exploded"
output [highest 1 of 6d[explode d10]] named "6k1 exploded before keeping"
output [6k1] named "6k1 exploded after keeping"
6k1 comparson
Comparing approaches.