Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Like
17Likes
Dislike

"Not So Random Randomness" in Game Design and Programming

By Carsten Germer | Published Nov 18 2013 05:40 AM in Game Design
Peer Reviewed by (jbadams, Dave Hunt, CRFaithMusic)

game design random randomness function method programming

As production of "Pavel Piezo - Trip to the Kite Festival" draws to a close later this year I reviewed the material I collected for the Postmortem and found it too much and too diverse to put in one huge article. So, I identified the topics that can stand very well on their own, which were not limited to this specific game or production, and decided to write three short(er) posts in advance to the Postmortem.
This bit of information was first posted on Gamasutra as short comment on Lucky Breaks. I use this technique in almost every game design and programming, it's a handy tool in the belt and justifies a longer explanation and some examples.

Does "randomness" have to be really random?


In game design there is a kind of love-hate relationship with randomness. On the one hand it allows for variety with many types of content, on the other hand one can't "design" true randomness. How about a function that provides random outcomes but within parameters that can be influenced to fit the game design? It's little effort compared to what is gained in control, it can easily be abstracted in code and only requires holding a few more variables for every not-really-random-randomness.

A quick example: how I have previously dealt with rolling dice for calculating "loot" in a "chest". (It was something different, but the mechanics are the same and the concepts of "chest" and "loot" are instantly recognizable to any game grognard.) Let's say the chance of finding a golden ticket, alongside the usual sell-loot, in a chest should be 1:10000. If you use true randomness the player may eventually find a ticket in three consecutive chests and after that she will find no ticket in three months of play. What we do first is to reduce the number 10000 by 100 with every chest opened, store this value in howLikelyIsPlayerToFindTicket and use this variable to calculate our random chance. 1:9900, 1:9800 and so on. The chances get better with every chest until, after 100 tries, a ticket is granted. Remember, it is still possible that the player finds a ticket with a chance of 1:8400 or all other combinations, the odds simply get better with every try.

Additionally, when the player has found a ticket, we want her to not find another ticket again, too shortly after. We set another variable absolutelyNoTicketFindable to, say, 10. With every chest looted we make sure that no ticket is in there and decrease the variable by 1.
Once absolutelyNoTicketFindable reaches 0 we go back to our initial 1:10000 chance and to reducing it by 100 with every chest opened. We introduced a few additional values: Base Chance (10000), Increase Chance (100), Blocker (10), Current Chance (X, between 10000 and 0) and Current Blocks (Y, between 10 and 0).

If you are a programmer, you can already see where this is headed.

I would hold BC, IC and B as statics (or in a parent class) and CC and CB as variables (or within an object or in a derived class). A function MyRandomSuccess() processes these statics and variables (or gets the classes and objects as parameters). It calculates the success with the current values, modifies the variables accordingly and simply returns true or false. Depending on how you want to influence the outcome, you can introduce as many additional values as you wish.
  • You can influence the success withs buffs, power-ups, in-game events or what-have-you.
  • You can reduce or increase “Increase Chance” with a buff or power-up
  • For a level that is playing in a "poor" area, the player never finds a ticket and finds fewer of the lootable "rare" items.
  • You don’t have to define a completely different lootable for every occasion. Simply tag the area or the specific chest as "poor".
  • If it fits better, the value of absolutelyNoTicketFindable can be an amount of time that counts down.
  • You can influence variables for e.g. "hard hit" and "critical hit" depending on the level-difference between opponents to even out the playing field in a MOBA.
  • You can generate filler enemies with not-so-random-randomness strengths and weaknesses based on the players performance in the game thus far.
Properly abstracted, the function can be used for countless other decisions. I used this technique for (something comparable to) critical hits, enemy encounters, "random" goodies in gamification, chances in a lottery depending on real life weather, etc.

Voila, randomness harnessed.

In conclusion: The mechanics in question are still "random", can be heavily influenced by game design and, in my experience, are far more easy to balance then having, for instance, 100 different lootables and just switch them around.

Top image credit



About the Author(s)


Carsten Germer is the Creative Director at intolabs GmbH, based in Hamburg, Germany. He makes games since he got hold of his first computer, a VIC-20.

License


GDOL (Gamedev.net Open License)




Comments

