# How can I prevent small numerical advantages from dominating the balance of an encounter?

I've been tinkering for some time with a game and I'm having a fair amount of trouble with something:

I have two characters, each of whom have attributes (about ten) in a range (between 1 and 20). I want to use these attributes to generate a 'roll' such that the higher roll wins that particular encounter. It's worth noting that the two characters are not damaging/defending each other. They're both rolling to see if they pass what I guess we could call a skill check. They're both rolling to pass/fail against a common value. They do not interact with one another.

However, when one of the characters has even a small numerical advantage, any formula I've come up with results in the ever-so-slightly superior one winning a huge majority of the time. This is undesirable.

I've tried weighting the 'most relevant' attribute for the test at 80% and the sum of the other attributes at 20%. I also tried comparing averages to produce a relative difference and using that to boost the weaker character. Both approaches resulted in the significant advantages I'm trying to remove (for example, if I run the encounter 5,000 times, it quite regularly produces one side winning all 5,000).

Adding a "luck" component only matters, it seems, if it's weighted somehow in favour of the lesser character, and I haven't hit a good balance there.

What approaches can I take to blunt the impact of a small numerical advantage but still preserve and increase that advantage as the relative gap in attributes increases?

Per the request, here are the specifics I have so far. Some things I haven't figured out yet so they remain generalities:

At the moment, the roll is generated as

0.8 * (mainAttribute) + 0.2 (1/3 * subAttA + 1/3 * subAttB * 1/3 subAttC)


At present, this produces numbers in the neighborhood of 4.0. Attributes are randomly generated between specified ranges. The current test uses one character with attributes from 2 to 4, and the opponent between 3 and 5. Predictably, this produce averages close to 3 and 4 respectively.

With this one-point advantage, I'd like to see the stronger of the two win in the area of 55% to 60% of the time, with this scaling up to winning about 80% of the time with an average attribute advantage of 5 or 6, 90% at advantages of 7 or 8, leaving some room for an unlikely win when the gap grows larger. I'd prefer not to ever have guaranteed wins, but perhaps things becoming very unlikely - to the tune of winning 99.5% or 99.6% of the time when the gap gets very large.

The current formula produces a non-random number. Randomness comes from the selection of which attributes are relevant. Not all of the attributes are used for each roll. It's possible for the one with the overall weaker attributes to be stronger in the areas relevant to that roll, and steal a win. But, predictably, it happens rarely.

My next attempt was to weigh their relative strengths, by taking an average of all of each's stats, dividing them against each other, and using that value to give a small boost to the lesser character. This smoothed things out a little, but still had a pronounced tendency to produce things like 5,000 wins for one guy out of 5,000 tries.

• You say the "role is generated" but then you post a formula which will always generate a fixed number. Where is the randomness? Commented Jul 18, 2016 at 16:48
• So if I understand you correctly, the only randomness in your game mechanic is the random pick of the main attribute? Commented Jul 18, 2016 at 17:05
• But as @Philipp implies, 5000 attempts will yield the exact same results then? Or do you generate new attributes each simulation Commented Jul 18, 2016 at 17:49
• How exactly does one of the two win, if they are not interacting with each other? There seems to be some data missing here?
– Erik
Commented Jul 18, 2016 at 19:42
• The roll each one produces is compared to a goal they're required to reach. If one reaches it and the other doesn't, that one wins. If they both reach it, the higher of the two wins. If neither reaches, neither gets the point. In the unlikely even of a tie, they split the point. By 'not interacting', I meant not hitting or defending against one another in the traditional sense, as that's where some of the initial discussion was headed. Commented Jul 18, 2016 at 20:01

## 8 Answers

The problem with your approach is that you decide the result of the combat the moment you decide on the main stat. When you have 4 main stats, and the fighter is only better in one of them, their win-chance is always 1 in 4, no matter how large the differences actually are. When you want more fine-grained results, you need more fine-grained randomness.

First of all, I think you can keep your random pick for the main attribute and you can also keep your formula if you want to. It is the number which represents how much of an edge this combatant has in this specific encounter. For the rest of this post, I will refer to this as just power.

A method which I used in quite a lot of games and which served me quite well when it comes to a duel between two things with a certain power, is to roll a random floating-point number between 0 and power for both and see who rolled higher. Here is a list of the expected results of this method. The percentages are not calculated but generated experimentally by running 100000 fights per combination and iteration count and counting who won how often:

