LmCast :: Stay tuned in

The gold standard of optimization: A look under the hood of RollerCoaster Tycoon

Recorded: March 22, 2026, 10 p.m.

Original Summarized

The gold standard of optimization: A look under the hood of RollerCoaster Tycoon – Larst Of Us

Skip to content

Larst Of Us

AboutBlogroll

Bluesky
Mastodon
LinkedIn
RSS Feed

The gold standard of optimization: A look under the hood of RollerCoaster Tycoon

Due to some lucky circumstances, I recently had the chance to appear in one of the biggest German gaming podcasts, Stay Forever, to talk about the technology of RollerCoaster Tycoon (1999). It was a great interview, and I strongly recommend to listen to the whole episode here, at least if you speak german. If not, don’t worry—this article covers what was said (and a little more).

RollerCoaster Tycoon and its sequel are often named as some of the best-optimized games out there, written almost completely in Assembly by their creator, Chris Sawyer. Somehow this game managed to simulate full theme parks with thousands of agents on the hardware of 1999 without breaking a sweat. An immensely impressive feat, considering that even nowadays a lot of similar building games struggle to hit a consistent framerate.

So how did Chris Sawyer manage to achieve this?
There are a lot of answers to this question, some of them small and focused, some broad and impactful. The one which is mentioned first in most articles is the fact that the game was written in the low-level language Assembly, which, especially at the time of the game’s development, allowed him to write more performant programs than if he had used other high-level languages like C or C++.
Coding in Assembly had been the standard for game development for a long time but at this point in time was basically a given-up practice. Even the first Doom, which was released six years earlier, was already mostly written in C with only a few parts written in Assembly, and nobody would argue that Doom was in any way an unoptimized game.
It’s hard to check for sure, but it’s likely that RCT was the last big game developed in this way. How big the performance impact was at the time is hard to quantify, but for what it’s worth, it was probably higher than it would be nowadays. Compilers have gotten much better at optimizing high-level code, and many optimizations that you’d need to do manually back then can be handled by compilers nowadays.
But besides the use of assembly, the code of RCT was aggressively optimized. How do we know this if the source code has never been released? We have something that’s almost as good: A 100% compatible re-implementation of it, OpenRCT2.

Written by (very) dedicated fans, OpenRCT2 manages to reimplement the entirety of RollerCoaster 1&2, using the original assets. Even though this is NOT the original source code, especially in its earlier versions, this re-implementation is a very, very close match to the original, being based on years of reverse engineering. Note that by now, OpenRCT2 contains more and more improvements over the original code. I’ll note some of those changes as we come across them.
Also, I won’t go through all optimizations, but I will pick some examples, just to illustrate that every part of the game was optimized to the brink.
Types of Money
How would you store a money value in a game? You would probably start by thinking about the highest possible money value you might need in the game and choose a data type based on that. Chris Sawyer apparently did the same thing, but in a more fine-grained way.

Different money values in the code use different data types, based on what the highest expected value at that point is. The variable that stores the overall park value, for example, uses 4 bytes since the overall park value is expected to use quite high numbers. But the adjustable price of a shop item? This requires a far lower number range, so the game uses only one byte to store it. Note that this is one of the optimizations that has been removed in OpenRCT2, which changed all occurrences to a simple 8-byte variable, since on modern CPUs it doesn’t make a performance difference anymore.
Replacing math operations with bitshifting
When reading through OpenRCT2’s source, there is a common syntax that you rarely see in modern code, lines like this:

NewValue = OldValue << 2;

Thanks to operator overloading, the ‘<<’ operator can mean a lot of things in C++. What the line effectively does is the same as what most coders would write like this:

NewValue = OldValue * 4;