One good example of not-so-random randomness is in Tetris, where the blocks aren't generated in a truly random manner. Typically, a "bag of pieces" (with the size of the bag varying on the version, from 7 (one of each), to more (so there are multiple copies of each piece in the bag)) is used, and the bag is randomly shuffled and then drawn from until depletion, then "refilled", shuffled again, etc. This reduces the risk of generating multiple S and Z shapes in a row (which would ruin your day). So it's random, but it's randomness is limited.

I wonder, if you apply physics to a 3d die that has bullet physics applied to it and toss it up and let it fall, wouldn't it land on a random face? 

 

I have an inkling that no one motion is perfectly like any other motion, so that every motion is always random. Are computerized physics "Perfect world" physics if it uses real world data for it's calculations?

 

Yet, at the end of the day it is true that as far as computer programming goes, you can't make anything 100% random unless you can toss a die in the air that has an infinite amount of sides. 

 

I don't know of one computer that can generate a random number between negative infinity and positive infinity. 

 

Don't know of one that can calculate 1003! 

 

I tried to create an automatic animation system (where a 3d model can animate itself), and I came to the issue that if a 3d model were to animate itself, then it would have to be able to move in an infinite amount of ways while having a finite range of motion. I still think this is possible. Perhaps I can work on it again. 

I wonder, if you apply physics to a 3d die that has bullet physics applied to it and toss it up and let it fall, wouldn't it land on a random face? 
 
I have an inkling that no one motion is perfectly like any other motion, so that every motion is always random. Are computerized physics "Perfect world" physics if it uses real world data for it's calculations?


Like most things involving calculation on a computer, a physics simulation is 100% deterministic. If you start with the same initial conditions and inputs, you'll always get the same result at the end.
 

Yet, at the end of the day it is true that as far as computer programming goes, you can't make anything 100% random unless you can toss a die in the air that has an infinite amount of sides. 
 
I don't know of one computer that can generate a random number between negative infinity and positive infinity. 
 
Don't know of one that can calculate 1003!


Some Unix-like systems will implement their dev/random device as an entropy pool or entropy generator. Essentially, as any computer goes through its normal day-to-day activities of executing code, gathering user input, etc... certain parts of its state approach non-determinism. Entropy daemons can gather this state in a pool which is churned and farmed for random numbers. As you use your computer, move your mouse, whack at your keyboard, open up youtube videos of cats doing gangnam, and so forth, you are increasing the entropy state on your computer, and any random numbers drawn from that state become "more random", if you will. While essentially even such entropy gathering is still technically deterministic (ie, if you reset all clocks involved to the same state, all prng seeds, all device state, and had the user perform exactly the same use and inputs, as well as any externally connected systems being forced to do the same exact things, then you would end up with an identical entropy pool and identical random number sequence) in practicality it becomes nearly as truly random as hooking up to a nuclear decay random generator, and in fact many quality dev/random implementations are cryptographically secure. The downside of an entropy pool is that if you just clean rebooted your computer, the entropy pool might not be considered "random enough" yet, so a lot of dev/random implementations block until it is calculated that there is sufficient entropy to generate quality random numbers.

Can you imagine a game pausing, telling you that your random numbers won't be random enough if you continue playing, and advising you to go look at cat videos for awhile and come back in an hour?

I tried to create an automatic animation system (where a 3d model can animate itself), and I came to the issue that if a 3d model were to animate itself, then it would have to be able to move in an infinite amount of ways while having a finite range of motion. I still think this is possible. Perhaps I can work on it again.


This seems like kind of a non-sequitur.
In calculus, it is possible for a curve to approach a finite point but never reach it. This is how you can have an infinite amount of motions within a finite range of motion. It's based on the idea that no one motion is the same. If you loose a strand of hair the next time you try to reapeat a motion, that motion is different, because your total body mass is minus one strand of hair, which affects does affect how you move, though it is very small and not noticeable.

Even then the angle and speed at which you try to repeat the motion will be different as your heart rate will be different, and your global location will be different etc...

There is a natural randomness in the real world. Most computers can only generate an approximation to the randomness in the real world, even if we strap sensors to our bodies and have the computers record it.

I was wondering if using real world data would make things in a game more random. All we need is "close enough"

You can determine quite precisely "when" a standard loot drop will occur, given it's rarity. You are only required to decide how often you want to be wrong. Let's say we wish to be right 19 times out of 20, so we'll be wrong 1 in 20.

 

Given a loot drop chance of 1/10 000, how many tries will it take to get that loot drop?

log(1/20) / log(9999/10000) =29956 tries

 

After approximately 30 000 tries, 19 out of 20 players will have that loot drop.