PowerA | PowerB | Win chance of A
9    |   1    |    94.5%
8    |   2    |    87.5%
7    |   3    |    78.6%
6    |   4    |    66.6%
5    |   5    |    50.0%
4    |   6    |    33.3%
3    |   7    |    21.5%
2    |   8    |    12.5%
1    |   9    |    5.5%


The nice thing about this algorithm is that it scales no matter how large the numbers are you are dealing with. The chance of 0.3 vs 0.7 is the same as that of 3 vs 7, 300 vs. 700 or 3,000,000,000 vs. 7,000,000,000.

When this is still too unpredictable for your taste, you can make the combat more predictable by rolling multiple random numbers for each combatant and add them up. Due to the law of large numbers, many random events will even out and result in more predictable results. Here is a table with different number of iterations.

| A | B | Iterations
|   |   |       1 |     2 |     3 |     4 |     5 |     6 |     7 |     8 |     9 |
-----------------------------------------------------------------------------------
| 9 | 1 |   94.5% | 99.3% | 99.9% |100.0% |100.0% |100.0% |100.0% |100.0% |100.0% |
| 8 | 2 |   87.4% | 96.3% | 98.8% | 99.5% | 99.8% |100.0% |100.0% |100.0% |100.0% |
| 7 | 3 |   78.7% | 89.2% | 94.0% | 96.6% | 97.8% | 98.9% | 99.2% | 99.6% | 99.7% |
| 6 | 4 |   66.8% | 74.3% | 79.2% | 82.9% | 85.7% | 88.0% | 89.9% | 91.2% | 92.5% |
| 5 | 5 |   50.0% | 50.0% | 50.0% | 50.0% | 50.0% | 50.0% | 50.0% | 50.0% | 50.0% |
| 4 | 6 |   33.6% | 25.6% | 20.9% | 17.1% | 14.7% | 12.0% | 10.2% |  8.9% |  7.5% |
| 3 | 7 |   21.4% | 10.7% |  6.0% |  3.5% |  2.0% |  1.2% |  0.7% |  0.4% |  0.3% |
| 2 | 8 |   12.7% |  3.7% |  1.2% |  0.4% |  0.1% |  0.1% |  0.0% |  0.0% |  0.0% |
| 1 | 9 |    5.5% |  0.7% |  0.1% |  0.0% |  0.0% |  0.0% |  0.0% |  0.0% |  0.0% |


The results of 100% and 0% in the above table are an illusion due to rounding differences. Unless the power of a combatant is exactly 0 there is always the possibility that they win. It just didn't happen in the above test, so you can expect it to be below 1:100000.

You might also notice some slight irregularities which can be attributed to mood swings of java.lang.Random and might not appear when you run the code again with a different seed.

The program I used to generate this table (Java).

public class Main {

private static Random random = new Random();
private static final int SAMPLES = 100000;

public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
double powerA = 10.0 - i;
double powerB = i;
System.out.print("| ");
System.out.print((int)powerA);
System.out.print(" | ");
System.out.print((int)powerB);
System.out.print(" |   ");

for (int iterations = 1; iterations < 10; iterations++) {
int wins = 0;
for (int j = 0; j < SAMPLES; j++) {
if (fight(powerA, powerB, iterations)) wins++;
}
System.out.print(String.format("%2.1f", 100.0 * (double)wins / (double)SAMPLES));
System.out.print("% | ");
}
System.out.print("\n");
}
}

private static boolean fight(double powerA, double powerB, int iterations) {
double sumA = 0.0f;
double sumB = 0.0f;
for (int i = 0; i < iterations; i++) {
sumA += random.nextDouble() * powerA;
sumB += random.nextDouble() * powerB;

}
return sumA > sumB;
}
}


If you would like to use this code in your game, it is licensed under the WTF Public License Version 2 as published by Sam Hocevar.

• This is an interesting approach. In some of my attempts, I went sort of halfway to this. I'll wire this up and give it a try. Many thanks. Commented Jul 18, 2016 at 19:47
• The percentages in your first table can be computed exactly as 1 - powerA / ( 2 * powerB ).
– Kyle
Commented Jul 18, 2016 at 20:01
• @Kyle That only works as long as powerA < powerB. Once powerA is larger, you need to switch to powerB / (2 * powerA). Commented Jul 19, 2016 at 13:38
• I'm not sure the StackExchange ToS lets you stray from the mandatory site license on content and code, even if your license is more permissive than theirs. Of course, it's impossible to find if it's the proposed MIT or still CC. Commented Jul 19, 2016 at 16:26
• @LarsViklund You are starting an off-topic discussion here, but no, this is incorrect. The license to stackexchange is non-exclusive, which means I am still free to give away my intellectual property under any other license terms when I want to. My contributions are dual-licensed under CC-BY-SA (as mandated by Stackexchange) and WTFPL. You can choose under which condition you would like to use my contributions. Commented Jul 19, 2016 at 22:26