What the ‘<<’ operator does here is called bit shifting, meaning all the bits that store the value of the variable are shifted to the left, in this case by two positions, with the new digits being filled in with zeros. Since the number is stored in a binary system, every shift to the left means the number is doubled.
At first this sounds like a strange technical obscurity, but when multiplying numbers in the decimal system we basically do the same. When you multiply 57 * 10, do you actually ‘calculate’ the multiplication? Or do you just append a 0 to the 57? It’s the same principle just with a different numerical system.
The same trick can also be used for the other direction to save a division:

NewValue = OldValue >> 3;

This is basically the same as 

NewValue = OldValue / 8;

RCT does this trick all the time, and even in its OpenRCT2 version, this syntax hasn’t been changed, since compilers won’t do this optimization for you. This might seem like a missed opportunity but makes sense considering that this optimization will return different results for underflow and overflow cases (which the code should avoid anyway).
The even more interesting point about those calculations, however, is how often the code is able to do this. Obviously, bit shifting can only be done for multiplications and divisions involving a power of two, like 2, 4, 8, 16, etc. The fact that it is done that often indicates that the in-game formulas were specifically designed to stick to those numbers wherever possible, which in most modern development workflows is basically an impossibility. Imagine a programmer asking a game designer if they could change their formula to use an 8 instead of a 9.5 because it is a number that the CPU prefers to calculate with. There is a very good argument to be made that a game designer should never have to worry about the runtime performance characteristics of binary arithmetic in their life, that’s a fate reserved for programmers. Luckily, in the case of RCT the game designer and the programmer of the game are the same person, which also offers a good transition to the third big optimization:
Game Design for Performance
RCT was never a pure one-man-project, even though it is often described as one. All the graphics of the game and its add-ons, for example, were created by Simon Foster, while the sound was the responsibility of Allister Brimble.
But it’s probably correct to call it a Chris Sawyer Game, who was the main programmer and only game designer in unison.
This overlap in roles enables some profound optimizations, by not only designing the game based on the expected game experience, but also informed by the performance characteristics of those design decisions.
One great example for this is the pathfinding used in the game. When writing a game design document for a park building game, it’s very easy to design a solution in which guests first decide on which attraction they want to visit (based on the ride preferences of the individual guest), and then walk over to their chosen attraction.

From a tech point of view, this design, however, is basically a worst case scenario. Pathfinding is an expensive task, and running it for potentially thousands of agents at the same time is a daunting prospect, even on modern machines. 
That’s probably why the guest behavior in RCT works fundamentally different. Instead of choosing a ride to visit and then finding a path to it, the guests in RCT walk around the park, basically blind, waiting to stumble over an interesting ride by accident. They follow the current path, not thinking about rides or needs at all. When reaching a junction, they will select a new walking direction almost randomly, only using a very small set of extra rules to avoid dead ends, etc. 
This “shortcoming” is actually easy to spot in the game, when following a guest around the park for a while. They don’t walk anywhere on purpose, even when complaining about hunger and thirst, they wouldn’t think of looking for the nearest food stall, they just continue until they randomly walk by a food stall.
This doesn’t mean that RCT doesn’t do any pathfinding at all; there are cases where a traditional pathfinder is used. For example, if a mechanic needs to reach a broken ride or a guest wants to reach the park exit, those cases still require traditional, and therefore expensive, pathfinding.
But even for those cases, RCT has some safety nets installed to avoid framespikes. Most importantly, the pathfinder has a built-in limit on how far it is allowed to traverse the path network for an individual path request. If no path has been found before hitting this limit, the pathfinder is allowed to cancel the search and return a failure as result. As a player, you can actually see the pathfinder failures in real-time by reading the guest thoughts:

