Ironsworn meets BitD dice mechanic (with a twist)

I'd like to find the probabilities of a mechanic inspired mainly by Ironsworn.

The basic premise is pretty simple and I have a working anydice program that gives me the right results but there's an additional rule I can't figure out how to implement.

The mechanic is this

1. You roll xd6 and take the highest result

2. You compare your highest result to two "challenge" d6s that act as target numbers to beat, if you roll over that's a success, if you roll under or equal that's a failure (this generates an output of 0, 1, or 2 successes)

3. (the extra rule I don't know how to program) In the event of a tie if your highest result has more matches than ties against the challenge dice you generate that many successes.

Examples

6, 6 vs 6, 6=0 success

6, 6, 6 vs 6, 6=1 success

6, 6, 6, 6 vs 6, 6=2 successes

6, 6 vs 1, 6=2 successes (6 beats 1 and the 2 6's beats 1 challenge 6)

As a possible add-on I was considering using the same mechanic basically for a crit system, where having the highest dice match generates extra successes (after they're used to break ties first). This would basically be adding one more output for a roll of 3+ if anyone would like to help with that.

• Something that might be easier to program and also draws from Blades: "if you roll 2 6s you can combine them into a critical die, which always succeeds". Jul 28, 2022 at 23:20
• "You roll xd6 and take the highest result" what does that mean? If you roll 3, 4, 6 do you take just one 6? What about 3, 5, 5 - is that a 5, 5?
– VLAZ
Jul 29, 2022 at 9:46
• "a crit system, where having the highest dice match generates extra successes (after they're used to break ties first)" I'm not sure that tracks. It seems you're saying that if your roll is 5, 5 vs 1, 4 then this is a crit which generates extra successes. Yet that's already 2 successes which is the maximum you can have. Would it apply to, say 6, 6, 6 vs 6, 6 = 2 successes because there is a pair and the tie breaks in roller favour? Otherwise for 6, 6, 6, 6 vs 6, 6= 2 successes has the same problem as 5, 5 vs 1, 4. If more successes are allowed, is that also 6, 6, 6 vs 1, 1 = 3 successes?
– VLAZ
Jul 29, 2022 at 10:17
• Significant discussion in chat expounds on some of the specifics of the rolling scheme in question. Jul 29, 2022 at 20:30
• A couple other opposed-sort-and-cancel questions: Neon City Overdrive, Anydice: two-colored polyhedral dice pool. Jul 30, 2022 at 2:25

My AnyDice Fu is lacking, so this is a dyce¹-based solution, but I believe is otherwise directly responsive and captures my understanding of the mechanic, although I'm not sure I've understood everything completely.

from dyce import P
from dyce.p import RollT

def ironsworn_bitd(action_roll: RollT, challenge_roll: RollT) -> int:
max_action = max(action.roll, default=0)
max_challenge = max(challenge.roll, default=0)
if max_action == 0 and max_challenge == 0:
return 0
basic_successes = sum(1 for c in challenge_roll if c < max_action)
num_action_matches = sum(1 for a in action_roll if a == max_action) - 1
if max_action < max_challenge:
match_successes = 0
elif max_action > max_challenge:
match_successes = num_action_matches
else:  # max_challenge == max_action:
num_challenge_matches = sum(1 for c in challenge_roll if c == max_action) - 1
# Offset the number of action matches by the number of challenge matches up to
# the number of action matches
match_successes = max(0, num_action_matches - num_challenge_matches)
return basic_successes + match_successes

h = P.foreach(ironsworn_bitd, action_roll=4@P(6), challenge_roll=2@P(6))
print(h.format(scaled=True))


Output:

avg |    1.66
std |    0.92
var |    0.85
0 |  10.95% |############
1 |  29.67% |#################################
2 |  43.74% |##################################################
3 |  13.53% |###############
4 |   1.99% |##
5 |   0.12% |


You can play around with a more generalized version in your browser: [source]

The above favors readability and consistency with the original sentiment. It can certainly be simplified. To limit the successes to 3, change the statement return basic_successes + match_successes to return min(3, basic_successes + match_successes) and re-run it.

While a matter of taste, I find anydyce's² "burst" graphs are well-suited to visualizing distributions.

¹ dyce is my Python dice probability library.

² anydyce is my visualization layer for dyce meant as a rough stand-in for AnyDice.

So, if I understand your mechanic right:

1. You roll Xd6. Let the highest result of this roll be M, and let the number of times this result appears in the roll be N. (Thus, we always have 1 ≤ M ≤ 6 and 1 ≤ NX.)

Examples (with the highest roll and its matches in bold):

Roll Results M N
3d6 1, 2, 3 3 1
3d6 1, 3, 3 3 2
3d6 3, 3, 3 3 3
3d6 3, 3, 4 4 1
3d6 2, 3, 4 4 1
6d6 1, 2, 3, 4, 5, 6 6 1
6d6 6, 6, 6, 6, 6, 6 6 6
2. You then roll two challenge dice (both d6). Let the lower result rolled on these dice be L, and the higher one H. (Thus, we always have 1 ≤ LH ≤ 6.)

3. You count successes as follows:

• If M > L, count one success.
• If M > H, count another success in addition to the first one.
• If M = H = L and N > 2, add extra N − 2 successes (one for each of the N dice matching H, minus the two used to "cancel out" H and L).
• If M = H > L and N > 1, add extra N − 1 successes (one for each of the N dice matching H, minus the one used to "cancel out" H).

(These last two cases being the "extra rule" that you don't know how to implement in AnyDice.)

With all that written out, we can actually translate it pretty straightforwardly into AnyDice code:

function: roll ROLL:s vs CHALLENGE:s {
M: 1@ROLL
N: M = ROLL
H: 1@CHALLENGE
L: 2@CHALLENGE

S: (M > L) + (M > H)
if M = H & H = L & N > 2 { S: S + N - 2 }
if M = H & H > L & N > 1 { S: S + N - 1 }
result: S
}

loop X over {1..6} {
output [roll Xd6 vs 2d6] named "[X]d6"
}


The output of this program looks like this (in graph mode):

Ps. Here's a slightly more general version of the function above that supports an arbitrary number of challenge dice instead of always just two:

function: roll ROLL:s vs CHALLENGE:s {
MAX: 1@ROLL
S: MAX > CHALLENGE
if MAX = 1@CHALLENGE {
S: S + [highest of 0 and (MAX = ROLL) - (MAX = CHALLENGE)]
}
result: S
}


Here, MAX > CHALLENGE counts the successes from the "basic" mechanic (i.e. one success for each challenge die lower than your highest roll), while the code inside the if block implements your "extra rule" that adds additional successes if your highest roll matches the highest challenge die and appears more times in your rolls than among the challenge die.

(The [highest of 0 and ...] function is used to ensure that we never add negative successes if you e.g. roll 1, 2, 6 vs. a challenge of 6, 6!)

For example, here's what the output looks like with three challenge dice instead of two:

• Nice! Regarding your "generalized" solution, I could be wrong, but I think the OP intended to have "matches" from the action dice also count as successes against any equal or lesser challenge dice if the max action die was greater than the max challenge die. In other words, 6, 6, 6 vs 1, 3 would count as four successes? I still don't understand why 6, 6 vs. 1, 1 and 6, 6, 6 vs. 1, 1 each result in three successes, but maybe the OP can weigh in here. Jul 30, 2022 at 13:58
• 6,6vs1,1 should be 3 and 6,6,6vs1,1 should be 4, I might have accidentally said 3 prior when I meant >=3. But yes, they also count as extra successes if they're greater than the max challenge die. Jul 30, 2022 at 15:50