Your mistake is using a "dice based" approach. You're on a computer you can use any system you like. Make a table that turns a difference in values into a %age chance to win and then you can set the values to absolutely anything you like, e.g.

Difference (A-B) | %chance A wins
-----------------|---------------
+5 or greater    | 100%
+4               | 95%
+3               | 85%
+2               | 70%
+1               | 55%
0                | 50%


(You only need do half the table, simply always choose A as the one with the higher stat)

Obviously, these numbers are just an example, you can make it follow whatever distribution pleases you.

• Though I'm currently working with a system based on the accepted answer, this is quite simple, and may also be a good solution for me. I knew good ol' StackExchange would come through for me. Commented Jul 19, 2016 at 20:46

This is a pretty deep question, honestly, from a game mechanics standpoint. But there are a few things that might help.

First, this is why most games have a separate component for hit and damage, where there's a "roll" to see if you hit for damage, then a "roll" against a damage table or range for the given character. This also leads to some standard archetypes across genres, where you might have smaller, quicker characters that have fewer hitpoints but deal more damage (glass cannon mages, certain types of rogues) and larger, armored characters that hit for less damage (tanks, warriors).

This leads to a natural balance where the smaller character might be fragile, but avoids being hit as often due to an agility-type ability, and also evens the playing field by doing more damage (a spell, or a poison effect that does damage over time). The tank might be slower and get hit more often, but often has a huge well of health or hitpoints to sustain, however tends to do less damage per hit (or damage per second).

The background for these is why many games continually go through balancing of weapons and classes and stats. World or Warcraft, Destiny, Diablo, Battlefield: any type of game in any genre often goes through balancing and tuning over time.

This may not be a direct answer, but you did ask for general ideas. So, let's also evaluate the play system.

How do those attributes function? If all else is equal (no archetype, no armor or better weapons or whatnot), then any slight gain is absolutely likely to throw things heavily in favor of one side. While adding facets to combat complicates any system, it also allows for more flexibility.

• As an aside, I think this is an excellent question and could lead to some very interesting game mechanics discussions. It's possible that this could end up becoming opinion-based, so it's important to be careful of such tangents (this game does it better than this game, and so forth), but there's some fundamentals involved that could be enlightening as more people post. Commented Jul 18, 2016 at 16:08
• Amusingly, I did have a 'hit' and 'damage' mechanic going first, but scrapped it for reasons I no longer remember (and it was only yesterday. My memory is a bit ... poor) I should be clear, the characters are not attacking/defending one another. There's no damage component. It's a skill check, where both are checked against a common value to see if the roll 'passes'.. There's no interaction between the two that are competing. Commented Jul 18, 2016 at 16:17

There's two big things.

First, remember you are on a computer. You can make any system you want. No need to limit yourself to a d20 roll, though this is easy to comprehend for players. Things like rolling 6 d6 dice are easy on a computer and they give far less random results.

Second, looking at other systems like D&D it is obvious that they simply throttle down the effect of attributes a great deal. Instead of having your base stat add 80% of its value to the rule, scale it down and make its addition more subtle. In D&D for example if you have 18 dexterity you get just 4 as a bonus to your armor class.

So in short numerically all you need to do is scale down your domain to fit your range better. But qualitatively I would think looking at other systems and coming up with things that seem less mathematic would make for a more satisfying system for the player.

• 1d20 or 6d6 or 5d4 - the results are no more or less random, you only change the range. Random is random. Scaling down range and domain aren't enough to balance a system. It's likely to only draw things out longer. Commented Jul 18, 2016 at 20:06
• @JesseWilliams that's not true. 1d20 has an equal chance to get any of the possible values. With 5d4, you're much more likely to get a 12 or 13 than you are to get a 20. Commented Jul 18, 2016 at 22:26
• Multiple rolls also elp hide flaws in number generators so it's especially important on computers. In fact combining rolls at a bitwise kind of level is pretty much the basis of many generators. Commented Jul 19, 2016 at 2:57
• I stand corrected. Commented Jul 19, 2016 at 19:25
• @RobWatts that's still not more or less random, it's just a different distribution. Having information about prior "rolls" doesn't allow you to make a better prediction of future outcomes (ignoring flaws in the RNG), so it's just as random. Commented Jul 19, 2016 at 21:22

