8

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"
}
bramford
  • 83
  • 5
  • 2
  • Do 6s add one success for the 6 and one for the new die, or just one for the new die? – user17995 Jul 29 '16 at 21:51
  • @TuggyNE each initial 6 counts as a SUCCESS, and each consecutive d6 counts as an additional SUCCESS. Good question, editing OP. – bramford Jul 29 '16 at 21:54
  • Do additional 6s and 1s rolled due to exploding dice count to the FUMBLE pool? – Thyzer Jul 29 '16 at 21:58
  • So a roll on an exploded dice of 1 will not decrease the total score BUT if it is another 6 it explodes again, right? – Rodger Jul 29 '16 at 21:58
  • Also related, but bear in mind that Shadowrun only counts a 5 or 6 as a success, so if 3+ is a success, your base odds will be much higher, starting at 66+% on the first die, and increasing heavily with additional dice. – tzxAzrael Jul 29 '16 at 22:01
  • @Thyzer yes, they do. – bramford Jul 29 '16 at 22:02
  • @Rodger Correct. But as mentioned above, rolling 1 on an exploded die will count towards a fumble. – bramford Jul 29 '16 at 22:03
  • lol, harsh. Exploding die rolls a 1 and negates itself. – tzxAzrael Jul 29 '16 at 22:03
  • @tzxAzrael Tweaking the difficulty level is one reason why I want to model this in AnyDice. I'm also considering to have different difficulty levels. So a goblin might be 2 or higher, while a dragon might be 5 or higher. I'm not sure if I want to go that route as the game might become too crunchy, though. – bramford Jul 29 '16 at 22:05
  • @tzxAzrael Yes, it's supposed to be a brutal game system. But I get your point, and I'm not a fan of a would-be critical turning into a fumble. But I will see how the odds turn out!

    edit: on second thought, it doesn't strictly negate itself: an exploded dice is a SUCCESS even if it's a 1.

    – bramford Jul 29 '16 at 22:06
  • 1
    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? – tzxAzrael Jul 29 '16 at 22:10
  • @SevenSidedDie That works! I edited it and came up with this: http://anydice.com/program/8f8b As discussed elsewhere, the fumble system just doesn't work as written in the question, as it's way too fumbly, and only gets worse with larger pools. – bramford Jul 30 '16 at 00:25

3 Answers3

2

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.

SevenSidedDie
  • 243,609
  • 44
  • 785
  • 1,025
JessLovely
  • 1,370
  • 1
  • 14
  • 34
1

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.

user17995
  • 5,172
  • 6
  • 36
  • 48
  • If you roll a 6, and with the the explosion dice another 6 you would count the 2nd 6 as two successes. – Thyzer Jul 29 '16 at 23:35
  • 1
    @Thyzer: I did. It doesn't count against fumbles, though. (Specifically, fumbles, as written, don't actually care about successes at all.) – user17995 Jul 29 '16 at 23:49
  • 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. http://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. – bramford Jul 30 '16 at 00:25
  • @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. – user17995 Jul 30 '16 at 00:29
0

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]
Rodger
  • 722
  • 4
  • 10
  • 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. – Rodger Jul 29 '16 at 22:08
  • 3
    The question does belong here—we support questions about AnyDice programming, hence the [[tag:anydice]] tag. Also, it's probably better to write AnyDice code rather than pseudocode when answering questions about AnyDice programming. – SevenSidedDie Jul 29 '16 at 22:32
  • 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!) – Rodger Jul 29 '16 at 22:35
  • I will say from personal experience that VBA is a very poor model for AnyDice coding, since AnyDice is fundamentally based on implicitly executing functions with many parameter variations and determining the aggregate results -- it's not procedural. (It's closer to functional programming, but does not do all that well at chaining functions, either.) – user17995 Jul 29 '16 at 22:42
  • True AnyDice is limited significantly in functionality regarding the odds of successes combined with critical failures so while custom VBA coding doesn't compete at all to using AnyDice for the things that AnyDice is good at... VBA is far superior to using AnyDice for something that can't be done in AnyDice. So in the spirit of answering the question... I gave the Anydice code that is closest to the results and an option that would work better for what they wanted to do. Just cause they tag it AnyDice doesn't mean it is the right tool for the job. ;-) – Rodger Jul 29 '16 at 22:45
  • 3
    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. – SevenSidedDie Jul 29 '16 at 22:45
  • 1
    Yeah, that could work. I'll see if I can get it going and edit the answer if so. Thanks! – Rodger Jul 29 '16 at 22:48
  • Thanks @SevenSidedDie - that was a good link! Sorry I overlooked it to start with. :( Edited the answer and the code to build in the subtract 1 success for 1s logic. :-) – Rodger Jul 29 '16 at 23:55