Anydice can almost do this
There are three main parts to doing this in Anydice (or any system really): using a mixed dice pool, testing for two highest, and eliminating pairs of dice.
Using mixed dice isn't too hard, we just have to cast each type as one sequence and then combine those. Basically, our function have to take multiple A:s
parameters which we then make into one pool as GREEN: [sort {A, B}]
. That means the number of different possible dice will be part of the function definition, but it's fairly easy to expand and quickly turns into a non-issue.
To test the success/failure conditions, we can recognize that the highest two being green is equivalent to the 2nd highest green being greater than the highest red. And vice versa. If neither of those are true, then the highest two must be of mixed colours.
The difficult step with Anydice is dealing with eliminations. I can't find a way around it which works with your success/failure condition, but it is doable. The trick is to build new sequences with the surviving dice, over manipulating the existing sequences. The big issue we're running into here is computation time. Anydice is limited to 5 seconds, and we rapidly hit that with too large pools (depends on number and size of dice). To help cut computation down, we can test success/failures as often as possible, but even then it struggles around 5-6 dice.
In the following implementation, failure is 0, complication 1 and success 2.
function: elimpool A:s B:s vs C:s D:s
{
INITGREEN: [sort {A, B}]
INITRED: [sort {C, D}]
if 2@INITGREEN > 1@INITRED {result:2}
if 2@INITRED > 1@INITGREEN {result:0}
GREEN: {}
RED: {}
loop X over [reverse {1..[highest of 1@INITGREEN and 1@INITRED]}]
{
DIFF: [count X in INITGREEN]-[count X in INITRED]
GREEN: {GREEN, X:DIFF}
RED: {RED, X:-DIFF}
if 2@GREEN > 1@RED {result:2}
if 2@RED > 1@GREEN {result:0}
}
GREEN: [sort GREEN]
RED: [sort RED]
if #RED = 0 & #GREEN = 0 {result: 1}
if #RED = 0 {result: 2}
if #GREEN = 0 {result: 0}
if 2@GREEN > 1@RED {result:2}
if 2@RED > 1@GREEN {result:0}
result: 1
}
output [elimpool 1d10 1d8 vs 1d6 1d8]
Monte-Carlo
If you're willing to sacrifice finding an analytical solution, running Monte-Carlo simulations is fairly straight forward (assuming you can do some basic coding). The basic premise is to write a function which actually does all the rolling and manipulation that a player would, then run that enough times to get a good approximation.
In principle we need those same three parts, but we get a fair bit more freedom since we're now using a fully fledged language. By preference I like giving mixed pools to be rolled as a list and then calling the roll function for each element, the success logic is exactly the same (if we set the same sort order), and if we're careful with the logic we can remove elements from lists (assuming our language supports that).
Anyway, you can use my Python code either as is or as inspiration. It produces same (or close enough given the inherent variance) results as Anydice, but it'll deal with as large dice pools as you're willing to give it CPU time. And if you want higher accuracy, just increase the count
parameter at the cost of same.
import random
def d(N):
return random.randint(1, N)
def RollPool(greenDice, redDice):
greenRoll = list(map(d, greenDice))
greenRoll.sort(reverse=True)
redRoll = list(map(d, redDice))
redRoll.sort(reverse=True)
if greenRoll[1] > redRoll[0]: return 2
if redRoll[1] > greenRoll[0]: return 0
i = 0
while i < len(greenRoll) and i >= 0:
j = 0
while j < len(redRoll) and j >= 0:
if greenRoll[i] == redRoll[j]:
greenRoll.pop(i)
redRoll.pop(j)
i -= 1
j = -2
else:
j+=1
i+=1
if len(greenRoll) == 0 and len(redRoll) == 0:
return 1
if len(redRoll) == 0:
return 2
if len(greenRoll) == 0:
return 0
if len(redRoll) >= 2:
if redRoll[1] > greenRoll[0]: return 0
if len(greenRoll) >= 2:
if greenRoll[1] > redRoll[0]: return 2
return 1
def runMC(greenDice, redDice, count):
data = [-1]*count
for i in range(count):
data[i] = RollPool(greenDice, redDice)
return data.count(0)/count, data.count(1)/count, data.count(2)/count
print(runMC([10, 8], [6, 8], 100000))