Randomness in games is an important part of making every playthrough different and keeping the player on their toes. But where does it come from? Let's take a look at some algorithms and methods games employ to create randomness.
I will say up-front that I am in no way an expert on randomness, I have just stumbled upon the subject many times while researching games for speedrun and TAS purposes. With that out of the way, let's get to it!
Almost all of these generators are going to fare badly in tests - they simply weren't made for applications like scientific simulations or cryptography. In many cases it was simply not feasible to spend more processing power on randomness because the rest of the game had to run as well, all within a 60th of a second.
Having a high quality source of randomness is of course great, but as long as the source is decent enough and used in clever ways you'd never know the randomness is of "low quality".
There are many ways to test the quality of PRNGs but we're going to keep it simple and just run our generators through PractRand: it's easy to use and prints out a number of tests passed for us to look at. We'll test 226 bytes (64 MBs).
The images below are made by generating pairs of random numbers from the PRNG in question, and using them as coordinates into a black image. The brightness is then increased at the selected coordinate. The images aren't really indicative of randomness quality, but we can still look at them and see if they look about right or if they have clear (non-random looking) patterns.
This generator has a big pre-generated table of values - four runs of 0-255 scrambled together. Rather than just returning a value from the table, it gets multiplied with a value the calling code supplies. This value acts as an upper limit: for example, Get_rng_value(0x67) would return a value in the range 0x00-0x66. While certainly convenient, the quality isn't great.
This image was generated by setting a limit of 192 (0-191) to clearly demonstrate what the limiting value does. Setting the limit lower will make a smaller but denser box, and the opposite with a higher value.
array [u8, 1024] rng_table =
[
0xA9, 0x39, 0x25, 0xCF, 0xCC, 0x20, 0x80, 0x8E, 0x1F, 0x77, 0x8B, 0x3D, 0xA2, 0x3E, 0x46, 0xDC,
0x55, 0x75, 0xB1, 0x6B, 0x38, 0x1C, 0xCC, 0xEA, 0x4B, 0x33, 0x97, 0x59, 0x8E, 0xBA, 0x12, 0xB8,
0x01, 0xB1, 0x3D, 0x07, 0xA4, 0x18, 0x18, 0x46, 0x77, 0xEF, 0xA3, 0x75, 0x7A, 0x36, 0xDE, 0x94,
0xAD, 0xED, 0xC9, 0xA3, 0x10, 0x14, 0x64, 0xA2, 0xA3, 0xAB, 0xAF, 0x91, 0x66, 0xB2, 0xAA, 0x70,
0x59, 0x29, 0x55, 0x3F, 0x7C, 0x10, 0xB0, 0xFE, 0xCF, 0x67, 0xBB, 0xAD, 0x52, 0x2E, 0x76, 0x4C,
0x05, 0x65, 0xE1, 0xDB, 0xE8, 0x0C, 0xFC, 0x5A, 0xFB, 0x23, 0xC7, 0xC9, 0x3E, 0xAA, 0x42, 0x28,
0xB1, 0xA1, 0x6D, 0x77, 0x54, 0x08, 0x48, 0xB6, 0x27, 0xDF, 0xD3, 0xE5, 0x2A, 0x26, 0x0E, 0x04,
0x5D, 0xDD, 0xF9, 0x13, 0xC0, 0x04, 0x94, 0x12, 0x53, 0x9B, 0xDF, 0x01, 0x16, 0xA2, 0xDA, 0xE0,
0x09, 0x19, 0x85, 0xAF, 0x2C, 0x00, 0xE0, 0x6E, 0x7F, 0x57, 0xEB, 0x1D, 0x02, 0x1E, 0xA6, 0xBC,
0xB5, 0x55, 0x11, 0x4B, 0x98, 0xFC, 0x2C, 0xCA, 0xAB, 0x13, 0xF7, 0x39, 0xEE, 0x9A, 0x72, 0x98,
0x61, 0x91, 0x9D, 0xE7, 0x04, 0xF8, 0x78, 0x26, 0xD7, 0xCF, 0x03, 0x55, 0xDA, 0x16, 0x3E, 0x74,
0x0D, 0xCD, 0x29, 0x83, 0x70, 0xF4, 0xC4, 0x82, 0x03, 0x8B, 0x0F, 0x71, 0xC6, 0x92, 0x0A, 0x50,
0xB9, 0x09, 0xB5, 0x1F, 0xDC, 0xF0, 0x10, 0xDE, 0x2F, 0x47, 0x1B, 0x8D, 0xB2, 0x0E, 0xD6, 0x2C,
0x65, 0x45, 0x41, 0xBB, 0x48, 0xEC, 0x5C, 0x3A, 0x5B, 0x03, 0x27, 0xA9, 0x9E, 0x8A, 0xA2, 0x08,
0x11, 0x81, 0xCD, 0x57, 0xB4, 0xE8, 0xA8, 0x96, 0x87, 0xBF, 0x33, 0xC5, 0x8A, 0x06, 0x6E, 0xE4,
0xBD, 0xBD, 0x59, 0xF3, 0x20, 0xE4, 0xF4, 0xF2, 0xB3, 0x7B, 0x3F, 0xE1, 0x76, 0x82, 0x3A, 0xC0,
0x69, 0xF9, 0xE5, 0x8F, 0x8C, 0xE0, 0x40, 0x4E, 0xDF, 0x37, 0x4B, 0xFD, 0x62, 0xFE, 0x06, 0x9C,
0x15, 0x35, 0x71, 0x2B, 0xF8, 0xDC, 0x8C, 0xAA, 0x0B, 0xF3, 0x57, 0x19, 0x4E, 0x7A, 0xD2, 0x78,
0xC1, 0x71, 0xFD, 0xC7, 0x64, 0xD8, 0xD8, 0x06, 0x37, 0xAF, 0x63, 0x35, 0x3A, 0xF6, 0x9E, 0x54,
0x6D, 0xAD, 0x89, 0x63, 0xD0, 0xD4, 0x24, 0x62, 0x63, 0x6B, 0x6F, 0x51, 0x26, 0x72, 0x6A, 0x30,
0x19, 0xE9, 0x15, 0xFF, 0x3C, 0xD0, 0x70, 0xBE, 0x8F, 0x27, 0x7B, 0x6D, 0x12, 0xEE, 0x36, 0x0C,
0xC5, 0x25, 0xA1, 0x9B, 0xA8, 0xCC, 0xBC, 0x1A, 0xBB, 0xE3, 0x87, 0x89, 0xFE, 0x6A, 0x02, 0xE8,
0x71, 0x61, 0x2D, 0x37, 0x14, 0xC8, 0x08, 0x76, 0xE7, 0x9F, 0x93, 0xA5, 0xEA, 0xE6, 0xCE, 0xC4,
0x1D, 0x9D, 0xB9, 0xD3, 0x80, 0xC4, 0x54, 0xD2, 0x13, 0x5B, 0x9F, 0xC1, 0xD6, 0x62, 0x9A, 0xA0,
0xC9, 0xD9, 0x45, 0x6F, 0xEC, 0xC0, 0xA0, 0x2E, 0x3F, 0x17, 0xAB, 0xDD, 0xC2, 0xDE, 0x66, 0x7C,
0x75, 0x15, 0xD1, 0x0B, 0x58, 0xBC, 0xEC, 0x8A, 0x6B, 0xD3, 0xB7, 0xF9, 0xAE, 0x5A, 0x32, 0x58,
0x21, 0x51, 0x5D, 0xA7, 0xC4, 0xB8, 0x38, 0xE6, 0x97, 0x8F, 0xC3, 0x15, 0x9A, 0xD6, 0xFE, 0x34,
0xCD, 0x8D, 0xE9, 0x43, 0x30, 0xB4, 0x84, 0x42, 0xC3, 0x4B, 0xCF, 0x31, 0x86, 0x52, 0xCA, 0x10,
0x79, 0xC9, 0x75, 0xDF, 0x9C, 0xB0, 0xD0, 0x9E, 0xEF, 0x07, 0xDB, 0x4D, 0x72, 0xCE, 0x96, 0xEC,
0x25, 0x05, 0x01, 0x7B, 0x08, 0xAC, 0x1C, 0xFA, 0x1B, 0xC3, 0xE7, 0x69, 0x5E, 0x4A, 0x62, 0xC8,
0xD1, 0x41, 0x8D, 0x17, 0x74, 0xA8, 0x68, 0x56, 0x47, 0x7F, 0xF3, 0x85, 0x4A, 0xC6, 0x2E, 0xA4,
0x7D, 0x7D, 0x19, 0xB3, 0xE0, 0xA4, 0xB4, 0xB2, 0x73, 0x3B, 0xFF, 0xA1, 0x36, 0x42, 0xFA, 0x80,
0x29, 0xB9, 0xA5, 0x4F, 0x4C, 0xA0, 0x00, 0x0E, 0x9F, 0xF7, 0x0B, 0xBD, 0x22, 0xBE, 0xC6, 0x5C,
0xD5, 0xF5, 0x31, 0xEB, 0xB8, 0x9C, 0x4C, 0x6A, 0xCB, 0xB3, 0x17, 0xD9, 0x0E, 0x3A, 0x92, 0x38,
0x81, 0x31, 0xBD, 0x87, 0x24, 0x98, 0x98, 0xC6, 0xF7, 0x6F, 0x23, 0xF5, 0xFA, 0xB6, 0x5E, 0x14,
0x2D, 0x6D, 0x49, 0x23, 0x90, 0x94, 0xE4, 0x22, 0x23, 0x2B, 0x2F, 0x11, 0xE6, 0x32, 0x2A, 0xF0,
0xD9, 0xA9, 0xD5, 0xBF, 0xFC, 0x90, 0x30, 0x7E, 0x4F, 0xE7, 0x3B, 0x2D, 0xD2, 0xAE, 0xF6, 0xCC,
0x85, 0xE5, 0x61, 0x5B, 0x68, 0x8C, 0x7C, 0xDA, 0x7B, 0xA3, 0x47, 0x49, 0xBE, 0x2A, 0xC2, 0xA8,
0x31, 0x21, 0xED, 0xF7, 0xD4, 0x88, 0xC8, 0x36, 0xA7, 0x5F, 0x53, 0x65, 0xAA, 0xA6, 0x8E, 0x84,
0xDD, 0x5D, 0x79, 0x93, 0x40, 0x84, 0x14, 0x92, 0xD3, 0x1B, 0x5F, 0x81, 0x96, 0x22, 0x5A, 0x60,
0x89, 0x99, 0x05, 0x2F, 0xAC, 0x80, 0x60, 0xEE, 0xFF, 0xD7, 0x6B, 0x9D, 0x82, 0x9E, 0x26, 0x3C,
0x35, 0xD5, 0x91, 0xCB, 0x18, 0x7C, 0xAC, 0x4A, 0x2B, 0x93, 0x77, 0xB9, 0x6E, 0x1A, 0xF2, 0x18,
0xE1, 0x11, 0x1D, 0x67, 0x84, 0x78, 0xF8, 0xA6, 0x57, 0x4F, 0x83, 0xD5, 0x5A, 0x96, 0xBE, 0xF4,
0x8D, 0x4D, 0xA9, 0x03, 0xF0, 0x74, 0x44, 0x02, 0x83, 0x0B, 0x8F, 0xF1, 0x46, 0x12, 0x8A, 0xD0,
0x39, 0x89, 0x35, 0x9F, 0x5C, 0x70, 0x90, 0x5E, 0xAF, 0xC7, 0x9B, 0x0D, 0x32, 0x8E, 0x56, 0xAC,
0xE5, 0xC5, 0xC1, 0x3B, 0xC8, 0x6C, 0xDC, 0xBA, 0xDB, 0x83, 0xA7, 0x29, 0x1E, 0x0A, 0x22, 0x88,
0x91, 0x01, 0x4D, 0xD7, 0x34, 0x68, 0x28, 0x16, 0x07, 0x3F, 0xB3, 0x45, 0x0A, 0x86, 0xEE, 0x64,
0x3D, 0x3D, 0xD9, 0x73, 0xA0, 0x64, 0x74, 0x72, 0x33, 0xFB, 0xBF, 0x61, 0xF6, 0x02, 0xBA, 0x40,
0xE9, 0x79, 0x65, 0x0F, 0x0C, 0x60, 0xC0, 0xCE, 0x5F, 0xB7, 0xCB, 0x7D, 0xE2, 0x7E, 0x86, 0x1C,
0x95, 0xB5, 0xF1, 0xAB, 0x78, 0x5C, 0x0C, 0x2A, 0x8B, 0x73, 0xD7, 0x99, 0xCE, 0xFA, 0x52, 0xF8,
0x41, 0xF1, 0x7D, 0x47, 0xE4, 0x58, 0x58, 0x86, 0xB7, 0x2F, 0xE3, 0xB5, 0xBA, 0x76, 0x1E, 0xD4,
0xED, 0x2D, 0x09, 0xE3, 0x50, 0x54, 0xA4, 0xE2, 0xE3, 0xEB, 0xEF, 0xD1, 0xA6, 0xF2, 0xEA, 0xB0,
0x99, 0x69, 0x95, 0x7F, 0xBC, 0x50, 0xF0, 0x3E, 0x0F, 0xA7, 0xFB, 0xED, 0x92, 0x6E, 0xB6, 0x8C,
0x45, 0xA5, 0x21, 0x1B, 0x28, 0x4C, 0x3C, 0x9A, 0x3B, 0x63, 0x07, 0x09, 0x7E, 0xEA, 0x82, 0x68,
0xF1, 0xE1, 0xAD, 0xB7, 0x94, 0x48, 0x88, 0xF6, 0x67, 0x1F, 0x13, 0x25, 0x6A, 0x66, 0x4E, 0x44,
0x9D, 0x1D, 0x39, 0x53, 0x00, 0x44, 0xD4, 0x52, 0x93, 0xDB, 0x1F, 0x41, 0x56, 0xE2, 0x1A, 0x20,
0x49, 0x59, 0xC5, 0xEF, 0x6C, 0x40, 0x20, 0xAE, 0xBF, 0x97, 0x2B, 0x5D, 0x42, 0x5E, 0xE6, 0xFC,
0xF5, 0x95, 0x51, 0x8B, 0xD8, 0x3C, 0x6C, 0x0A, 0xEB, 0x53, 0x37, 0x79, 0x2E, 0xDA, 0xB2, 0xD8,
0xA1, 0xD1, 0xDD, 0x27, 0x44, 0x38, 0xB8, 0x66, 0x17, 0x0F, 0x43, 0x95, 0x1A, 0x56, 0x7E, 0xB4,
0x4D, 0x0D, 0x69, 0xC3, 0xB0, 0x34, 0x04, 0xC2, 0x43, 0xCB, 0x4F, 0xB1, 0x06, 0xD2, 0x4A, 0x90,
0xF9, 0x49, 0xF5, 0x5F, 0x1C, 0x30, 0x50, 0x1E, 0x6F, 0x87, 0x5B, 0xCD, 0xF2, 0x4E, 0x16, 0x6C,
0xA5, 0x85, 0x81, 0xFB, 0x88, 0x2C, 0x9C, 0x7A, 0x9B, 0x43, 0x67, 0xE9, 0xDE, 0xCA, 0xE2, 0x48,
0x51, 0xC1, 0x0D, 0x97, 0xF4, 0x28, 0xE8, 0xD6, 0xC7, 0xFF, 0x73, 0x05, 0xCA, 0x46, 0xAE, 0x24,
0xFD, 0xFD, 0x99, 0x33, 0x60, 0x24, 0x34, 0x32, 0xF3, 0xBB, 0x7F, 0x21, 0xB6, 0xC2, 0x7A, 0x00,
]
u16 offset = 0
u8 Get_rng_value(u8 multiplicand)
{
offset = offset + 1 & 0x03FF
return (multiplicand * rng_table[offset]) >> 8
}
Period: 210
PractRand tests passed: 0
Games known to use this PRNG: