Join the conversation
Log in with your Blackthorn account to create topics and reply.
Guides
How RNG works
0 replies · 411 views
Every so often, someone posts a clip of an unlucky sequence of misses against a mob or another player, but nobody clips the opposite. Streaks that cost you something get noticed, remembered, and shared; equally unlikely streaks that go your way don't. This perception of unfairness often leads to suspicions that the dice are loaded or simply aren't working.
First, nothing here is "true" randomness. The systems described below run on a standard pseudo-random number generator (PRNG): a deterministic algorithm that produces a sequence of numbers indistinguishable from randomness for any practical purpose. The team didn't custom-write the generator itself, Microsoft engineers did. What is custom is how that underlying randomness gets used.
There are two ways the game handles probability:
1. Plain rolls
Let's assume plain rolls are used for combat-related events. If an attack has an 18% miss chance, then over a very large number of swings, the observed rate will converge to exactly 18%. This is known as the law of large numbers. But a single fight isn't the long run; you might only see ten swings before someone dies. The "shape" of outcomes in these shorter bursts is called a binomial distribution:
2. Decks
For the rolls that shape how combat feels, the game uses a data structure that acts like a physical deck of cards.
a. Guaranteed distribution:
A deck contains 100 cards (numbered 0 to 99). It is shuffled using the Fisher–Yates shuffle, and then the cards are dealt one by one. If you have an 18% miss chance, cards 0–17 act as misses, and 18–99 act as hits. Once all 100 cards are dealt, the deck is rebuilt and reshuffled.
Because the deck always contains exactly one of every number, a full 100-draw cycle will always produce exactly 18 misses.
b. Guaranteed rates:
In many games, your true chance to hit starts well below the tooltip value and ramps up every time you fail (example). A deck system has no ramp. Every position in a shuffled deck is equally likely to hold a success or failure, meaning your very first swing carries your full, printed percentage.
c. Anti-clumping mechanism:
Guaranteeing 18 misses per 100 swings, and ensuring every position in the deck has an equal chance to hold any number, doesn't inherently prevent those misses from clumping together. To solve this, the 100 cards are dealt out into ten buckets of ten cards in a manner that guarantees every bucket gets exactly one card from each decile (0–9, 10–19, 20–29, up to 90–99). The buckets are then concatenated. At an 18% miss chance, this means:
d. Creation and usage:
A deck is created for each roll type on a per-player basis. Which deck is used depends on the roll type:
Important caveats:
e. Code:
https://github.com/OpenDAoC/OpenDAoC-Co ... ils/Random
3. Comparisons and simulations
Here is the aggregate difference when you simulate 20,000 ten-swing fight windows, comparing independent rolls to the decks:
a. Plain rolls:
Zero misses in a rolling window of ten swings happens about 14% of the time, and four or more misses happens about 9% of the time. The two most likely outcomes (1 and 2 misses) only account for 60% of encounters. The worst single window saw 8 misses total.
While a 5-miss streak starting on the first swing has only a 0.02% chance of happening (incredibly rare in isolation), across a 200-swing combat session, the chance of one occurring somewhere in that sequence is roughly 3%. Across a whole server of players fighting every day, these streaks happen constantly purely by unmodified probability.
b. Decks:
Zero misses in a rolling window of ten swings drops to about 4%, and 5 or more consecutive misses becomes mathematically impossible. The two most likely outcomes (1 and 2 misses) now account for almost 80% of encounters.
Here is a generated deck, showing how the grouping prevents those wild streaks:
4. Testing it yourself
If you want to test the RNG system, it's important to know whether the events you are parsing use decks or plain rolls.
a. Testing decks (main-hand, dodges, blocks...):
Because the deck system guarantees an exact distribution, a pure, uninterrupted sequence of 100 main-hand swings will yield exactly your printed percentage. If you have an 18% miss chance, you will see exactly 18 misses every 100 swings.
If your parser shows 25 misses in 100 swings, you likely included off-hand attacks, included swings that bypassed miss checks (like blocked swings), triggered a style chain (which uses a separate deck), experienced a forced reshuffle, or found an actual bug. Additionally, if you see streaks longer or more frequent than the deck's structure should mathematically allow, flag it.
b. Testing plain rolls (off-hand attacks, mixed logs, non-combat events...):
This is more complex. The process requires collecting enough samples and then determining if the measured odds fall within an acceptable statistical range.
Because we know the target rate in the game's code, we can calculate the range of results we'd normally expect to see from a player's logs due to random variation:
Therefore, with a sample size of 100, observing 25% does not by itself provide statistically significant evidence that the rate is incorrect.
The margin of error shrinks proportionally to 1 / sqrt(n), meaning that halving the margin of error requires approximately 4x as many samples, while reducing it by 10x requires approximately 100x as many samples.
First, nothing here is "true" randomness. The systems described below run on a standard pseudo-random number generator (PRNG): a deterministic algorithm that produces a sequence of numbers indistinguishable from randomness for any practical purpose. The team didn't custom-write the generator itself, Microsoft engineers did. What is custom is how that underlying randomness gets used.
There are two ways the game handles probability:
- Plain rolls:
The game calls the generator, gets a number, applies it, and then throws it away. Every roll is completely independent of the last. - Deck system:
The game uses the generator to shuffle a fixed set of outcomes, then deals them out one at a time without replacing them, only reshuffling when the deck is emptied.
1. Plain rolls
Let's assume plain rolls are used for combat-related events. If an attack has an 18% miss chance, then over a very large number of swings, the observed rate will converge to exactly 18%. This is known as the law of large numbers. But a single fight isn't the long run; you might only see ten swings before someone dies. The "shape" of outcomes in these shorter bursts is called a binomial distribution:
2. Decks
For the rolls that shape how combat feels, the game uses a data structure that acts like a physical deck of cards.
a. Guaranteed distribution:
A deck contains 100 cards (numbered 0 to 99). It is shuffled using the Fisher–Yates shuffle, and then the cards are dealt one by one. If you have an 18% miss chance, cards 0–17 act as misses, and 18–99 act as hits. Once all 100 cards are dealt, the deck is rebuilt and reshuffled.
Because the deck always contains exactly one of every number, a full 100-draw cycle will always produce exactly 18 misses.
b. Guaranteed rates:
In many games, your true chance to hit starts well below the tooltip value and ramps up every time you fail (example). A deck system has no ramp. Every position in a shuffled deck is equally likely to hold a success or failure, meaning your very first swing carries your full, printed percentage.
c. Anti-clumping mechanism:
Guaranteeing 18 misses per 100 swings, and ensuring every position in the deck has an equal chance to hold any number, doesn't inherently prevent those misses from clumping together. To solve this, the 100 cards are dealt out into ten buckets of ten cards in a manner that guarantees every bucket gets exactly one card from each decile (0–9, 10–19, 20–29, up to 90–99). The buckets are then concatenated. At an 18% miss chance, this means:
- Every bucket gets at least one miss (because the 0–9 card is always a miss).
- Eight of those buckets will get a second miss (from the 10–17 portion of the 10–19 decile).
- The remaining two buckets will have only one miss (because they drew the 18 or 19).
d. Creation and usage:
A deck is created for each roll type on a per-player basis. Which deck is used depends on the roll type:
- Miss, resist, dual wield activation, offensive procs, variance, crits -> Attacker's deck.
- Evade, parry, block, defensive procs, intercept (primarily for Spiritmaster) -> Defender's deck.
Important caveats:
- Off-hand swings are excluded:
Dual wield and hand-to-hand extra attacks roll on plain independent randomness. This stops them from eating through the main-hand's carefully structured decks. - Style chains get separate decks:
If every stage of a combo shared the same set of decks, landing your opener would consume a card from the same pool your follow-up needs. This would subtly alter the follow-up's actual odds. - NPCs don't use decks:
The only exception is Necromancer pets. They use their owner's decks and are effectively treated as players. - Decks don't eliminate variance entirely:
The anti-streak guarantee is only ironclad within a specific bucket. The game doesn't heavily constrain the "seams" where one bucket ends and another begins, or where an empty deck is reshuffled. These boundaries introduce some localized variance. Additionally, other game events may clear the memory of your current deck, forcing a premature reshuffle.
e. Code:
https://github.com/OpenDAoC/OpenDAoC-Co ... ils/Random
3. Comparisons and simulations
Here is the aggregate difference when you simulate 20,000 ten-swing fight windows, comparing independent rolls to the decks:
a. Plain rolls:
Zero misses in a rolling window of ten swings happens about 14% of the time, and four or more misses happens about 9% of the time. The two most likely outcomes (1 and 2 misses) only account for 60% of encounters. The worst single window saw 8 misses total.
While a 5-miss streak starting on the first swing has only a 0.02% chance of happening (incredibly rare in isolation), across a 200-swing combat session, the chance of one occurring somewhere in that sequence is roughly 3%. Across a whole server of players fighting every day, these streaks happen constantly purely by unmodified probability.
b. Decks:
Zero misses in a rolling window of ten swings drops to about 4%, and 5 or more consecutive misses becomes mathematically impossible. The two most likely outcomes (1 and 2 misses) now account for almost 80% of encounters.
Here is a generated deck, showing how the grouping prevents those wild streaks:
Notice that every fixed 10-swing bucket lands on exactly 1 or 2 misses. Reshuffle the deck, and the specific numbers will change, but that distribution won't. The deck system essentially cuts the variance in half.Player wrote: Draws 1- 10: 47 91 20 87 13 56 67 35 5 72 -> misses: 2
Draws 11- 20: 49 76 16 62 36 90 28 85 6 50 -> misses: 2
Draws 21- 30: 81 2 32 66 18 95 55 27 74 41 -> misses: 1
Draws 31- 40: 25 94 59 31 63 86 45 17 77 4 -> misses: 2
Draws 41- 50: 82 22 57 34 92 71 43 11 7 68 -> misses: 2
Draws 51- 60: 24 38 69 98 40 80 52 75 15 8 -> misses: 2
Draws 61- 70: 21 93 1 79 14 65 58 30 46 83 -> misses: 2
Draws 71- 80: 78 61 44 51 3 23 39 97 89 12 -> misses: 2
Draws 81- 90: 33 0 48 10 88 29 60 96 70 53 -> misses: 2
Draws 91-100: 99 84 73 26 42 37 54 64 9 19 -> misses: 1
Total: 18 misses out of 100.
4. Testing it yourself
If you want to test the RNG system, it's important to know whether the events you are parsing use decks or plain rolls.
a. Testing decks (main-hand, dodges, blocks...):
Because the deck system guarantees an exact distribution, a pure, uninterrupted sequence of 100 main-hand swings will yield exactly your printed percentage. If you have an 18% miss chance, you will see exactly 18 misses every 100 swings.
If your parser shows 25 misses in 100 swings, you likely included off-hand attacks, included swings that bypassed miss checks (like blocked swings), triggered a style chain (which uses a separate deck), experienced a forced reshuffle, or found an actual bug. Additionally, if you see streaks longer or more frequent than the deck's structure should mathematically allow, flag it.
b. Testing plain rolls (off-hand attacks, mixed logs, non-combat events...):
This is more complex. The process requires collecting enough samples and then determining if the measured odds fall within an acceptable statistical range.
Because we know the target rate in the game's code, we can calculate the range of results we'd normally expect to see from a player's logs due to random variation:
Using this formula for an 18% target rate and a 95% confidence level, here is the acceptable margin of error for a given sample size:Player wrote: MOE (margin of error) = z * sqrt((p * (1 - p)) / n)
Expected range = p ± MOE
Where p is the target rate as a decimal (e.g. 0.18), n is the sample size, and z is the z-score corresponding to the desired confidence level:
A 95% confidence level means that, if the true rate really is 18%, approximately 95% of samples of this size would be expected to fall within the calculated range.Code: Select all
Two-tailed CL | Z-score ----------------------- 80.0% | 1.282 85.0% | 1.440 90.0% | 1.645 95.0% | 1.960 99.0% | 2.576 99.5% | 2.807 99.9% | 3.291
Code: Select all
Sample size | Expected Range (95%)
-------------------------------------
100 | 10.5% – 25.5% (± 7.5%)
1,000 | 15.6% – 20.4% (± 2.4%)
10,000 | 17.2% – 18.8% (± 0.8%)
50,000 | 17.7% – 18.3% (± 0.3%)
The margin of error shrinks proportionally to 1 / sqrt(n), meaning that halving the margin of error requires approximately 4x as many samples, while reducing it by 10x requires approximately 100x as many samples.
