8
\$\begingroup\$

I'm designing a tabletop game with dice pool mechanic for combat resolution. You roll some d6 and each die that rolled at least 3 counts as a SUCCESS:

  • If you roll more 1s than 6s, it's a fumble
  • One or two SUCCESSES is a light wound
  • Three SUCCESSES in one roll is a severe wound
  • Six SUCCESSES is a critical wound
  • For each 6 you roll you get a SUCCESS. Also, add another d6 to the pool for each 6 you rolled. Count each of these die as a SUCCESS regardless of the number thrown. Each of these can also explode on 6.

I have all but last point covered in the below function (direct link to AnyDice function). But I'm struggling with the last part, the exploding dice mechanic.

That is critical to evaluating the system, as without exploding dice fumble chances increase, and are nearly 1/3 which is way too high. Exploding dice will limit the likelihood of fumbles. [edit: no, it won't]

Any idea how to write this function?

FUMBLE: -1
MISS: 0
WOUND: 1
SEVERE: 2
CRITICAL: 3

function: evaluate ROLL:s {
 SUCCESSES: ROLL >= 3
 if [count 1 in ROLL] > [count 6 in ROLL] { result: FUMBLE }
 if SUCCESSES = 0 { result: MISS }
 if SUCCESSES = {1..2} { result: WOUND }
 if SUCCESSES = {3..5} { result: SEVERE }
 if SUCCESSES >= 6 { result: CRITICAL }
}

loop DICE over {1..6} {
 output [evaluate DICE d6] named "[DICE]d6"
}
\$\endgroup\$
14
  • 2
    \$\begingroup\$ [Related] How can I model Burning Wheel probabilities in AnyDice? \$\endgroup\$ Commented Jul 29, 2016 at 21:43
  • \$\begingroup\$ Do 6s add one success for the 6 and one for the new die, or just one for the new die? \$\endgroup\$
    – user17995
    Commented Jul 29, 2016 at 21:51
  • \$\begingroup\$ @TuggyNE each initial 6 counts as a SUCCESS, and each consecutive d6 counts as an additional SUCCESS. Good question, editing OP. \$\endgroup\$
    – bramford
    Commented Jul 29, 2016 at 21:54
  • \$\begingroup\$ Do additional 6s and 1s rolled due to exploding dice count to the FUMBLE pool? \$\endgroup\$
    – Thyzer
    Commented Jul 29, 2016 at 21:58
  • 1
    \$\begingroup\$ If you're aiming for a rules-light system, I'll warn you that dice pools appear to often lead to rules-heavy. Caveat; this may be a "correlation vs causation" issue. That is, all the systems I know that use dice pools are very crunchy. Are they crunchy because they use dice pools, or do they simply happen to use dice pools and have a crunchy ruleset as well? ...hm, Anyone know of any rules-light systems with dice pools? \$\endgroup\$
    – tzxAzrael
    Commented Jul 29, 2016 at 22:10

3 Answers 3

1
\$\begingroup\$

There's a built-in explode function, but you have to use it before doing your other processing. Specifically, use FOOdBAR to have BAR evaluated as a sequence and used to generate FOO "dice" with arbitrary side counts and values. Here, DICEd([explode d6]) does the job, turning an exploded d6 into the base for the actual roll. (explode doesn't separate dice that you give it — you have to do that, or you'll get a 5d6 roll that only explodes, once, on 30.)

You can't compare sequences to a threshold to get a list or sum of values against that threshold — you'll just get the sequence summed and checked against the threshold. Instead, use [count VALUES in SEQUENCE] for each of the valid values (3-6). And as it turns out, since every 6 triggers a new die and all those dice count, you can just double-count 6s once exploded. So really that should be [count {3..6, 6} in ROLL].

The results, as best I can manage, aren't pretty. Each explosion will either fumble harder (1), make no progress against fumbling (2-5), or break even and get a new chance to do the same thing (6), and the more dice you start with, the more chances for explosions. There is no provision for fumbles to peter out in explosions, as one would expect from a great success; the only question is whether the chain ends with a 1 or not.

\$\endgroup\$
4
  • \$\begingroup\$ If you roll a 6, and with the the explosion dice another 6 you would count the 2nd 6 as two successes. \$\endgroup\$
    – Thyzer
    Commented Jul 29, 2016 at 23:35
  • 1
    \$\begingroup\$ @Thyzer: I did. It doesn't count against fumbles, though. (Specifically, fumbles, as written, don't actually care about successes at all.) \$\endgroup\$
    – user17995
    Commented Jul 29, 2016 at 23:49
  • \$\begingroup\$ This works, but unfortunately it times out on pools larger than four dice. I edited another answer and came up with this, which seems to work fine, although it's not as elegant. anydice.com/program/8f8b I indeed have to revisit the fumble system, as they happen way too often with the way the original question is written. \$\endgroup\$
    – bramford
    Commented Jul 30, 2016 at 0:25
  • \$\begingroup\$ @bramford: I can't seem to get the code from the other answer here, or your link, to agree with my results. I'll keep digging when I have a bit more time and try to figure out where it's going wrong. \$\endgroup\$
    – user17995
    Commented Jul 30, 2016 at 0:29
