Here's a script using my Icepool Python library. Rather than considering entire sets of rolls at once, Icepool uses the strategy of updating a running total based on how many dice in each pool rolled each number.
import icepool
from icepool import d6, EvalPool
class AllActionStress(EvalPool):
def next_state(self, state, outcome, action, stress):
# This is called for each possible outcome 6, 5, 4, 3, 2, 1
# with the number of action and stress dice that rolled that number.
# This method uses this information to update a "running total".
# action_set: The set of surviving action dice.
# leftover_stress: The number of surviving stress dice that rolled
# this number or higher.
action_set, leftover_stress = state or ((), 0)
leftover_stress += stress
if leftover_stress >= action:
leftover_stress -= action
else:
action -= leftover_stress
action_set += (outcome,) * action
leftover_stress = 0
return action_set, leftover_stress
def final_outcome(self, final_state, *pools):
# Just return the action set; any remaining stress is ignored.
return final_state[0]
def direction(self, *_):
# See outcomes in descending order.
return -1
all_action_stress = AllActionStress()
result = all_action_stress.eval(d6.pool(5), d6.pool(3))
# The number of surviving action dice.
# If this is all you are interested in, it would be more efficient to only
# count the number of surviving action dice in AllActionStress, rather than
# computing the entire surviving action sets and counting the length at the
# end as we do here.
print(result.sub(len))
Denominator: 1679616
Outcome |
Weight |
Probability |
2 |
1097586 |
65.347437% |
3 |
432960 |
25.777321% |
4 |
132915 |
7.913416% |
5 |
16155 |
0.961827% |
which matches posita's results.
result
contains the distribution of full surviving action sets (a la "player 1 is left with 3 action dice: 6, 3, 3") but the number of possible such sets grows rapidly with number of action dice (e.g. this example results in 252 rows), so I won't reproduce the table here.
You can try the script in your browser here.
Efficient computation
The trouble with AnyDice is that it works by enumerating all possible multisets of numbers rolled. While this is faster than enumerating all ordered (equivalently, unsorted) rolls and is convenient in that it gives you the entire rolled sequence at once, the number of possible multisets can still grow quickly as you add dice. Mixing different dice also makes things much more difficult.
Fortunately, it turns out that, as long as you can express the dice mechanic in terms of an incremental calculation where you're told how many dice rolled each number in each pool for one number at a time without carrying too much information from number to number, it's possible to compute the solution more efficiently. It's also possible to handle mixed standard dice without much loss in efficiency. I've implemented this approach in my Icepool Python package. Here's a script:
import icepool
from icepool import d4, d6, d8, d10, d12, EvalPool
class GreenRed(EvalPool):
def next_state(self, state, outcome, green, red):
# State is the number of top-two dice for green and red.
top_green, top_red = state or (0, 0)
# If there are remaining places in the top two...
remaining_top_two = 2 - (top_green + top_red)
if remaining_top_two > 0:
# Compute the number of non-eliminated dice that rolled this outcome.
net = green - red
# Then add them to the winning team's top two.
if net > 0:
top_green += min(net, remaining_top_two)
elif net < 0:
top_red += min(-net, remaining_top_two)
return top_green, top_red
def final_outcome(self, final_state, *pools):
top_green, top_red = final_state
if (top_green > 0) and not (top_red > 0): return 2
elif (top_red > 0) and not (top_green > 0): return 0
else: return 1
def direction(self, *_):
# See outcomes in descending order.
return -1
green_red = GreenRed()
# The argument lists are implicitly cast to pools.
print(green_red.eval([d10, d8], [d6, d8]))
Denominator: 3840
Outcome |
Weight |
Probability |
0 |
265 |
6.901042% |
1 |
2784 |
72.500000% |
2 |
791 |
20.598958% |
We can handle even much larger pools. Here's two entire dice sets (10 dice total) on each side:
print(green_red.eval([d12, d10, d8, d6, d4, d12, d10, d8, d6, d4], [d12, d10, d8, d6, d4, d12, d10, d8, d6, d4]))
Denominator: 281792804290560000
Outcome |
Weight |
Probability |
0 |
67701912081930556 |
24.025423% |
1 |
146388980126698888 |
51.949155% |
2 |
67701912081930556 |
24.025423% |
You can try this in your browser using this JupyterLite notebook. Fair warning, though, I'm currently doing a major revision to the package.
Best Answer
I will leave it to others with superior knowledge of AnyDice to propose/adapt a solution for that platform. That being said, I adapted my
dyce
¹-based solution to this question (mentioned by @HighDiceRoller) to augment my prior response to your cited NCO question to explore a comparison of your mod against the base mechanic. It's showcased in the last cell of that notebook and the implementation is captured in thenco_so_dangerous
function inneon_city_overdrive.py
. I'm sure the implementation could be optimized, but I focused on readability. I also made several assumptions, including:In other words, in response to @HighDiceRoller's question above, Action [6, 1] vs. Danger [6, 6, 5] results in 0. (First danger six cancels the action six, second danger six cancels the action one, the remaining danger available to work against the result is five (i.e., not a six).
Even so, your proposed mod appears substantially more dangerous than the base mechanic where the danger pool size meets or exceeds the action pool size. While a matter of taste, I find
anydyce
's² "burst" graphs are well-suited for visualizing the differences. (Screenshot for select comparisons below. Gray outer rings show the base mechanic. Red inner rings show your dangerous mod.)You can see a more generalized version in action (and play around with it) in your browser: [source]
Note that you might have to scroll down to the section that addresses this particular question. Limitations and caveats mentioned elsewhere apply.
¹
dyce
is my Python dice probability library.²
anydyce
is my visualization layer fordyce
meant as a rough stand-in for AnyDice.