How about this: Add a constant, e.g. 1000, to all attributes concerned. Then the relative difference becomes very small.

Know your numbers

Adding a bit to Philipp's answer, namely that rand[x] compared to rand[y] may not always produce what one expects. Below a table where we compare A to B. Both A and B have the values 1...10. We compare in two ways (note: rand() in this case generates integers, ie. rolls):

1. rand[A] > rand[B]
2. rand[A] ≥ rand[B] (ie. greater or equal to)

Additionally we compare

1. rand[A*1000000] > rand[B*1000000]
(in this case it is irrelevant whether it is > or ≥ as they are so close). These big figures are within parenthesis.

Cells hold %'s. Each result is holds 1 million iterations (made using Dyalog APL).

┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
│ A↓      B→ │ 1 (1000000)│ 2 (2000000)│ 3 (3000000)│ 4 (4000000)│ 5 (5000000)│ 6 (6000000)│ 7 (7000000)│ 8 (8000000)│ 9 (9000000)│10(10000000)│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 1 (1000000)│ >0(50) ≥100│  >0(25) ≥50│  >0(17) ≥33│  >0(13) ≥25│  >0(10) ≥20│   >0(8) ≥17│   >0(7) ≥14│   >0(6) ≥13│   >0(6) ≥11│   >0(5) ≥10│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 2 (2000000)│>50(75) ≥100│ >25(50) ≥75│ >17(33) ≥50│ >12(25) ≥38│ >10(20) ≥30│  >8(17) ≥25│  >7(14) ≥21│  >6(13) ≥19│  >6(11) ≥17│  >5(10) ≥15│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 3 (3000000)│>67(83) ≥100│ >50(67) ≥83│ >33(50) ≥67│ >25(37) ≥50│ >20(30) ≥40│ >17(25) ≥33│ >14(21) ≥29│ >12(19) ≥25│ >11(17) ≥22│ >10(15) ≥20│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 4 (4000000)│>75(87) ≥100│ >62(75) ≥88│ >50(62) ≥75│ >37(50) ≥63│ >30(40) ≥50│ >25(33) ≥42│ >21(29) ≥36│ >19(25) ≥31│ >17(22) ≥28│ >15(20) ≥25│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 5 (5000000)│>80(90) ≥100│ >70(80) ≥90│ >60(70) ≥80│ >50(60) ≥70│ >40(50) ≥60│ >33(42) ≥50│ >29(36) ≥43│ >25(31) ≥38│ >22(28) ≥33│ >20(25) ≥30│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 6 (6000000)│>83(92) ≥100│ >75(83) ≥92│ >67(75) ≥83│ >58(67) ≥75│ >50(58) ≥67│ >42(50) ≥58│ >36(43) ≥50│ >31(38) ≥44│ >28(33) ≥39│ >25(30) ≥35│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 7 (7000000)│>86(93) ≥100│ >79(86) ≥93│ >71(79) ≥86│ >64(71) ≥79│ >57(64) ≥71│ >50(57) ≥64│ >43(50) ≥57│ >38(44) ≥50│ >33(39) ≥44│ >30(35) ≥40│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 8 (8000000)│>88(94) ≥100│ >81(87) ≥94│ >75(81) ≥87│ >69(75) ≥81│ >63(69) ≥75│ >56(62) ≥69│ >50(56) ≥62│ >44(50) ≥56│ >39(44) ≥50│ >35(40) ≥45│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│ 9 (9000000)│>89(94) ≥100│ >83(89) ≥94│ >78(83) ≥89│ >72(78) ≥83│ >67(72) ≥78│ >61(67) ≥72│ >55(61) ≥67│ >50(56) ≥61│ >44(50) ≥56│ >40(45) ≥50│
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
│10(10000000)│>90(95) ≥100│ >85(90) ≥95│ >80(85) ≥90│ >75(80) ≥85│ >70(75) ≥80│ >65(70) ≥75│ >60(65) ≥70│ >55(60) ≥65│ >50(55) ≥60│ >45(50) ≥55│
└────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────┘


If looking at A=2 and B=3 (and 1 million tests):

• rand(2) is bigger than rand(3) in 17 % of the cases
• rand(2000000) is bigger than rand(3000000) in 33 % of the cases (notice scaling ./.. integer rounding)
• rand(2) is bigger than or equal to rand(3) in 50 % of the cases
• (rand(2000000) is also bigger than or equal to rand(3000000) in 50 % of the cases)

Surprises might be that:

• rand(2) > rand(3) in only 17 % of the cases
• rand(10) > rand(10) in 45 % of the cases
• rand(6) > rand(5) every other time

I might in fact solve this Q differently, by simply hand-typing a 10x10 table with nice, desired percentages (maybe one wants irregularity as well?). Then if needed interpolate between two values, to get an exact percentage, say it is of some reason 53. Then it is easy to generate a 53%-probability hit, a 0 or 1, by simply executing a rand(100) and testing if it is smaller or equal to 53 :-).

That is along the line Jack Aidley mentions.

• Are you using a random number generator which generates integers? My answer uses a RNG which generates double-precision floating point numbers between 0.0 and 1.0. In that case the difference between > and >= is negligible. You might want to point that out. Commented Jul 20, 2016 at 7:54
• Yes, that's part of the intended message, to just point out the varying behaviour of numerical spaces, eg. small-value integers (rough granularity) vs. large-value integer (and indeed also floats) with fine granularity. I'll insert "integer" somewhere, thx for pinpointing. I actually do point out that negligibility: "(in this case it is irrelevant whether it is > or ≥ as they are so close"). Often numbers find surprising values (for the hman mind) if the system isn't encouraged to seek balance. Generically speaking, ofc, not necessarily in this case. Commented Jul 20, 2016 at 21:09

The traditional approach that several answers have implicitly referenced but nobody has actually spelled out is that the task requires a fixed die roll, and add an ability modifier derived from your stats.

For example, if two players follow the procedure:

• Roll a 14 sided die
• Add their modifier to the die roll

and repeat until one side beats the other, then you get numbers in your range: here are the odds of victory with a given numerical advantage to their modifier:

0   50%
1   57%
2   64%
3   70%
4   76%
5   81%
6   85%
7   89%
8   92%
9   95%
10  97%


The characters do not challenge each other for supremacy. They challenge a requirement. What if both pass the requirement. Who wins? I'm surprised you haven't challenged the logic enough that you've even went to calculation with it.

Either way, here are two things that might do you some good.

Win chance case with advantage:

IF pass bar/skill check is a roll of 10. A Rolls 40. B Rolls 42. IF only one has to win. Starting from an equal A 50% Win/ B 50% Win . You can add % to win chance based on amount of advantage. Roll B has (42-40)/40 = 5% advantage in terms of roll. Adding it directly makes B's win chance 55%. Or you can determine a custom win chance per advantage percent. Say for every 100% advantage you add 10% chance to win. So if A Rolls 10 and B rolls 20. Then A wins 40% and B Wins 60% of the cases.

Fair randomness concept:

Doing a standard 30% chance to win you may end-up winning 38 checks out of 100.

Some people want an extra step in fairness and make sure that a 30% chance always wins exactly 30 out of 100 encounters and suffice with the randomness of not knowing which encounters in the sequence are a win and which are a loss.

This is particularly useful for well calculated game economies. Because a random stat of 70% chance to win. Say 70% chance for a mob to drop 5 gold. The mobs may end up dropping the gold 81 times out of 100. Which throws off income/expense flows off balance. And depending on how many entities/instances use such rolls inflation and/or shortages are inevitably created. Of course many folks don't even have a rough estimate of full input/expense rundowns of their economy. A lot of people suffice with doing "most" economy points. And leave some generation variables that are not calculated and stack discrepancy overtime even with fair randomness.

Inflation and shortages aren't a problem on their own. You can manage non-fair randomness and even unforeseen variables if your economy is setup to respond adequately to inflation and shortage.

Why bother with this as the law of large numbers evens things out in the long run?

Not every environment can retain its design behavior while counting on things to even out later on...

• I like especially the last sentence. Grabbing from elsewhere: I believe that for example Viking Lotto's in:out ratio is ca 4:1 on the long run (where one could replace "long" with "large"); it has an almost controversial (but well defined) design behaviour, and it functions. One cannot execute the math beneath unless the design behaviour is precisely defined first. Numbers tend to be uncontrolled without control... Commented Jul 20, 2016 at 21:30
• @Stormwind of course. If the design/theory lacks - math is useless. Its but a tool. I've seen designers with 5th grader math level pull good economies. They simply mapped what they wanted to do logically and went to the math folks (usually the coders) for the tools/advice how to do the math bits. It somehow still manages not to be obvious to everyone - The more problems you have with the blueprint - the more headache you will see in construction. Just grabbing some system and tweaking it is missing the point entirely. If you just settle on whatever gets working first, its not really creative. Commented Jul 21, 2016 at 7:47