header image

Code complexity

Everything takes longer than you think. Except doing the dishes, I’ve got that figured out. But coding, yeah, coding takes ages. As I’ve seen it said, 80% of the work takes 10% of the time you think it will, and the other 20% takes 800%.

What chewed through the the first three months of my coding time on Mini Metro was not what I expected at all. It was one aspect of the prototype that literally took five minutes to write, but had so many more demands made on it by the extended design of Mini Metro that the code complexity ballooned out by, at an estimate (which is therefore most likely incorrect), two orders of magnitude.

Mind the Gap’s track

In Mind the Gap, the track that connects stations consists of a single straight line. The train travels along the track at a constant speed, and inherits the rotation of the track it is travelling on. That was a simple piece of code to write. It did have some issues, such as multiple tracks between stations displaying on top of each other, but nothing that was unshippable for a Ludum Dare entry.

Mini Metro’s track

For Mini Metro, we quickly identified many things we wanted to change or improve from Mind the Gap, such as:

  • Track will only leave stations at a multiple of 45° (north, northwest, west, etc.).
  • Where multiple tracks leave a station at the same angle, the second and subsequent tracks will run alongside the first to avoid running over the top of each other.
  • The track between stations would be made up of either one or two straight lines, each at a rotation that is a multiple of 45°. In the case of two lines, they will meet at a rounded corner.

The goal of these new rules was to improve the aesthetic of the game. We wanted it to look much more like the classic stylised map of the London Underground map (hence the obsession with 45° angles). As you can imagine there was a non-trivial amount of work implementing all of these, especially compared to Mind the Gap’s technique.

There were, as always, some unexpected complexities. The worst of these was the requirement to create a ‘weld’ wherever two connecting pieces of track do not meet up at a station. This occurs when the track comes in or out of a station at an offset (due to the second rule, above) and also bends at the station.

So boo hoo Pete, writing a game means writing code so get over yourself, right? Yeah, you probably have a point. But hear me out!

Complexity++

As I was ironing out the track code, it occurred to me that other improvements we had in the pipeline were going to wreak havoc with the track code. A couple of examples are:

  • The first or last section of track in a route can be removed by dragging a terminator back over the station. If the train is on that track, the track is still ‘live’, but scheduled for removal once the train has left the track.
  • Carriages can be added to trains to increase their carrying capacity. Additional carriages fall in behind each other along the track.

The first improvement was in direct response to one of the biggest criticisms of Mind the Gap; editing routes was a nightmare if you wanted to do anything more than add a station to the beginning or end. We identified is a critical feature to turning the prototype into Mini Metro. Without it, it was obvious track editing was frustrating players.

However it added one significant unexpected complexity – by scheduling track for deletion and immediately adding a new link, you could now make Y-junctions where three tracks on the same route connected together. Theoretically, once you added multiple carriages and therefore extended the number of tracks the train could be on simultaneously, you could even connect four or more tracks.

What this did was just explode the possibilities of track connections. Where before a track connection was a simple one-to-one affair, we know had one-to-many. This was the straw that almost broke the camel’s back; the complexity of the track code exploded, and I was this close (holds thumb and forefinger really close together) to leaving out track editing altogether.

Another common request that we decided to implement, circular routes, threw another spanner in the works when combined with Y-junctions. I’ve bored you enough with details already so I won’t try to explain that one!

So what I’ve had is a lesson on how easy it can be for several seemingly-unrelated design decisions to have a non-linear impact on code complexity. This is important for me to keep an eye out for if we want to ever publish multiple titles a year. Heck, even to ship one at all!