[HN Gopher] The gold standard of optimization: A look under the ...
___________________________________________________________________
The gold standard of optimization: A look under the hood of
RollerCoaster Tycoon
Author : mariuz
Score : 102 points
Date : 2026-03-22 19:02 UTC (3 hours ago)
(HTM) web link (larstofus.com)
(TXT) w3m dump (larstofus.com)
| sroerick wrote:
| I had always heard about how RCT was built in Assembly, and
| thought it was very impressive.
|
| The more I actually started digging into assembly, the more this
| task seems monumental and impossible.
|
| I didn't know there was a fork and I'm excited to look into it
| mikkupikku wrote:
| Macros. Lots of macros.
| timschmidt wrote:
| And presumably generous use of code comments
| HelloUsername wrote:
| Fun read, thx! I'd also recommend more about RCT:
|
| "Interview with RollerCoaster Tycoon's Creator, Chris Sawyer
| (2024)" https://news.ycombinator.com/item?id=46130335
|
| "Rollercoaster Tycoon (Or, MicroProse's Last Hurrah)"
| https://news.ycombinator.com/item?id=44758842
|
| "RollerCoaster Tycoon at 25: 'It's mind-blowing how it inspired
| me'" https://news.ycombinator.com/item?id=39792034
|
| "RollerCoaster Tycoon was the last of its kind [video]"
| https://news.ycombinator.com/item?id=42346463
|
| "The Story of RollerCoaster Tycoon"
| https://www.youtube.com/watch?v=ts4BD8AqD9g
| fweimer wrote:
| What language is this article talking where compilers don't
| optimize multiplication and division by powers of two? Even for
| division of signed integers, current compilers emit inline code
| that handles positive and negative values separately, still
| avoiding the division instruction (unless when optimizing for
| size, of course).
| cjbgkagh wrote:
| It was written in assembly so goes through an assembler instead
| of a compiler.
| rawling wrote:
| I assume GP is talking about the bit in the article that goes
|
| > 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.
| shakow wrote:
| That's what I would have thought as well, but looks like that
| on x86, both clang and gcc use variations of LEA. But if
| they're doing it this way, I'm pretty sure it must be faster,
| because even if you change the x4 for a <<2, it will still
| generate a LEA.
|
| https://godbolt.org/z/EKj58dx9T
| adrian_b wrote:
| They use LEA for multiplying with small constants up to 9.
|
| For multiplying with powers of two greater or equal to 16,
| they use shift left, because LEA can no longer be used.
| londons_explore wrote:
| > it turns an optimization done out of technical necessity into a
| gameplay feature
|
| And this folks is why an optimizing compiler can never beat
| sufficient quantities of human optimization.
|
| The human can decide when the abstraction layers should be
| deliberately broken for performance reasons. A compiler cannot do
| that.
| timschmidt wrote:
| Agreed. It really requires an understanding of not just the
| software and computer it's running on, but the goal the
| combined system was meant to accomplish. Maybe some of us are
| starting to feed that sort of information into LLMs as part of
| spec-driven development, and maybe an LLM of tomorrow will be
| capable of noticing and exploiting such optimizations.
| applfanboysbgon wrote:
| > 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
|
| Numeric characteristics are _absolutely_ still a consideration
| for game designers even in 2026, one that influences what numbers
| they use in their game designs. The good ones, anyways. There
| are, of course, also countless bad developers /designers who
| ignore these things these days, but not because it is free to do
| so; rather, because they don't know better, and in many cases it
| is one of many silent contributing factors to a noticeable
| decrease in the quality of their game.
| edflsafoiewq wrote:
| Examples?
| andai wrote:
| https://en.wikipedia.org/wiki/Nuclear_Gandhi
|
| From what I heard, there was a Civilization game which
| suffered from an unsigned integer underflow error where
| Gandhi, whose aggression was set to 0, would become "less
| aggressive" due to some event in the game, but due to integer
| underflow, this would cause his aggression to go to 255,
| causing him to nuke the entire map.
|
| The article says this was just an urban legend though. Well,
| real or not, it's a perfect example of the principle!
| luaKmua wrote:
| Indeed an urban legend. Sid Meier himself debunked in his
| memoir, which is a pretty great read.
| Waterluvian wrote:
| Not really an example that proves any point, but one that
| comes to mind from a 20-year-old game:
|
| World of Warcraft (at least originally) encoded every item as
| an ID. To keep the database simple and small (given millions
| of players with many characters with lots of items): if you
| wanted to permanently enchant your item with an upgrade, that
| was represented essentially as a whole new item. The item was
| replaced with a different item (your item + enchant).
| Represented by a different ID. The ID was essentially a
| bitmask type thing.
|
| This meant that it was baked into the underlying data
| structures and deep into the core game engine that you could
| _never_ have more than one enchant at a time. It wasn 't like
| there was a relational table linking what enchants an item in
| your character's inventory had.
|
| The first expansion introduced "gems" which you could socket
| into items. This was basically 0-4 more enchants per item.
| The way they handled this was to just lengthen item Ids by a
| whole bunch to make all that bitmask room.
|
| I might have gotten some of this wrong. It's been forever
| since I read all about these details. For a while I was
| obsessed with how they implemented WoW given the sheer scale
| of the game's player base 20 years ago.
| hcs wrote:
| Not the same thing but I was reminded of a joke about the
| puzzle game Stephen's Sausage Roll:
|
| > I have calculated the value of Pi on Sausage Island and
| found it to be 2.
|
| https://web.archive.org/web/20240405034314/https://twitter.c.
| ..
| mort96 wrote:
| I think Minecraft's lighting system is a good example: there
| are 16 different brightness levels, from 0 to 15. This allows
| the game to store light levels in 4 bytes per block.
|
| Similarly, redstone has 16 power levels: 0 to 15. This allows
| it to store the power level using 4 bits. In fact, quite a
| lot of attributes in Minecraft blocks are squeezed into 4
| bits. I think the system has grown to be more flexible these
| days, but I'm pretty sure the chunk data structure used to
| set aside 4 bits for every block for various metadata.
|
| And of course, the world height used to be at 255 blocks.
| Every block's Y position could be expressed as an 8-bit
| integer.
|
| A voxel game like that is a good example of where this kind
| of efficiency really matters since there's just _so much
| data_. A single 16 _16_ 256 chunk is 65.5k blocks. If a game
| designer says they want to add a new light source with
| brightness level 20, or a new kind of redstone which can go
| 25 blocks, it might very well be the right choice to say no.
| ErroneousBosh wrote:
| Going way back into history, the Alesis MIDIVerb reverb unit
| had a really simple DSP core made out of discrete logic
| chips. It could add a memory location to an accumulator and
| divide it by two, invert it, add it and divide it by two, or
| store it in ram either inverted or not and divide the
| accumulator by two.
|
| Four instructions, in about eight chips.
|
| By combining shifts and adds Keith Barr was able to devise
| all the different filter and delay coefficients for 63
| different reverb programs (the 64th one was just dead
| passthrough).
| timschmidt wrote:
| Absolutely. I have written a small but growing CAD kernel which
| is seeing use in some games and realtime visualization tools (
| https://github.com/timschmidt/csgrs ) and can say that
| computing with numbers isn't really even a solved problem yet.
|
| All possible numerical representations come with inherent
| trade-offs around speed, accuracy, storage size, complexity,
| and even the kinds of questions one can ask (it's often not
| meaningful to ask if two floats equal each other without an
| epsilon to account for floating point error, for instance).
|
| "Toward an API for the Real Numbers" (
| https://dl.acm.org/doi/epdf/10.1145/3385412.3386037 ) is one of
| the better papers I've found detailing a sort of staged
| complexity technique for dealing with this, in which most
| calculations are fast and always return (arbitrary precision
| calculations can sometimes go on forever or until memory runs
| out), but one can still ask for more precise answers which
| require more compute if required. But there are also other
| options entirely like interval arithmetic, symbolic algebra
| engines, etc.
|
| One must understand the trade-offs else be bitten by them.
| lukan wrote:
| "and in many cases it is one of many silent contributing
| factors to a noticeable decrease in the quality of their game"
|
| Game designers are not so constrained anymore by the limits of
| the hardware, unless they want to push boundaries. Quality of a
| game is not just the most efficient runtime performance - it is
| mainly a question if the game is fun to play. Do the mechanics
| work. Are there severe bugs. Is the story consistent and the
| characters relatable. Is something breaking immersion. So ...
| frequent stuttering because of bad programming is definitely a
| sign of low quality - but if it runs smooth on the targets
| audience hardware, improvements should be rather done
| elsewhere.
| scns wrote:
| > it is mainly a question if the game is fun to play.
|
| 10000x this. Miyamoto starts with a rudimentary prototype and
| asks himself this. Sadly it seems for many fun is an
| afterthought they try to patch in somehow.
| sublinear wrote:
| This way of thinking has caused at least a few prominent
| recurring bugs I can think of.
|
| Texture resolution mismatches causing blurriness/aliasing,
| floating point errors and bad level design causing collision
| detection problems (getting stuck in the walls), frame rate
| and other update rates not being synced causing stutter and
| lag (and more collision detection problems), bad illumination
| parameters ruining the look they were going for, numeric
| overflow _breaking everything_ , bad approximations of
| constants also breaking everything somewhere eventually,
| messy model mesh geometry causing glitches in texturing,
| lighting, animation, collision, etc.
|
| There's probably a lot more I'm not thinking of. They have
| nothing to do "with the hardware", but the underlying math
| and logic.
|
| They're also not bugs to "let the programmer figure out".
| Good programmers and designers work together to solve them. I
| could just as easily hate on the many _criminally ugly,
| awkward, and plain unfun_ games made by programmers working
| alone, but I 'll let someone else do that. :)
| lukan wrote:
| Game designer != game engine designer
|
| (But it definitely helps if the game designer knows of the
| technical limits)
| sublinear wrote:
| Sorry, I'm not super familiar with professional game dev,
| but I am familiar with professional web dev. The problems
| seem similar, as evidenced by the constant complaining
| here on HN about the state of the web.
|
| Who formats or cleans up the assets and at least oversees
| that things are done according to a consistent spec,
| process, and guidelines? Is that not a game designer or
| someone under their leadership?
|
| I think in all the cases I gave, what might be completely
| delegated to "engine design" really should be teamwork
| with game design and art direction too. This is what the
| top-level comment was talking about. Even when a game is
| "well made", they just adopted someone else's standards
| and that sucks all the soul out of it. This is a common
| problem in all creative work.
| lukan wrote:
| It depends how big the studio is, but a job of a game
| designer is usually not cleaning up assets. It is to
| well, design the game. The big picture.
| lefty2 wrote:
| > The same trick can also be used for the other direction to save
| a division:
|
| > NewValue = OldValue >> 3;
|
| You need to be careful, because this doesn't work if the value is
| negative. A
| whizzter wrote:
| Most CPU's has signed and unsigned right shift instructions
| (left shift is the same), so yes it works (You can test this in
| C by casting a signed to unsigned before shifting).
|
| The biggest caveat is that right shifting -1 still produces -1
| instead of 0, but that's usually fine for much older game
| fixed-point maths since -1 is close enough to 0.
| bluelightning2k wrote:
| Great write up. Thank you. Really great!
|
| I was reminded of the factorio blog. That game's such a huge
| optimization challenge even by today's standards and I believe
| works with the design.
|
| One interesting thing I remember is if you have a long conveyor
| belt of 10,000 copper coils, you can basically simplify it to
| just be only the entry and exit tile are actually active. All the
| others don't actually have to move because nothing changes... As
| long as the belts are fully or uniformly saturated. So you avoid
| mechanics which would stop that.
| youarentrightjr wrote:
| > 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_.
|
| (emphasis mine)
|
| Not at all true. Assuming the types are such that >> is
| equivalent to /, modern compilers will implement division by a
| power of two as a shift every single time.
___________________________________________________________________
(page generated 2026-03-22 23:00 UTC)