@AngleWyrm: You're right, but my article is about introducing modifiers to the probability of rolling a 1 (success) that can be programmed easily and present options to the underlying Game Design and/or balancing.

 

so we'll be wrong 1 in 20.

 

Rolling a simple 1d/20 will result in sequences like:

8, 12, 4, 19, 1, 1, 1, 1, 1, 1, 15, 15, (hundred times NOT 1), 1, ...

This means the player found six consecutive "rare items" near the beginning and after that no rare drop for 102 times.

That is perfectly random but not very well distributed and undesirable from the point of Game Design and balancing.

 

So we introduce modifiers.

To have some "space" to modify the numbers we make them bigger to start with.

In my example I roll 1d/10000.

 

Given a loot drop chance of 1/10 000, how many tries will it take to get that loot drop?

 

Did you understand from my protocode that the value 10000 get's decreased by 100 for every subsequent roll?

Until the player has found a rare drop and the value get's back to 10000.

The probability of success increases with every roll until success is practically granted.

 

Why use 100/10000 and not 1/1000?

To have another modifier. The value 100 can also be in- or decreased with game mechanics according to the Game Design.

 

If you simply roll 1d/20, how would you handle, for instance, a buff that gives 5% to finding rare items? Easy, just make it a 1d/19!

But what about if you want to give a player a higher probability incrementation, because it's "Lootfest Holiday Special"?

You could set the chance to 2/19, which might be a bit high for your overall Game Design. So, you set it to 3/38.

If you need more modifiers, pretty soon you manage variable values like there's no tomorrow. And we know from experience that that is where bugs occur...

 

Use a central function or method similar to the one I described in the article and you're all set :-)

I was wondering if using real world data would make things in a game more random. All we need is "close enough"

 

This is an interesting topic but I think we agree that modern random generators, properly seeded, are good for games.

 

This article is about introducing modifiers to influence the probability of the outcome of a "dice roll".

 

From the perspective of Game Design and balancing: Using random() is fine enough but the resulting sequences of numbers are not sufficient in many cases.

Rolling a simple 1d/20 will result in sequences like:

8, 12, 4, 19, 1, 1, 1, 1, 1, 1, 15, 15, (hundred times NOT 1), 1, ...

The point seems to be discussing what are called Runs, run lengths, and run frequencies. A random number generator produces runs with specific set of runs-up and runs-down lengths and frequencies, and the output can be tested to make sure that it conforms to mathematical definitions of randomness.

 

A good article on this topic is Non-Parametric Tests for Randomness by Ying Wang.

@carsten The encapsulation described in the article makes perfect sense. But I was wondering if you know of an open source framework for organizing this kind of system. I.e. something that helps organize the behavior of the random 'provider' subject to attributes or properties of the environment and character?

 

-Josh

@jjd: Actually, I think it wouldn't make much sense having some kind of framework for this and I don't know of any.

The implementation of this method is simple and relies heavily on how your code is structured, storing values etc., and more so on your game design, what values to use, how and when.

In my experience it takes much longer to think about what this method is to do within your game then to implement it :-)

Implementing the simple protocode I described in, say, Javascript shouldn't take more then a few minutes (sans testing).

 

Addendum: What framework or language are you looking for? I can search old backups for something that isn't owned by a company.

Yeah, I figured that was likely to be the case. The situation I was thinking about was that this kind of approach often starts out as a one-off solution because it is so straightforward to implement. However, I can see it easily leading to tight coupling between components of the system as more parameters from those components are used to affect the outcome.

 

As far as language goes and I not looking for anything in particular. Although these days I would be more inclined toward something in python or javascript.

I use a similar random method.

 

But my absolutelyNoTicketFindable depends on how lucky you were before.

 

Example:

There is 20% Chance to find a ticket, in other words 1:5 Chance to find a ticket.

Every time you don't find a ticket I add, lets say, 1 to the numerator (2:5, 3:5, 4:5, 5:5).
Now depending on when you find the ticket the
absolutelyNoTicketFindable will change depending on the numerator. If you found it with 1:5 chance. The next 5-1=4 times you won't find a ticket again. If you found one with 4/5 chance. The next 5-4=1 time you won't find a ticket again.

 

This works very well with higher chances, but not as good with very low chances.

Old 4X games used this model for chance of accomplishing research

(often you had to pay the research price once in full and by additional payment increased the chance of breakthrough every turn)


Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS