3
\$\begingroup\$

I'm experimenting with the probabilities of various "die pool" attack resolutions for a game I'm designing, knowing that pools will generally be 1-4 dice, with sacrifices to temporarily boost a die pool.

The problem I'm having is the following:

  • highest attacker die > highest defender die draws very often, and attacks rarely go through. Boosting offers only minor advantages at 2-4 dice. Stakes don't feel heightened.
  • highest attacker die >= highest defender die draws very often, so attacks often succeed. Boosting is also lackluster at 2-4 dice.

To remedy this, I'm thinking of the following resolution: first non-draw high die wins: 6, 5 wins vs 6, 3, and both win against 6, since there are dice left over. Simply put, draws are taken out of the equation.

How would I model this in anydice (no sacrifices, just [nondraw 5] > [nondraw 4]-like situations)?

\$\endgroup\$
  • \$\begingroup\$ Note that your proposed mechanic does not totally remove draws from the equation, as in the case that two dice pools of the same size throw an identical result, there is still no clear winner; that is rather unlikely though. \$\endgroup\$ – Carcer Dec 5 '17 at 21:11
  • \$\begingroup\$ Similar earlier question: rpg.stackexchange.com/questions/62589/… \$\endgroup\$ – Ilmari Karonen Dec 5 '17 at 23:33
  • \$\begingroup\$ @IlmariKaronen It's similar, and I've checked it out, but ultimately this is simpler and Carcer answered it. Draws will be used somehow else. \$\endgroup\$ – Alex Mitan Dec 6 '17 at 9:54
6
\$\begingroup\$

Just use sequence comparison.

The documentation could explain this better, but it turns out (with an assumption about your intention) this is really trivial to do with Anydice's built in comparisons. In the documentation about sequences:

Compared to a sequence

The sequences are compared number by number, from left to right, resulting in either a 1 or a 0.

Experimentation shows this means it actually iterates through the sequences, comparing the Nth value of the first to the Nth value of the other. If we have two sequences, A and B, then A > B iterates through the sequence:

  1. returns 1 if the Nth value of A is greater than the Nth value of B

  2. returns 0 if the Nth value of A is less than the Nth value of B

  3. goes on to the N+1th value if they are equal

It also returns 1 if B runs out of elements before A or 0 if A runs out of elements before or at the same time as B.

So if you compare the sequences:

{6,4,3,2} > {6,4,2,1} : 1

{6,4,3,2} > {6,4,3,3} : 0

{6,4,3,2} > {6,4,3} : 1

{6,4,3} > {6,4,3,1} : 0

{6,6,5} > {6,5,5,1} : 1

You can do this sort of comparison with dice if you cast them to sequences using a function before you compare them.

function: compare ATTACK:s to DEFENCE:s {
  result: ATTACK > DEFENCE
}

output [compare 4d6 to 3d6]

When the function compare ATTACK to DEFENCE is invoked, the ATTACK:s and DEFENCE:s in the declaration convert whatever arguments are provided into a sequence. When a dice pool is converted into a sequence, it becomes a sequence representing the results of rolling that dice pool, sorted from high to low (and the function is invoked once for every possible result of the roll and the results statistics'd together).

This program takes dice pools and does a bit of sequence comparison to tell you which one wins under these circumstances. This does assume that your rule is that 6,6,5 beats 6,5,5, i.e. that dice cancel each other out on a 1:1 basis; if you intended differently, this becomes much more complex to describe and Anydice starts to not be very efficient.

But I want to count the number of draws!

Unfortunately, sequence comparison won't let you do that. But if you write your own loop iterator manually, it will run less efficiently than anydice's built in comparison (so you are limited to smaller dice pools before anydice gives up on the program) but you can get this information:

function: compare ATTACK:s to DEFENCE:s {
  loop N over {1..[lowest of #ATTACK and #DEFENCE]} {
    if N@ATTACK > N@DEFENCE { result: N }
    if N@ATTACK < N@DEFENCE { result: -N }
  }
  if #ATTACK > #DEFENCE {result: #DEFENCE +1}
  if #ATTACK < #DEFENCE {result: -#ATTACK -1}
  result: 0
}

Basically, we loop over the length of the shortest array doing the element by element comparison, and when we get a victory or fail we return N for success (or -N for fail). This means we reached the Nth element of the array before we got a result and therefore N-1 pairs of dice were cancelled. Then there's the extra bit of logic on the end which works out who wins if all the dice it was able to compare were draws (and ensures that the result is consistent with the above, so subtract 1 from the absolute value to work out how many dice pairs were cancelled).

This program implements the above code. In my testing it can do up to 5d6 vs 5d6 before it starts taking too long for anydice to execute (as you expect dice pools in the 1-4 range, hopefully that's still useful for you).

\$\endgroup\$
  • \$\begingroup\$ No, this is actually exactly what I wanted, and it matches my "do it 16000 times" implementation in code! Thank you for the elegant solution. \$\endgroup\$ – Alex Mitan Dec 6 '17 at 9:53
  • \$\begingroup\$ Is there any way to count how many dice were actually eliminated? \$\endgroup\$ – Alex Mitan Dec 6 '17 at 9:56
  • \$\begingroup\$ @AlexMitan: unfortunately not when you use the built-in sequence comparison. You could write more complicated code that does the comparison element by element manually and output a result including information on the cancelled dice somehow, but it becomes really inefficient. \$\endgroup\$ – Carcer Dec 6 '17 at 19:51
  • \$\begingroup\$ @AlexMitan but not to be disheartened, because you can write your own comparison function which will encode this information, as my edit describes. \$\endgroup\$ – Carcer Dec 6 '17 at 20:19
  • \$\begingroup\$ It's easier, overall, to just model this process in R or Python and gather and represent statistics from there, but thank you very much! Anydice is good for sharp, complete probabilities whereas programming works for more detailed, unique polishing, in my view. \$\endgroup\$ – Alex Mitan Dec 6 '17 at 21:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.