Yep, every time a park guest complains about not being able to find the exit, this is basically the Pathfinder telling the game that there might be a path, but for the sake of performance, it won’t continue searching for it.
This part is especially fascinating to me, since it turns an optimization done out of technical necessity into a gameplay feature. Something that can barely happen in “modern” game development, where the roles of coders and game designers are strictly separated. In case of the pathfinding limit, even more game systems were connected to it. By default, the pathfinder is only allowed to traverse the path network up to a depth of 5 junctions, but this limit isn’t set in stone. Mechanics, for example, are seen as more important for the gameplay than normal guests, which is why they are allowed to run the pathfinder with a search limit of 8 junctions.
But even a normal park guest is allowed to run the pathfinder for longer, for example by buying a map of the park, which is sold at the information kiosk.
When searching a path for a guest who bought a map, the pathfinder limit is increased from 5 to 7, making it easier for guests to find the park exit.
Changing the design of a game to improve its performance can seem like a radical step, but if done right, it can result in gains that no amount of careful micro-optimization could ever achieve.
Crowds without traffic jams
Another example of this is how RCT handles overcrowded parks. Congested paths are a common sight in every theme park, and obviously, the game also has to account for them somehow. But the obvious solution, implementing some form of agent collision or avoidance system, would do to the framerate what Kryptonite does to Superman.

The solution, again, is just to bypass the technical challenge altogether. The guests in RCT don’t collide with each other, nor do they try to avoid each other. In practice, even thousands of them can occupy the same path tile:
However, this doesn’t mean that the player doesn’t need to account for overcrowded parks. Even though guests don’t interact with guests around them, they do keep track of them. If too many other guests are close by, this will affect their happiness and trigger a complaint to the player. The outcome for the player is similar, as they still need to plan their layout to avoid too crowded paths, but the calculations needed for this implementation are a magnitude faster to handle.
RCT might have been the “perfect storm” for this specific approach to optimization, but this doesn’t mean that it can’t be done anymore, nowadays. It just means more dialogue between coders and game designers is needed, and often, the courage to say “No” to technical challenges. No matter how much you’d wish to solve them.
If you read my rumblings up to this point, you can follow me at Mastodon, Bluesky, or LinkedIn, or subscribe to this blog directly below this article. I publish new articles about game programming, Unreal, and game development in general about every month.
Share this:
Share on X (Opens in new window)
X

Share on Facebook (Opens in new window)
Facebook

Share on LinkedIn (Opens in new window)
LinkedIn

Share on Mastodon (Opens in new window)
Mastodon
Like Loading…

More posts to read

The gold standard of optimization: A look under the hood of RollerCoaster Tycoon
March 22, 2026

The curious case of Unreal’s slow asset reimport notification
February 14, 2026

I tried to use Unreal on Linux… it’s rough
January 31, 2026

How to run a city builder on 16 MHz
November 16, 2025

Speeding up the Unreal Editor launch by … not opening 5500 files?
September 27, 2025

Speeding up the Unreal Editor launch by … not spawning 38000 tooltips?
September 2, 2025

Profiling without Source code – how I diagnosed Trackmania stuttering
July 27, 2025

Upgrading Unreal’s Static Mesh Editor …again!
May 3, 2025

How “deleting multiplayer from the engine” can save memory
April 5, 2025

Next Page

Leave a comment Cancel reply

Δ

Type your email…


Subscribe

Mastodon

 

Loading Comments...

 

Write a Comment...

Email (Required)

Name (Required)

Website

Comment

Reblog

Subscribe

Subscribed

Larst Of Us

Join 40 other subscribers

Sign me up

Already have a WordPress.com account? Log in now.

Privacy

Larst Of Us

Subscribe

Subscribed

Sign up
Log in

Copy shortlink

Report this content

View post in Reader

Manage subscriptions

Collapse this bar

%d

RollerCoaster Tycoon (RCT), developed by Chris Sawyer, stands as a landmark achievement in game optimization, particularly notable for its consistent performance despite simulating thousands of agents within a theme park environment – a feat that continues to challenge modern game development. This article, stemming from an interview with Chris Sawyer on the German podcast “Stay Forever,” delves into the key techniques employed to achieve this remarkable level of optimization, primarily through a combination of low-level coding, aggressive optimization strategies, and a uniquely designed game system.