2
\$\begingroup\$

I took a crack at it in Python (I know it's not anydice. Whatever.). Turns out, even if we exclude exploded ones from counting towards the fumble, fumbles are still WAY too common in any reasonably-sized die pool (my test pool of 8d6 still turned up fumbles around half the time). Here's my Python script that excludes exploded ones:

# This is exploding D6s.
# Basically, you start with a single roll with x dice.
# A result of 4 or higher is a success.
# A result of 3 or lesser is a failure.
# If a 6 is rolled, you roll another die. It will always be a success.
# If that die is a 6, you roll yet another, and so on. This is the "exploding" part.
# However, if more 1's are rolled than 6's, there's a FUMBLE.
# Exploded dice don't count towards 1's when rolled, but do count toward 6's.
# Let's... rationalize that.

import random

def explodeD6(x, xcount=0, explodes=0, explodecount=0, successes=0, ones=0): #x is the number of dice initially rolled.
  if xcount < x: #This means not all the dice in the initial pool have been rolled yet.
    roll = random.randint(1,6)
    if roll == 1:
      ones = ones + 1
    elif roll == 2 or roll == 3:
      successes = successes #Just demonstrating that nothing happens.
    elif roll == 4 or roll == 5:
      successes = successes + 1
    elif roll == 6:
      successes = successes + 1
      explodes = explodes + 1
    else: #Whoops! The randint wasn't between one and six. That's not right.
      print("Oops. Wrong die.")
    print(str(roll)) #Report.
    xcount = xcount + 1 #Indicate one more die has been rolled.
    explodeD6(x,xcount,explodes,explodecount,successes,ones) #And again.
  elif xcount == x: #This means all the initial dice have been rolled, but not necessarily the explosions.
    if explodecount < explodes: #This is an explosion roll. There are no failures, but it can explode again!.
      roll = random.randint(1,6)
      if roll == 1 or roll == 2 or roll == 3 or roll == 4 or roll == 5:
        explodes = explodes #Again, demonstrating nothing happens.
      elif roll == 6:
        explodes = explodes + 1
      else: #D'oh. randint still not working.
        print("Oops. Wrong die.")
      successes = successes + 1
      explodecount = explodecount + 1
      print(str(roll) + "(Explosion)") #Report.
      explodeD6(x,xcount,explodes,explodecount,successes,ones)
    elif explodecount == explodes: #All dice have been rolled and all manner of bonus dice shall have been rolled.
      totalRolls = x + explodes
      if ones > explodes: #Fumble.
        print("With %s ones, but only %s sixes, the result is a fumble, despite %s successes.") % (ones,explodes,successes)
        print("%s rolls total, from a starting pool of %sd6, due to %s explosions.") % (totalRolls,x,explodes)
      elif ones <= explodes:
        if successes > 0: #Success!
          print("There were %s ones, but %s sixes, allowing the %s successes to shine through.") % (ones,explodes,successes)
          print("%s rolls total, from a starting pool of %sd6, due to %s explosions.") % (totalRolls,x,explodes)
        elif successes == 0: #Miss.
          print("There were %s ones, but %s sixes, yet there were %s successes, resulting in a miss.") % (ones,explodes,successes)
          print("%s rolls total, from a starting pool of %sd6, due to %s explosions.") % (totalRolls,x,explodes)
        else: #Oops. Successes are negative.
          print("Something's wrong.")
    else:
      print("Something's wrong.")
  else:
    print("Something's wrong.")

#Holy shit. That's really long. Let's test it.

explodeD6(8)

Copy-paste it here to see it in action.

\$\endgroup\$
1
  • \$\begingroup\$ There are no exploded ones specified in the question, so exploding them is expected. \$\endgroup\$ Commented Nov 25, 2016 at 0:53
0
\$\begingroup\$

Looks like I was wrong and learned something. You can do this on AnyDice. :-) I tweaked that code in the first link in the comments to add in the logic to lower successes when 1s occur. It looks like with a 3d6 roll you would get a -1 or lower score ~8.5% of the time. 0 ~12%, 1 ~22% 2 ~22% and 3 or more ~35% of the time based on the results.

