User blog:AimeePlaysMSM/On randomness and fairness

A recent reddit post reminded me once again of the nature of randomness, and what constitutes fairness.

If you're a programmer, you probably know the scenario. Your boss, or the company/person you're contracted with, asks you to build a certain functionality, and they say something needs to be random.

It's tempting to just use your knowledge of the language's built-in random, Math.random, rand, etc. functionality. After all, they said they wanted it to be random, here's random results, that'll be $$$, and have a nice day.

In reality, however, unless you have experience with what the actual desire is, a list of questions pops up instead, that can be prompted with "are you sure you want it to be random?"

( the following is a simplified example that hopefully everyone can follow - level generation of this type is a complex subject that is nevertheless very well-covered in written and online literature ) Let's say you did just use a built-in random function. You're writing a piece of code that, for example, uses random numbers to generate a procedural game level with land, water, mountains, trees, etc. Job done, right? Except that this is supposed to be Level 1. And Level 1 should be the same every time you start the game. But when you restart the game, Level 1 suddenly looks different, because the random number generator generated different numbers. Okay, so they didn't want it to be random all the time. It needs to be random, but reproducible. No problem, most built-in functions take a so-called 'seed'. Set this seed to a specific value before generating the random numbers, and each time you do so, the same random numbers result. Job done, right?

But not if instead you're generating a security product. Let's say that not only should the random numbers not be reproducible using a given seed, but that given a long list of generated numbers, nobody should ever be able to predict what the next number should be. Suddenly, you can't use most built-in random functions anymore. Now you need to delve into libraries or write your own solution that takes presumed or guaranteed fully random events as part of their random number generation. On some systems this can include a user typing, mouse moving, the temperatures of components in the system - all of which can be influenced. On others this could mean taking advantage of truly random events such as quantum effects that are part of a CPU's operation, or using an external electronic solution where any attempt at influencing the random number generation yields no information to the person trying to influence it and/or is immediately detected allowing your code to stop executing altogether to thwart the attack.

But you're not trying to create the next cryptocurrency banking solution, you're just writing a game. So it's all good. But then you get to level 9, and it's all water, and the game just dumps out and says there's nowhere to put the player's base. You could be lazy and detect that situation, and just re-generate over and over until there's land. But now you get to level 27, and it's a small spot of land otherwise surrounded by mountains and ocean. Now you might detect that, and generate numbers over and over and over again until there's at least 60% usable land. Now every time the player gets to level 31, they have to wait a while for the level to be generated, because through sheer bad luck you've got hundreds of runs and none of them generate a level that would have 60% usable land.

That's when it's time to look at a different method - shuffling. Presume the level is based on a 10 by 10 grid (100 tiles total). Make a list that has 60 of those tiles be land, maybe 20 water, 10 mountains, and 10 trees. Now you have 60% usable land, and all you need to do is shuffle those tiles around in the list, just as you would shuffle a deck of cards. You can even use random numbers to determine how and/or how often you want to shuffle those tiles around. The result appears random, each level should look different, but now you no longer run the risk of having an all-water level, or a level with just a tiny spot of land, or no trees, etc. You're absolutely guaranteed to have 60% usable land, no matter what the level is (for variety you might vary that number).

Which is how this becomes on topic for My Singing Monsters.

As far as anyone is able to tell, breeding monsters is an actual random process. You can breed Pango and PomPom together a thousand times, and still not get a Hoola. You could breed them 10,000 times, and still not get a Hoola. If somebody told you that there's a one in a hundred chance to get a Hoola (I don't know what the actual odds arE), they don't mean that if you try 100 times, you should get a Hoola one of those times. It just means that statistically speaking, if you don't get that Hoola after 100 tries, then the chance that you'll get one in the next 100 tries has doubled, but you might still not get a Hoola. Another 100 tries, and the chance you'll get one has tripled. Put a different way, if you have a one in hundred chance to get a Hoola, then you have a 10 in 1000 chance to get a Hoola.. and if after 900 tries you still don't have one, all 10 remain in the last 100 attempts; but it's still not guaranteed!

Statistically, mathematically, over exceedingly large number of attempts, this is considered 'fair'. A good random function is always 'fair' in this way. Flip a coin, and you it might land 'heads' 20 times in a row, but if you flip it often enough, eventually it will even out to 50% 'heads' and 50% 'tails', so it's 'fair'.

But for games, this doesn't appear 'fair' at all. It's not 'fair' that one person gets 10 Hoolas after 100 attempts, while somebody else using the exact same elements gets zero. Or, even if it is 'fair', it certainly doesn't *feel* 'fair' to the person with zero Hoolas. For games such as MSM, a shuffle approach would feel much more fair. A list of a hundred resulting monsters, one of which is a Hoola, and shuffled randomly (seeded by your player ID) would be much more fair. Now it might take another person a single breeding attempt, and it might take you a hundred attempts, but you *would* both get a single Hoola, guaranteed. It doesn't take away some elements of luck; getting one on the first attempt is still much more lucky than getting it on the 100th attempt, especially if you have to race time for a limited time event like this. It might also discourage some players from further attempts; if you get a Hoola on your first attempt, and you know you're not going to get one from the next 99 attempts, you're probably not going to keep trying. Thankfully, these sorts of issues can all be overcome, even at the cost of reducing fairness (one player gets 3 Hoolas, you only get 1), while still making sure it doesn't become a frustrationfest where you get zero Hoolas no matter how many attempts you make.

tl;dr: Game programmers should look beyond built-in random functions and think carefully whether a shuffle operation is more appropriate for their design.