Ironsworn meets BitD dice mechanic (with a twist)

diceironswornstatistics

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.

Best Answer

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: Try dyce [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.

anydyce burst graphs

¹ dyce is my Python dice probability library.

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

Related Topic