To clarify what this is doing: The functions at the bottom use 1s to indicate that they should allow for that thing (exploding dice, rerolls, subract fails) and 0s to indicate that they shouldn't. Threshold is the dice roll (or higher) that counts as a success. So if it is 3 then 3-6 are successes. I only added the bef function though you can easily tweak the functions to change threshold, if they explode or not, etc. The lion's share of the credit goes to @Magician who answered this question. So for your question you can just edit the last line bef 3 part to be bef [#d6 to roll initially].

function: roll ROLL:n threshold T {
  if ROLL >=T {result: 1}
  result: 0
}

function: rollexploding ROLL:n threshold T{
  if ROLL =6 {result: 1+[rollexploding 1d6 threshold T]}
  result: [roll ROLL threshold T]
}

function: rerollfailures ROLL:n threshold T{
  if ROLL < T {result: [roll 1d6 threshold T]}
  result: [roll ROLL threshold T]
}

function: subtractfailureexplode ROLL:n threshold T{
  if ROLL =6 {result: 1+[subtractfailureexplode 1d6 threshold T]}
  if ROLL =1 {result: -1+[roll ROLL threshold T]}
  result: [roll ROLL threshold T]
}

function: rerollthenexplode ROLL: n threshold T {
  if ROLL < T {result: [rollexploding 1d6 threshold T]}
  result: [rollexploding ROLL threshold T]
}

function: wrapper DICE:n threshold T explode E reroll R fail F{
  RES:0
  loop N over {1..DICE} {
    if E & F {RES:RES+[subtractfailureexplode 1d6 threshold T]}
      else {if E & R {RES:RES+[rerollthenexplode 1d6 threshold T]}
        else {if E {RES:RES+[rollexploding 1d6 threshold T]}
          else {if R {RES:RES+[rerollfailures 1d6 threshold T]}
            else {RES:RES+[roll 1d6 threshold T]}
        }
      }
    }
  }
  result:RES
}

function: b DICE:n{
  result:[wrapper DICE threshold 4 explode 0 reroll 0 fail 0]
}

function: be DICE:n{
  result:[wrapper DICE threshold 4 explode 1 reroll 0 fail 0]
}

function: gr DICE:n{
  result:[wrapper DICE threshold 3 explode 0 reroll 1 fail 0]
}

function: bef DICE:n{
  result:[wrapper DICE threshold 3 explode 1 reroll 0 fail 1]
}

output [bef 3]
\$\endgroup\$
8
  • \$\begingroup\$ Oh, I forgot that anydice is really limited on functions. Now I remeber why I don't use it. LOL. Let me rework that logic a touch. \$\endgroup\$
    – Rodger
    Commented Jul 29, 2016 at 22:08
  • 3
    \$\begingroup\$ The question does belong here—we support questions about AnyDice programming, hence the [anydice] tag. Also, it's probably better to write AnyDice code rather than pseudocode when answering questions about AnyDice programming. \$\endgroup\$ Commented Jul 29, 2016 at 22:32
  • \$\begingroup\$ Yep, but the thing he is doing doesn't work in any dice. I should have been more clear about that and am amending the answer as such. :-) (or if it does I very much look forward to someone explaining to me how to do it!) \$\endgroup\$
    – Rodger
    Commented Jul 29, 2016 at 22:35
  • 3
    \$\begingroup\$ Check out the related question about Burning Wheel linked in the comments on the question. AnyDice can do exploding dice and arbitrary success-counting, just not in an obvious way. It's also OK to say “that probably won't work, but here's some code in another language that will”, but when doing that the writer is taking on the job of being a better salesperson of their answer than they'd normally have to. \$\endgroup\$ Commented Jul 29, 2016 at 22:45
  • 1
    \$\begingroup\$ Yeah, that could work. I'll see if I can get it going and edit the answer if so. Thanks! \$\endgroup\$
    – Rodger
    Commented Jul 29, 2016 at 22:48

You must log in to answer this question.

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