Designing Game Difficulty Curves

This is an article explaining our somewhat mathematical approach to designing difficulty curves (or difficulty progressions) into games!  It was originally an internal document which we used when making games from around 2005 through to 2010, where arcade and action-puzzle games formed the majority of our output.

TL;DR : This is an article intended primarily for other developers who are developing games which get more difficulty with time or level. The solution at it’s simplest is to work out how well the best possible player could play, and adjust based on that!  Note – this gets a bit more technical than everyone may be happy to read!

TL;DR/2 : There’s a mild dinosaur reference in this article!

 


 

Difficulty Curves

In a typical puzzle or arcade style game, the challenge (difficulty) would normally increase through the progress of the game.

In the simplest case, difficulty would be related linearly to level by

D = c + G L

c =  The base (starting) difficulty

G = gradient; i.e. the amount difficulty changes by per level.

 

Hopefully the player will play many times, each time learning new skills or practising them, and on average find themselves getting further into the game on each try.

 

At the most simple form, the player goes up ‘levels’ and the difficulty increases at each level, like this:

 

There will be some point that the very best player cannot regularly get past, this is a very important value to learn!

It’s possible to find this by passing the game in an alpha form to top beta testers, or your most awesome player in your team (for us, it used to be Paul K!).

We refer to this difficulty setting as “Dm” (or Dmax), being the maximum difficulty that the most competent player can reach.

 

Difficulty Factors

Identifying the factors of the game which increase the difficulty is the primary goal; understanding how the factors interrelate is important.

Some obvious examples:

  • How fast targets appear and disappear in Police Range (one of our reaction based shooting games)
  • How many gaps there are in the board in OddBlob (a tile based puzzle game)
  • The target number of words to make in Bubble Babble (a target based word game)
  • The amount of time available for the level (many games!)

Some subtler examples

  • The ratio of single jump to double jump tiles in OddBlob. (More double jumps makes the board harder to “read”)
  • The propensity of a new petal to be spawned the same colour as an adjacent petal in Crazy Daisy, setting up (or breaking) easy chains.

Assuming the designer can wrap these factors together into a sensible progression, and relate them to an overall difficulty factor D, then the game progression can be worked on using this.

Difficulty Model

While it’s easy to implement a difficulty progression that just gets harder by a fixed amount every level, it isn’t the best choice, for a few reasons.

Instead, I came up with a “Standard Difficulty Model” – such that the difficulty doesn’t vary linearly with level, but is a curve with a number of somewhat distinct phases.

 

 

Intro Phase

Low difficulty level, shallow gradient; Allows a new player to learn the basics of the game and make very rapid gains as they acquire basic knowledge and get to grips with the UI and features of the game.

Learning Phase

Medium difficulty level, steep gradient; As the player builds on the basic skills they work to get further through the levels. The player should again feel like they make good progress as the skills become practised.

Progress Phase (or progress ramp)

High difficulty level, very shallow gradient; The player will have acquired all of the skills by this point, and is practising them at an expert level. Over this region, a small increase in skill (relative to earlier in the game) will still give them a good progress in the level that they can reach.

Dm would hopefully be at somewhere around 2/3 to 3/4 of the progress ramp. On the rare occasions that the player is on truly top form and is very lucky in the game setup, they will be able to get a level or two further than usual.

Ideally you would want the curve to be almost horizontal around the point of Dm, but this is less desirable in practise because max varies from player to player. It would be better for a good player and an expert player to be separated by a few levels, rather than by orders of magnitude. In Flight Control, one of our developers (Iain) was better than any other player in our office by a margin, but was able to get massively further in the game as a result with scores that the rest of us couldn’t comprehend.

Kill-off Phase

Extreme difficulty level, very steep gradient; This is to allow the game to become too difficult to continue and naturally kill off the player (running out of lives / moves / time, etc.).

The main reason for including this phase (rather than just linearly extending the progress phase, which will eventually pass the player’s ability at some point) is that it helps the designer to bring the gameplay time into a clearer ending zone, as well as more safely bringing an end-point to difficulty factors.

