An introduction to integrating randomness in games using the example of Qake.

From the first conception of our arcade game Qake, it was clear to us that we wanted some sort of randomness somewhere within the game. We considered many different approaches, but sometime down the road we decided that this somewhere was going to be the levels themselves.

Why would we want that? Replayability! - Randomness is a great addition because it enables replayability. If the levels of the game feature random factors, every gaming session can be unique and offer a new, exciting challenge to the player. Furthermore, it bears the potential to make players feel lucky. Think of roll-playing-games like Diablo III ® [1], where loot drops randomly. If that was not the case but drops were static, the game would lose a lot of appeal.

In the spirit of this 9gag post (see figure 1), we layed down that our game was going to be like those good old classics - but with a twist: There should be no hard finish line (in terms of a final level) for the game, but an infinite amount of ever-changing levels. To narrow things down, we established that we wanted randomized enemy configurations for each level, while maintaining comparable difficulty levels.

With this decision the question of "how does one design and implement randomness in levels?" arose. To answer this question many factors need to be considered - one of which is progression of difficulty. When you decide how the difficulty will progress within your game, you start out by establishing relevant factors. Maybe your game contains enemies that can be eliminated and therefore have a certain amount of hit points. These hit points then become a factor of difficulty, i.e. an enemy presumably is harder to kill if it has more hit points. Maybe your game is time-based and the player has to finish a certain task within a given timeframe. This timeframe then becomes a factor of difficulty, i.e. a shorter timeframe makes it harder for the player to succeed.

Most major games incorporate several of these factors, often interrelated with each other forming complex level systems.

Concerning Qake, we established that there would be three major difficulty factors:

- The number and type of enemy balls.
- The speed of enemy balls.
- The rate at which bombs and power ups spawn.

A basic first approach to start out with is developing these factors by linear incrementation (see figure 2.a).

More sophisticated and more commonly employed are exponential developments (see figure 2.b), where factors change slowly in the beginning and increments get bigger with later levels.

Usually not all factors are treated equally but develop at different gradients. In Qake, for example, ball movement speed increases linearly while the number of balls progresses exponentially.

Returning to our random component, we so far have laid down that we wanted the number of balls to increase exponentially but configurations to be random.

Since, however, different ball types also bear different difficulty levels, i.e. a Wobble-Ball (see video 1.a) in Qake is arguably a harder enemy than e.g. a Stop&Go-Ball (see video 1.b), we have a complex interrelation, which has to be taken into account when deciding which balls are going to live in each level.

Our approach to solve this problem incorporates a concept that might be familiar from real time strategy games like Starcraft ® [4]: Supply. The concept is rather simple and can easily be explained by a real-world metaphor: Entities need to eat, drink, etc. in order to live. In other words: they require supplies. If you do not have the required supplies to spare, you cannot entertain the unit. Simple as that.

Different entities, in our case ball types, are therefore associated with different quantities of required supply they eat up in order to live in the game.

A basic Spacemarine from Starcraft ® for example requires one supply, while a Tank - a massive siege unit - requires three (see figure 4).

Extracting this concept and adapting it to our game allows us to incorporate randomness into our difficulty progression: Instead of increasing the number of balls per level directly, we increase the supply available for balls, which is then allocated by choosing pseudo-randomly until exhausted.

A basic implementation might be nothing more than a few lines of code (see listing 1).

// retrieve supply based on some function supply = getSupplyForLevel(currentLevel); enemies = []; while(supply > 0){ // retrieve a random enemy based on supply left if(supply > 2){ i = randInt(1, 3); } else if(supply > 1){ i = randInt(1, 2); } else { i = 1; } switch(i){ case 1: enemies.add(new EnemyTier1()); break; case 2: enemies.add(new EnemyTier2()); break; case 3: enemies.add(new EnemyTier3()); break; } supply -= i; }

With such a randomized population algorithm it is possible to achieve a lot of different configurations even with only a few different enemy types. Some basic statistics help us out in determining how many are possible:

Given our three enemy tiers (Tier 1 , Tier 2 and Tier 3 with respective supply requirements of 1, 2 and 3) and a supply cap of 6, possible configurations are:

If we further increase the available supply, the number of possible configurations increases exponentially. Try listing all possible configurations with a supply cap of nine for example and you will see that possibilities become vast very fast.

To see the result in action have a look at figure 5, where you can see three different variations of Qake's level 33. Though the configurations differ significantly our approach enables us to maintain comparable difficulty levels. This facilitates great replayability and a unique experience at every gaming session.

// generates a random float between min and max (inclusive) +(float)randomFloatBetween:(float)min and:(float)max { float diff = max − min; return (((float) (arc4random() % ((unsigned)RANDMAX + 1)) / RANDMAX) ∗ diff) + min; } // generates a random int between min and max (inclusive) +(int)randomIntBetween:(int)min and:(int)max { return (int) (min + arc4random_uniform(max + 1 − min)) ; } // generates a random NSUInteger between min and max (inclusive) +(NSUInteger)randomNSUIntegerBetween:(NSUInteger)min and:(NSUInteger)max { return (NSUInteger)(min + arc4random_uniform((unsigned int)max + 1 − (unsigned int)min)); } // generates a random CGFloat between min and max (inclusive) +(CGFloat)randomCGFloatBetween:(CGFloat)min and:(CGFloat)max{ CGFloat diff = max − min; return (((CGFloat)(arc4random() % ((unsigned)RANDMAX + 1)) / RANDMAX) ∗ diff) + min; }

We hope you enjoyed this article and that it comes in handy to those of you who want to integrate some randomness in their games!

Do not forget to let us know about your experiences, ideas, critiques and questions about randomness in games in the comments!

### References:

- [1] Blizzard Entertainment, Inc.: Diablo III. http://us.battle.net/d3/
- [2] Sherlock_Horse: I guarantee it. http://9gag.com/gag/a8b5GEY
- [3] Allin Cottrell and Riccardo Lucchetti: Gnu Regression, Econometrics and Time-series Library. http://gretl.sourceforge.net
- [4] Blizzard Entertainment, Inc.: Starcraft II. http://us.battle.net/sc2/
- [5] Blizzard Entertainment, Inc.: Starcraft II Game Guide: Terran Units. http://us.battle.net/sc2/en/game/unit/#terran

## Comments

## Post a comment