Level design for Two Birds One Stone is a bit different to other games because of the simplicity of the game, plus the goals of the game. This describes the process for generating lots of levels which don’t fall apart, have some estimate of difficulty, and how to sequence the levels so the game feels right.
A level for this game is very simple. It defines the number of birds (from 2 to 5), where those birds start, their initial velocity (which is always horizontal), their left/right boundaries, and the win condition. At one stage there were levels where the goal was to kill exactly two birds with one stone (in a level with more than two birds), and while it was amusing from a “technicality” perspective, these levels were never much fun. So, this style of level was removed, and so for every level the win condition just became “kill all birds with one stone”.
Initially, I had a level editor built into the game. It allowed moving the birds, changing the number of birds, adjusting their initial velocity, and also their left/right boundaries. This is all we needed. I would make levels by hand, and try to work out how to make them hard (make it so the birds are never near each other) or easy (have the two birds right near each other and flying at the same speed). While this was possible, it was not entirely enjoyable, and I don’t think I was particularly good at it. I did play each level, and come up with a difficulty rating (which was basically “easy” if I beat it the first time I tried it, “medium” if I didn’t, “hard” if it often took 5 stones, and “extreme” if it took more than 5 stones but was known possible). I never wanted there to be impossible levels, so I would always make sure I beat each level, and I had the game record how to beat each level so I could prove it later.
As the technology used for fudge factors was developed, I realised I could use this to automate the level design process. I could generate random “valid” levels, then use the solver to make sure it was possible to beat, and even use the solver to estimate difficulty – the harder it was for the solver, the harder it might be for a person.
Valid levels took just a few rules to define:
- Birds must not hit each other in their “unhit” state.
- Birds must never go too close to the corner with the slingshot because it makes it too hard to make the graphics not look weird.
With these rules, we can easily generate any number of levels, and check they are all solveable. Each level is given a difficulty rating which corresponds to how hard the solver had to work. These difficulties are saturated at a maximum value which was “the trial and error solver never solved the level in the number of iterations allowed, but the brute force solver showed it was definitely possible”. The solvers here were hardcoded to always release the stone 1 second into the level, so it is possible that some “extreme difficulty” levels are actually simple if you just wait a bit longer.
While it would be possible to have a nice linear progression of levels, and starting back at level 1 when you start a new game, this grows repetitive quickly for a game like this. We can generate a huge number of levels, and as long as the difficulty starts low and slowly ramps up, then it is preferable to keep giving “fresh” levels to the player.
The difficulty of the levels given depends on how many levels have been beaten this “game”. I wanted the concept of a “game” to be pretty loose, and for people to be able to play this without ever really caring – you lose a game by requiring more than 5 stones to beat a single level, but even when you lose, you still get to keep playing the level (with the option of skipping), and once you have beaten it, the difficulty dials back again. If you aren’t paying attention to when games start and stop, it is as if the levels get harder and harder and harder, then suddenly the difficulty drops, and then again they slowly get harder and harder and harder.
To implement this, the game keeps a history of recently seen levels, and will avoid repeating levels in short succession. Then the game picks levels that are in a specific range of difficulties – the upper bound of the difficulty grows a bit faster than the lower bound. This has the effect of slowly mixing in occasional “hard” levels without necessarily increasing the overall difficulty too quickly.
A new level is only ever selected if:
- It is beaten by the player
- The player skips it
- The player uses a robot to beat it
It is important that even if you lose a “game”, and run out of stones on a level that the player can still keep playing the level. Often it is enjoyable to play a difficult level, and it is also important that the player can (if they work hard enough) convince themselves that the level can be beaten. On the other hand, if a level is indeed too difficult, then skipping it needs to be possible (skipping levels is only disabled when they player is on their last stone – and a “game” only really starts when the player beats the first level).