In a game with advertising, it may make sense to attune the game to appropriate advertising points (one advert per 10 minutes of play, for instance), so the positioning of Dmax and the kill-off phase would have bearing here.

Cautionary Examples : In some of our earlier games which were conversions from web games, the “change in difficulty” gradient was quite fixed.  Unforeseen was that some players were able to get considerably further than the developer (me!) on these titles. In one game this meant that their game became unbounded, and eventually would break the “maximum score” which would do an Int wrap, oh the embarrassment!  

In another game, the player would get a new life for each X (I think 10k) points.  After a certain level, the number of enemy items on the screen grew so large that the player would only have to make a single suicidal move, for the enemies to all crash too and award the player with enough points to get an extra life to replace the one they just used. This meant that players could again continue indefinitely (in fact, it got easier with level after a while!) and again were able to Int wrap.

Either of these could have been fixed by including some kind of kill-off phase.

Time & Progress Balance

Overall, the phases would be approximately balanced that each time unit (say 1/4 hour) of gameplay would on average give an equal increase to the maximum level they reach. This is desirable so that the player continues to feel that they are making progress, even though their rate of skill acquisition and expertise at applying those skills will change. (Skill acquisition is fast at intro levels, skill and expertise both gain fast at learning levels, acquisition and gain are slow at progress levels so the gradient is made shallow again.)

Variable Choice

The right values to choose for D for the edges of each phase are different by the game in question, and do require extensive tweaking.

Da should be chosen as the basic difficulty such that it can be given to a beginner and they can get some progress. Note that this is possible to choose as a “beginner to this game” as opposed to a “beginner to this genre” if appropriate.

Dm can be chosen empirically (i.e. by testing) using a linear progress, finding the average level that an expert player can reach, and then working back to find the difficulty variables.

The range of Da to Dm can then be used to guess the first few values to use.  As an approximate guide for a starting point:

Difficulty %age of (Dm-_Da_)
Da 0%
Db 20%
Dc 80%
Dd 110%

Level Positions

You would choose the level ranges to be appropriate positions where you’d want a player to be able to start if they were choosing different difficulty levels, also with some consideration to how long you want a game to last for a beginner, veteran, and expert player.

An example; In Police Range, we would like a full game for a truly expert player to last around 10 minutes. Each level is 25 seconds of play, followed by a few (say 5) seconds of summary and intro to the next level.  This means we’re expecting to level Police Range so that the expert would play about 20 levels.

Player Rating May choose to
start at
Approximate game time range
if played from beginning
Value for end of range
Beginner La 1-2 minutes Lb = 4
Veteran Lb 2-5 minutes Lc = 10
Expert Lc 5-10 minutes Ld = 20

These values will change as appropriate for different games, and of course not all games have a timed mode in quite so simple a way, so the choices won’t always be simple.


Simplified Difficulty Model

In practise, the smooth curves are harder to program and less critical than getting the gradients right over the phase. It is much simpler to treat the phases as distinct areas and have different (discontinuous) lines to cover each area.

(lines for areas)

Kill-Off Gradient

In the kill-off phase, you’d want the difficulty gradient to be steep – perhaps as steep as the gradient in the learning phase. The difference is there’s no end point in the kill-off phase (apart from the obvious, that is!)

The gradient Gk for the kill off phase, would be

Gk = (Dc-Db)/(Lc-Lb)

General Difficulty Function

Pseudo-Code for the general difficulty D as a function of L would be;

 

if(L<Lb)
{
  // Intro Phase
  D=Da+(Db-Da)/(L-La);
}
else if(L<Lc)
{
  // Learning Phase
  D=Db+(Dc-Db)/(L-Lb);
}
else if(L<Ld)
{
  // Progress Phase
  D=Dc+(Dd-Dc)/(L-Lc);
}
else
{
  // Kill-off phase
  D=Dd+Gk*(L-Ld);
}

