# Weird dice roll mechanic

There's a game called Cthulhutech which has a dice-based problem resolution system.

I'll use a notation of nd+b where relevant, where "n" is the number of 10-sided dice (giving a value from 1 to 10), and b is the skill bonus.

The first part is simple: The result of a roll is (initially), the highest of all dice.

The next complication is that if any number appears multiple times, the result is the sum of all copies: i.e, if I roll 3d+0 getting [5,5,9], the result is 10 (5+5).

The final complexity is that rolling a series of (at least 3) contiguous numbers results in the sum of all of them: i.e., rolling 5d+0 getting [2,4,5,6,10] is 15 (4+5+6).

The final result is the highest of all three possibilities, so 2d+0 => [2,2,10] is 10.

So, multiple related questions:

1. Is it possible to find a probability function for this (That would be f(n,b,t) represents P(nd+b>=t) where n is the number of dice, b is the bonus, and t is the target. (Or, equivalently, p(nd>=t-b))).
2. Since I can pay a certain amount of skill points in order to raise either n or b, is it valid to take df/dt vs df/db to decide which option is better?
• Since this is just a simplified a variant of Poker calculations (with a ten-card deck, only three kinds of "hands" to consider, and drawing without replacement), the answer to (1) must be "yes": just emulate the procedures used to compute chances in Poker. (2) is harder to answer because it depends on your objectives in the game, what use you can make of the result of the roll, and your aversion to risk. If you could supply that kind of information it would likely be answerable.
– whuber
Commented May 28, 2017 at 19:51
• Oh facepalm I didn't even think about poker, thank you! I'm not sure how to answer your second question; basically, at character creation (for simplicity, let's assume we only have 1 skill), I can spend some amount of points to increase the bonus, or get more dice. Is it possible to know which option is more cost effective? Commented May 28, 2017 at 19:57
• You would have to balance the possible benefits against the costs, in a context in which a cost has a fixed value but the benefits are stochastic. There is a huge literature about this: it's a part of making decisions under uncertainty. Not only are differing answers possible, the choice many people might make is known to depend on how it is described to them!
– whuber
Commented May 28, 2017 at 20:01
• But isn't there a simple case of, say, 1 point = 1 die or 1 point bonus, so if I'm rolling, for example, 1d+0 against a target of 5, 1 point is a flat 10% increase, but 1 die is (say) 30%, so obviously go for the die, but if you're rollin 10d+0, the bonus is still a flat 10%, but the die adds 1%, so go for the bonus? (Obviously this gets complicated when considering long term, but I'm not immediately concerned about this). Commented May 28, 2017 at 20:11
• But it's a discrete function; it changes by a step in response to the smallest possible step change in the x-variable; surely you'd be interested in the difference a change would make. (consider for example that one derivative may be larger, but by the time you get to the next possible value of the argument the other one does better). Commented May 29, 2017 at 0:54

## 2 Answers

(1) Yes, why wouldn't it be possible? Here is an implementation in Python. Beware that the algorithm for computing the outcomes table for each $n$ is $O(10^n)$, and it already takes over a minute to run on my machine with $n = 7$.

from itertools import product, tee
from collections import Counter

min_seq_len = 3
sides = 10

def outcomes(n_dice):
counts = Counter()
for got in product(*tee(range(1, sides + 1), n_dice)):
# First check for n-of-a-kind (including 1-of-a-kind).
best = max(n*v for n, v in Counter(got).items())
# Now look for sequences.
seq = []
for v in sorted(set(got)):
if not seq or v == seq[-1] + 1:
seq.append(v)
else:
if len(seq) >= min_seq_len:
best = max(best, sum(seq))
del seq[:]
if len(seq) >= min_seq_len:
best = max(best, sum(seq))
counts[best] += 1
return counts

def p_at_least(n_dice, bonus, the_min):
enough = 0
total = 0
for n, outcome in outcomes(n_dice).items():
total += n
if outcome + bonus >= the_min:
enough += n
return enough/total

print(p_at_least(7, 2, 5))


(2) It seems that your concern is whether $f(n, b + 1, t)$ or $f(n + 1, b, t)$ is bigger for a given $n$, $b$, and $t$, which a derivative won't tell you.

## Results for 1-10 dice

Tabular form (Google Sheets).

CthulhuTech was also asked about on rpg.se.

### How much flat bonus is a die worth?

Since I can pay a certain amount of skill points in order to raise either n or b, is it valid to take df/dt vs df/db to decide which option is better?

Everything here is discrete, so you can only really take a difference as Kodiologist said. Even then you would have to decide what sorts of target numbers you want to optimize for. Looking at the graph:

• Flat bonuses help you do easy things reliably.
• Extra dice give you a better chance at doing hard things.

Of course, the cost ratio between the two will come into play as well. For a simple rule of thumb I would say 1 die is comparable to a +2 flat bonus.

### Algorithm details

It is possible to compute the result in polynomial time by phrasing the mechanic as a state transition function with the inputs being how many dice rolled 1, how many rolled 2, and so forth. Even for 10 dice it takes longer to graph the result than to compute it.

def next_state(self, state, outcome, count):
score, run, prev_outcome = state or (0, 0, outcome - 1)
if count > 0:
set_score = outcome * count
run_score = 0
if outcome == prev_outcome + 1:
run += 1
else:
run = 1
if run >= 3:
# This could be the triangular formula, but it's clearer this way.
for i in range(run): run_score += (outcome - i)
score = max(set_score, run_score, score)
else:
# No dice rolled this number, so the score remains the same.
run = 0
return score, run, outcome


Here's a JupyterLite notebook that you can run in your browser. The underlying general-purpose algorithm is described here though I'm still working on a better explanation. I've implemented it in my Icepool Python package.