A central element of RCT’s success was Chris Sawyer’s utilization of Assembly language, a practice largely abandoned by the game development industry at the time. Assembly provided a significantly higher degree of control over hardware resources compared to higher-level languages like C or C++, resulting in performance advantages that were considerably more impactful than they would be in contemporary development. While other games, such as Doom, had utilized Assembly for specific sections, RCT was, according to the interview, one of the last major games developed almost entirely in this manner. The compiler technologies of the late 1990s were less sophisticated than those available today, meaning manual optimizations within Assembly were often substantially more impactful.

Alongside Assembly coding, the game’s code was systematically optimized. The exploration of OpenRCT2, a 100% compatible reimplementation of the original game, provides valuable insight into these optimizations. OpenRCT2, developed by dedicated fans, reflects years of reverse engineering and represents an incredibly close match to the original code, particularly in its more recent iterations. This allows for examination of the specific techniques employed.

One striking example is the handling of monetary values. The game utilizes different data types based on the expected value range, conserving memory and improving computation speed. For instance, the overall park value is stored in a 4-byte variable, while shop items require only a 1-byte variable, a choice now largely irrelevant due to modern CPU capabilities. However, this optimization has been removed in OpenRCT2, utilizing a single 8-byte variable, demonstrating how performance considerations evolve with technological advancements.

Another key optimization involved the substitution of traditional math operations with bitshifting. The code employs the left-shift operator (`<<`) for multiplication by powers of two and the right-shift operator (`>>`) for division by powers of two. This circumvents the performance overhead associated with standard multiplication and division operations, which, while faster in modern processors, weren’t as efficient in 1999. This technique highlights the meticulous attention to detail and the understanding of hardware architecture that Sawyer and his team exhibited. It’s important to note that compilers no longer perform this optimization automatically, necessitating its inclusion within the original code.

Furthermore, the game’s design choices were intrinsically linked to its performance. Chris Sawyer’s approach emphasized a “game design for performance” philosophy. A prime example is the guest behavior system. Rather than implementing a complex, computationally intensive pathfinding system for every guest, the game employs a deliberately “broken” approach: guests wander around the park randomly, stumbling upon attractions by chance. This dramatically reduces the processing burden on the system, preventing performance slowdowns and frame rate drops, particularly when numerous guests are present. This system isn’t simply a design flaw; it’s a strategic optimization.

The pathfinding system itself incorporates safety measures to prevent frame spikes. The pathfinder has a built-in limit on the maximum distance it will traverse to find a path, and if a path isn’t found within that limit, the search is aborted. This limitation is adjustable—mechanics and guests who have acquired a map of the park receive increased search allowances. This clever manipulation demonstrates how design choices can directly influence performance and even subtly shape gameplay.

Crowded park management also exemplifies optimization. The game bypasses the complex task of implementing agent collision or avoidance systems, allowing thousands of guests to occupy the same path tiles simultaneously. This decision is informed by the realization that the computational cost of resolving these collisions would severely impact performance. However, the game includes a system to monitor guest happiness, triggering complaints when guests feel overwhelmed, requiring the player to strategically manage the layout to avoid bottlenecks. The calculations for this system are significantly faster than traditional collision detection methods, showcasing a deliberate trade-off between technical complexity and performance.

These optimizations weren't solely the product of a single programmer; the game benefited from the collaborative efforts of additional developers, including Simon Foster (graphics) and Allister Brimble (sound). Chris Sawyer’s simultaneous role as both programmer and game designer allowed for a deeply integrated design-performance feedback loop, enabling critical decisions to be made with a nuanced understanding of the game’s technical limitations.

In essence, RollerCoaster Tycoon achieved its legendary status through a masterful blend of low-level coding, intelligent design choices, and a meticulous attention to detail. The techniques employed – from bitshifting to random guest behavior – underscore a profound understanding of hardware architecture, game design principles, and the importance of collaboration. OpenRCT2 continues to build upon these optimizations, solidifying RCT's legacy as a benchmark for game optimization.