This linearly interpolates between the start and end points for the section, dividing pro-rata by how far (in levels) you are through the section.

An advantage of using this method (rather than coding a lengthy array of difficulty by level) is that it is extremely easy to modify the curve by moving one of the coordinate (La, Da) (Lb, Db) (Lc, Dc) (Ld, Dd) pairs.

 

Intra-Level Difficulties

Assumptions so far are that a difficulty would stay the same throughout a level. An improvement on this is to adjust the difficulty (perhaps by an additional or subtler means) so that it increases steadily throughout each level.  This could be done smoothly so that the end of one level is the same difficulty as the start of the next, but it is actually better to have the ramp in a level steeper than this.

i.e.

In level 1, the difficulty increases from L1s to L1e  (start to end).

In level 2, the difficulty increases from L2s to L2e

L2s would be higher than L1s (as expected) but L1e should be greater than L2s, and so on from L2 to L3 etc.  By doing this the player experiences a gradient within the level, and the next level feels like a significant reduction (call it a reprieve if you prefer) in difficulty, allowing the player to regain their composure and continue with a bit more confidence.

As a “simple” first point suggestion of the relative settings

Take the difficulty function from earlier and use this to set the “start” difficulty for each level.

Ls = D(L);

Le = (D(L+1) + D(L+2)) / 2;

i.e. The end of level 1 is at approximately the difficulty halfway between the start point of level 2 and the start point of level 3.

Example when applied to a linear-by-level difficult progression

 

This alone would make for a reasonable progress system for many games, and is easy to implement.

 

 

Combining Difficulty Curve with Intra-Level Ramps

 

When the intra-level ramp is superimposed onto the phased model, you get a more complex overall curve (or poly-line) such as this:

 

In this way the tension of the game does not increase constantly, but varies each level, to give many “tension peaks” per game. It also means that a player can be tested at their breaking point (near Dmax) potentially two or three times before they eventually get overwhelmed. This is brilliant for conveying the impression that “I can do better on my next go!” – and when they do that little bit better, they are rewarded with (nearly) an entire level’s worth of extra progress.

If the game has audio or visual feedback (i.e. a panic heartbeat, outta-time warning flash) or similar, then this progression + relief will be extremely effective – it hits the emotive centre of the brain!

This is ultimately the chart we refer to as “The Progressisaurus“, a ravenous beast (rawr!), unslayable by normal players, but tameable by the mighty developer 😉

 

(Any player who survives the kill-off phase and falls off the right hand side deserves to suffer an int-wrap, and quite possibly, be eaten by a carnotaurus!)

 

Adding Features

If the game progressively adds extra features (a new baddie type, a new power-up to master, a new type of tile to stand on) etc, then the apparent drop at the beginning of a level is excellent because it gives the player a short time to learn and accommodate the new feature, before ramping back up in difficulty.  This technique was used in Space Giraffe, and was explained usefully by Jeff Minter in person at GDC.

 

Collecting Data and Continual Improvement

Using Flurry (or equivalent method) to send back usage and achievement data (during beta, or post-launch), it is possible to tweak the settings to be appropriate to the end players who will play the game.

Further, by looking at the self-selected groupings of what difficulty level was chosen, and what level they reached (and how long it took them), you can use this to adjust the coordinate pairs for the “end point” of that difficulty level.

The ultimate goal is to make the game as enjoyable as possible for the maximum number of real world players.

In our games we’ve shipped with default parameters for various settings, but have the ability to push overrides for these via the network. Sending a small JSON package of the difficulty coordinate sets would allow us to tweak the entire difficulty curve of the game with less than 1kb of data, and no app update required.

 

Credits

While this all made sense in the end, it was the product of a lot of thinking and discussion amongst the whole Astraware team, but particularly I’d like to call out Kieren Smith, Paul Klosowski, Larry Dyde, Iain Moore, Roland Glew and Paul (Jeff) Scorthorne, who each contributed to this back when it was part of our internal Wiki!