Jump to content

Formula for Opposed Rolls in MRQ2


fmitchell

Recommended Posts

My probability-fu is weak. I tried to derive a formula to calculate the probability of success (by whatever margin) in opposed percentile rolls:

the active roll is a critical and the resisting roll is not a critical,

or else the active roll is a regular success and the resisting roll is a failure,

or else both rolls are critical successes and the active roll is higher than the resisting roll,

or else both rolls are regular successes and the active roll is higher than the resisting roll,

So, using the MRQ2 definition of critical success (1/10 of skill, rounded up), I get:

Success = (P[a]/10)*(1-P[r]/10) + (P[a] - P[a]/10)*(1-P[r]) + Q(crit) + Q(reg)

where P[x] is the percentage for x, "a" is the Active roll, and "r" is the Resisting roll. Q(y) is the factor for the case where the active roll is greater than the resisting roll, and needs to be broken into two parts: the critical vs. critical case and the regular vs. regular case.

Unfortunately, when I try to express both values of Q, I keep getting different results however I derive it. (It's partly algebraic mistakes, I think.) When I do get a formula, I try match it to results I derived numerically (enumerate all possible active rolls and resisting rolls, and count all cases that denote a success).

FWIW, here are the numerical results I obtained, in increments of five:


Active/Resisting

      5     10     15     20     25     30     35     40     45     50     55     60     65     70     75     80     85     90     95   

 5  4.890  4.690  4.480  4.280  4.070  3.870  3.660  3.460  3.250  3.050  2.840  2.640  2.430  2.230  2.020  1.820  1.610  1.410  1.200 

10  9.890  9.540  9.080  8.630  8.170  7.720  7.260  6.810  6.350  5.900  5.440  4.990  4.530  4.080  3.620  3.170  2.710  2.260  1.800 

15 14.930 14.630 14.060 13.410 12.740 12.090 11.420 10.770 10.100  9.450  8.780  8.130  7.460  6.810  6.140  5.490  4.820  4.170  3.500 

20 19.930 19.630 19.060 18.260 17.340 16.440 15.520 14.620 13.700 12.800 11.880 10.980 10.060  9.160  8.240  7.340  6.420  5.520  4.600 

25 24.960 24.710 24.190 23.440 22.410 21.310 20.180 19.080 17.950 16.850 15.720 14.620 13.490 12.390 11.260 10.160  9.030  7.930  6.800 

30 29.960 29.710 29.190 28.440 27.410 26.160 24.780 23.430 22.050 20.700 19.320 17.970 16.590 15.240 13.860 12.510 11.130  9.780  8.400 

35 34.980 34.780 34.310 33.610 32.630 31.430 29.940 28.390 26.800 25.250 23.660 22.110 20.520 18.970 17.380 15.830 14.240 12.690 11.100 

40 39.980 39.780 39.310 38.610 37.630 36.430 34.940 33.240 31.400 29.600 27.760 25.960 24.120 22.320 20.480 18.680 16.840 15.040 13.200 

45 44.990 44.840 44.420 43.770 42.840 41.690 40.250 38.600 36.650 34.650 32.600 30.600 28.550 26.550 24.500 22.500 20.450 18.450 16.400 

50 49.990 49.840 49.420 48.770 47.840 46.690 45.250 43.600 41.650 39.500 37.200 34.950 32.650 30.400 28.100 25.850 23.550 21.300 19.000 

55 54.990 54.890 54.520 53.920 53.040 51.940 50.550 48.950 47.050 44.950 42.540 40.090 37.580 35.130 32.620 30.170 27.660 25.210 22.700 

60 59.990 59.890 59.520 58.920 58.040 56.940 55.550 53.950 52.050 49.950 47.540 44.940 42.180 39.480 36.720 34.020 31.260 28.560 25.800 

65 64.990 64.930 64.610 64.060 63.230 62.180 60.840 59.290 57.440 55.390 53.030 50.480 47.610 44.710 41.740 38.840 35.870 32.970 30.000 

70 69.990 69.930 69.610 69.060 68.230 67.180 65.840 64.290 62.440 60.390 58.030 55.480 52.610 49.560 46.340 43.190 39.970 36.820 33.600 

75 74.990 74.960 74.690 74.190 73.410 72.410 71.120 69.620 67.820 65.820 63.510 61.010 58.190 55.190 51.860 48.510 45.080 41.730 38.300 

80 79.990 79.960 79.690 79.190 78.410 77.410 76.120 74.620 72.820 70.820 68.510 66.010 63.190 60.190 56.860 53.360 49.680 46.080 42.400 

85 84.990 84.980 84.760 84.310 83.580 82.630 81.390 79.940 78.190 76.240 73.980 71.530 68.760 65.810 62.530 59.080 55.290 51.490 47.600 

90 89.990 89.980 89.760 89.310 88.580 87.630 86.390 84.940 83.190 81.240 78.980 76.530 73.760 70.810 67.530 64.080 60.290 56.340 52.200 

95 94.990 94.990 94.820 94.420 93.740 92.840 91.650 90.250 88.550 86.650 84.440 82.040 79.320 76.420 73.190 69.790 66.050 62.150 57.900 

Help?

Frank

"Welcome to the hottest and fastest-growing hobby of, er, 1977." -- The Laundry RPG
 
Link to comment
Share on other sites

I think your problem is that not all you cases are mutually exclusive.

It also seems to ingore the chance of the active player failing, which can happen statistically.

Edited by Atgxtg

Chaos stalks my world, but she's a big girl and can take of herself.

Link to comment
Share on other sites

I think your problem is that not all you cases are mutually exclusive.

I'm trying to account for double-counting -- (P[a] - P[a]/10), not (P[a]) -- but I'm probably missing something. (Then again, my candidate formulas have been coming in too low, so maybe I'm undercounting, or rounding down when I should go up.)

The verbal algorithm uses "or else" to indicate if one clause is true, then the others aren't considered. Unfortunately math doesn't work that way.

It also seems to ingore the chance of the active player failing, which can happen statistically.

I'm only counting cases where the active player succeeds, to get a total success probability. If the active player fails or fumbles, then by definition he doesn't succeed. (Even if the resisting roll fumbles, a failure is a failure.) Computing the chance of failure is an exercise for the reader.

Frank

"Welcome to the hottest and fastest-growing hobby of, er, 1977." -- The Laundry RPG
 
Link to comment
Share on other sites

Oh, here's the Ruby code that I used to generate the table, in case I made a mistake there:

#!/usr/bin/env ruby


require 'rational'


#

# Determine the probability of success in an opposed skill test

# through brute-force counting.

#

# pct_a - percentile value of active skill, 1...99

# pct_r - percentile value of resisting skill, 1...99

#

def opposed_prob(pct_a, pct_r)

  pct_a_crit = (0.10 * pct_a).ceil # critical for pct_a

  pct_r_crit = (0.10 * pct_r).ceil # critical for pct_r


  success = 0


  # Enumerate all possible rolls of two d100

  1.upto(100) do | roll_a |

    1.upto(100) do | roll_r |

      # count successful combinations

      if roll_a <= pct_a_crit then

        if roll_r > pct_r_crit then

          # Active rolled critical, resisting didn't

          success += 1

        elsif roll_a > roll_r then

          # Both criticals, active rolled higher

          success += 1

        end

      elsif roll_a <= pct_a then

        if roll_r > pct_r then

          # Active rolled success, resisting didn't

          success += 1

        elsif roll_a > roll_r

          # Both successes, active rolled higher

          success += 1

        end

      end

      # Active failed or fumbled => no success

    end

  end


  return Rational(success, 10000)

end


puts("Active/Resisting\n")

printf("%2s ", "")

1.upto(19) do |r|

  printf("%2s%2d%2s ", '', r * 5, '')

end

puts("\n")


1.upto(19) do |a|

  pct_a = a * 5

  printf("%2d ", pct_a)

  1.upto(19) do |r|

    pct_r = r * 5

    printf("%6.3f ", opposed_prob(pct_a, pct_r) * 100)

  end

  puts("\n")

end

Frank

"Welcome to the hottest and fastest-growing hobby of, er, 1977." -- The Laundry RPG
 
Link to comment
Share on other sites

Okay, since I don't have MRQ2, I want to make sure I understand how it works before I do some number crunching.

As far as I know the possible results are:

Active Crits, Resistant Crits= High Roll Wins (partially counted)

Active Crits, Resistant Succeeds= Active Wins

Active Crits, Resistant Fails= Active Wins

Active Crits, Resistant Fumbles= Active Wins

Active Succeeds, Resistant Crits= Resistant Wins (not counted)

Active Succeeds, Resistant Succeeds= High Roll Wins (partially counted)

Active Succeeds, Resistant Fails= Active Wins

Active Succeeds, Resistant Fumbles= Active Wins

Active Fails, Resistant Crits= Resistant Wins (not counted)

Active Fails, Resistant Succeeds= Resistant Wins (not counted)

Active Fails, Resistant Fails= High Roll Wins (partially counted)

Active Fails, Resistant Fumbles= Active Wins

Active Fumbles, Resistant Crits= Resistant Wins (not counted)

Active Fumbles, Resistant Succeeds= Resistant Wins (not counted)

Active Fumbles, Resistant Fails= Resistant Wins (not counted)

Active Fumbles, Resistant Fumbles= High Roll Wins (partially counted)

Note the cases I have in red boldface. If I understand how MRW2 works, then those cases are not included in you calculations and are at least partially responsible for the discrepancies.

Chaos stalks my world, but she's a big girl and can take of herself.

Link to comment
Share on other sites

MRQ2 rule is : "If both fail, then a stalemate has occured, forcing the opposed test to be rolled again at a later point".

Note that nothing is said in cases when both rolls are identical, which is common when both opponents rolled a crit.

Link to comment
Share on other sites

MRQ2 rule is : "If both fail, then a stalemate has occured, forcing the opposed test to be rolled again at a later point".

Yeah, that's why those cases aren't included. The cases in red, above, are mutual FAILURES. Not successes.

Note that nothing is said in cases when both rolls are identical, which is common when both opponents rolled a crit.

I noticed that too; nothing in the MRQ2 Core Book. However, the errata say "If a draw is rolled in an Opposed Test (both participants rolls exactly the same dice result and Level of Success) then the character with the highest score in the skill is considered the winner." I simply considered all ties as failures. (And what if both characters have exactly the same skill level?)

Frank

"Welcome to the hottest and fastest-growing hobby of, er, 1977." -- The Laundry RPG
 
Link to comment
Share on other sites

I simply considered all ties as failures. (And what if both characters have exactly the same skill level?)

Its all about gaining advantage and success. In the case of a successful ties (where both characters roll precisely the same dice result), then there are two possible ways of handling things:

1. Have them re-roll. The chances are, and biased towards the highest skilled character, that one will emerge with some advantage.

2. Rule that the character with the highest skill secures the advantage.

If you have two characters with identical skills both rolling the same success result, then, again, you could rule for a re-roll of the contest. Alternatively, you could rule that the character with the highest base value (ie, the sum/multiplier of the characteristics that determine initial values in the skill) secures the advantage.

I think, though, that these circumstances will further complicate the absolute math needed for your calculations.

The Design Mechanism: Publishers of Mythras

DM logo Freeforums Icon.png

Link to comment
Share on other sites

Last night I noticed that the Ruby program to generate probabilities with brute-force counting accidentally counted cases in which the Active roll was a success and the Resisting roll was a critical. I feel like an idiot. On the other hand, maybe that's why they were higher than the formula I worked out.

At any rate, I've corrected the problem and refactored the program to represent the rules more directly. I think I've also derived a workable equation, but I'll have to test it first.

FWIW, here are the corrected probabilities:

Active/Resisting

      5     10     15     20     25     30     35     40     45     50     55     60     65     70     75     80     85     90     95   

 5  4.850  4.650  4.410  4.210  3.980  3.780  3.560  3.360  3.150  2.950  2.740  2.540  2.330  2.130  1.920  1.720  1.510  1.310  1.100 

10  9.850  9.450  8.910  8.460  7.930  7.480  6.960  6.510  6.000  5.550  5.050  4.600  4.110  3.660  3.180  2.730  2.260  1.810  1.350 

15 14.840 14.590 13.800 13.150 12.360 11.710 10.930 10.280  9.510  8.860  8.100  7.450  6.700  6.050  5.310  4.660  3.930  3.280  2.560 

20 19.790 19.540 18.850 17.900 16.810 15.910 14.830 13.930 12.860 11.960 10.900 10.000  8.950  8.050  7.010  6.110  5.080  4.180  3.160 

25 24.770 24.570 23.890 23.190 21.750 20.650 19.310 18.210 16.880 15.780 14.460 13.360 12.050 10.950  9.650  8.550  7.260  6.160  4.880 

30 29.720 29.520 28.790 28.090 26.850 25.350 23.710 22.360 20.730 19.380 17.760 16.410 14.800 13.450 11.850 10.500  8.910  7.560  5.980 

35 34.690 34.540 33.820 33.170 31.940 30.790 28.700 27.150 25.260 23.710 21.830 20.280 18.410 16.860 15.000 13.450 11.600 10.050  8.210 

40 39.640 39.490 38.720 38.070 36.790 35.640 33.850 31.800 29.610 27.810 25.630 23.830 21.660 19.860 17.700 15.900 13.750 11.950  9.810 

45 44.600 44.500 43.740 43.140 41.870 40.770 38.990 37.390 34.650 32.650 30.210 28.210 25.780 23.780 21.360 19.360 16.950 14.950 12.550 

50 49.550 49.450 48.640 48.040 46.720 45.620 43.790 42.190 39.850 37.250 34.510 32.260 29.530 27.280 24.560 22.310 19.600 17.350 14.650 

55 54.510 54.450 53.650 53.100 51.790 50.740 48.920 47.370 45.040 42.990 39.600 37.150 34.160 31.710 28.730 26.280 23.310 20.860 17.900 

60 59.460 59.400 58.550 58.000 56.640 55.590 53.720 52.170 49.790 47.740 44.850 41.700 38.410 35.710 32.430 29.730 26.460 23.760 20.500 

65 64.420 64.390 63.550 63.050 61.700 60.700 58.840 57.340 54.970 52.970 50.090 47.590 43.550 40.650 37.110 34.210 30.680 27.780 24.260 

70 69.370 69.340 68.450 67.950 66.550 65.550 63.640 62.140 59.720 57.720 54.790 52.290 48.850 45.150 41.310 38.160 34.330 31.180 27.360 

75 74.330 74.320 73.440 72.990 71.600 70.650 68.750 67.300 64.890 62.940 60.020 57.570 54.140 51.190 46.500 43.150 39.060 35.710 31.630 

80 79.280 79.270 78.340 77.890 76.450 75.500 73.550 72.100 69.640 67.690 64.720 62.270 58.790 55.840 51.850 47.600 43.210 39.610 35.230 

85 84.240 84.240 83.320 82.920 81.490 80.590 78.650 77.250 74.800 72.900 69.940 67.540 64.070 61.170 57.190 53.790 48.450 44.650 40.010 

90 89.190 89.190 88.220 87.820 86.340 85.440 83.450 82.050 79.550 77.650 74.640 72.240 68.720 65.820 61.790 58.390 53.850 49.050 44.110 

95 94.150 94.150 93.190 92.840 91.370 90.520 88.540 87.190 84.700 82.850 79.850 77.500 73.990 71.140 67.120 63.770 59.240 55.390 49.400 

Frank

"Welcome to the hottest and fastest-growing hobby of, er, 1977." -- The Laundry RPG
 
Link to comment
Share on other sites

Ah, if only each side's rolls were Independent then it would be so much simpler... ;)

Britain has been infiltrated by soviet agents to the highest levels. They control the BBC, the main political party leaderships, NHS & local council executives, much of the police, most newspapers and the utility companies. Of course the EU is theirs, through-and-through. And they are among us - a pervasive evil, like Stasi.

Link to comment
Share on other sites

I think I'm there, or at least close enough for government; error is < 1.5%. This after a whole bunch of screwed up algebra, geometric arguments, and triple-checking my work. I also refactored the numerical procedure, to reduce duplication and to mirror the rules more closely.

Here's the full program:

#!/usr/bin/env ruby


require 'rational'


STEP = 10


LEVEL_CRIT =  3

LEVEL_SUCC =  1

LEVEL_FAIL =  0

LEVEL_FUMB = -1


#

# Categorize a roll as a critical success, regular success,

# failure, or fumble.

#

# pct  - percentile skill, 1...99

# roll - actual roll of resisting skill, 1...99

#

# returns LEVEL_CRIT, LEVEL_SUCC, LEVEL_FAIL, LEVEL_FUMB

#

def success_level(pct, roll)

  pct_crit = (0.10 * pct).ceil # critical value

  if (roll == 100) or (roll == 99 and pct >= 100) then

    return LEVEL_FUMB

  elsif roll > pct then

    return LEVEL_FAIL

  elsif roll > pct_crit then

    return LEVEL_SUCC

  else

    return LEVEL_CRIT

  end

end


#

# Determine the probability of success in an opposed skill test

# through brute-force counting.

#

# pct_a - percentile value of active skill, 1...99

# pct_r - percentile value of resisting skill, 1...99

#

# returns Rational with value between 0 and 1.

#

def opposed_prob_count(pct_a, pct_r)

  success = 0


  # Enumerate all possible rolls of two d100

  1.upto(100) do | roll_a |

    1.upto(100) do | roll_r |

      # compare success levels

      level_a = success_level(pct_a, roll_a)

      level_r = success_level(pct_r, roll_r)


      if level_a > LEVEL_FAIL then

        if level_a > level_r then

          success += 1

        elsif level_a == level_r then

          # compare die rolls

          if (roll_a > roll_r) then

            success += 1

          elsif (roll_a == roll_r) and (pct_a > pct_r) then

            success += 1

          end

        end

      end

      # If active fails or fumbles, never a success

    end

  end


  return Rational(success, 10000)

end


#

# Determine the probability of success in an opposed skill test

# mathematically.

#

# pct_a - percentile value of active skill, 1...99

# pct_r - percentile value of resisting skill, 1...99

#

# returns Rational with value between 0 and 1.

#

def opposed_prob_math(pct_a, pct_r)

  prob_a = Rational(pct_a, 100)

  prob_r = Rational(pct_r, 100)


  # Clear successes: crit vs. non-crit, success vs. failure

  result = prob_a - Rational(91, 100) * prob_r * prob_a


  # Successes based on one die roll over another

  if pct_a <= pct_r then

    result += (101 * prob_a * prob_a + prob_r * prob_r - 20 * prob_a * prob_r) / 200

  else

    result += prob_a * prob_r * 82 / 100 - \

               (101 * prob_r * prob_r + prob_a * prob_a - 20 * prob_r * prob_a) / 200

  end


  return result

end


puts("Active/Resisting\n")

printf("%2s ", "")

1.upto(100/STEP - 1) do |r|

  printf("%2s%2d%2s ", '', r * STEP, '')

end

puts("\n")


1.upto(100/STEP - 1) do |a|

  pct_a = a * STEP

  printf("%2d ", pct_a)

  1.upto(100/STEP - 1) do |r|

    pct_r = r * STEP

    printf("%6.3f ", opposed_prob_math(pct_a, pct_r) * 100)

  end

  puts("\n")

  printf("%2s|", '')

  1.upto(100/STEP - 1) do |r|

    pct_r = r * STEP

    math = opposed_prob_math(pct_a, pct_r)

    count = opposed_prob_count(pct_a, pct_r)

    printf("%6.3f|", ((count - math)/count) * 100)

  end

  puts("\n")

end

Frank

"Welcome to the hottest and fastest-growing hobby of, er, 1977." -- The Laundry RPG
 
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...