[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  : 563 points
       Date   : 2026-03-22 19:02 UTC (1 days 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
        
           | cogman10 wrote:
           | Yup. I've done a bit of assembly and it's really only a
           | little harder than doing C. You simply have to get familiar
           | with your assembler and the offered macros. Heck, I might
           | even say that it's simpler than basic.
        
         | kevincox wrote:
         | Programming in assembly isn't really "hard" it mostly takes
         | lots of discipline. Consistency and patterns are key. The
         | language also provides very little implicit documentation, so
         | always document which arguments are passed how and where, what
         | registers are caller and callee saved. Of course it is also
         | very tedious.
         | 
         | Now writing very optimized assembly is very hard. Because you
         | need to break your consistency and conventions to squeeze out
         | all the possible performance. The larger "kernel" you optimize
         | the more pattern breaking code you need to keep in your head at
         | a time.
        
         | markus_zhang wrote:
         | Back then a lot of people started with assembly because that
         | was the only way to make games quick enough. Throughout the
         | years they accumulated tons of experience and routines and
         | tools.
         | 
         | Not saying that it was not a huge feat, but it's definitely a
         | lot harder to start from scratch nowadays, even for the same
         | platform.
        
         | mrguyorama wrote:
         | >I didn't know there was a fork and I'm excited to look into it
         | 
         | OpenRCT2 isn't a fork, it's like OpenTTD, a recreation.
         | 
         | Go look at GDC's Classic Game Postmortems. They have tens of
         | videos of the people who built famous games from the 80s and
         | 90s who often go into technical details of how they do it. For
         | example, Robotron goes into how the code works.
         | 
         | It's remarkably familiar. They basically built object oriented
         | programming and classes using convention only. You treat every
         | actor you want to work with as a chunk of memory with standard
         | layout that includes pointers for behavior and slots for state,
         | and you just try really hard to only operate on the right
         | "Types" at the right places. From there you have your standard
         | game loop of "Get input, update all Actors, render, loop"
         | 
         | The Pitfall postmortem is wonderful. The Atari 2600 had roughly
         | zero RAM to work with, and barely any cartridge space to hold
         | your game. To make their large, somewhat open world, they made
         | each screen built off just a few parameters, and created a
         | bidirectional psuedorandomish function that would generate the
         | parameters on a cycle, giving you a connected map space!
        
       | 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.
        
             | cjbgkagh wrote:
             | That makes more sense, I second their sentiment, modern
             | compilers will do this. I guess the trick is knowing to use
             | numbers that have these options.
        
               | bombcar wrote:
               | There was a recent article on HN about which compiler
               | optimizations would occur and which wouldn't and it was
               | surprising in two ways - first, it would make some that
               | you might not expect, and it would _not_ make others that
               | you would - because in some obscure calling method, it
               | wouldn 't work. Fixing that path would usually get the
               | expected optimization.
        
         | 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
           | (not only with powers of two, but also with 3, 5 and 9; even
           | more values could be achieved with two LEA, but it may not be
           | worthwhile).
           | 
           | For multiplying with powers of two greater or equal to 16,
           | they use shift left, because LEA can no longer be used.
        
           | shaggie76 wrote:
           | Not only is LEA more flexible I believe it's preferred to SHL
           | even for simple operations because it doesn't modify the
           | flags register which can make it easier to schedule.
        
             | Cold_Miserable wrote:
             | shlx doesn't alter the flag register.
        
               | fweimer wrote:
               | SHLX does not support an immediate operand. Non-
               | destructive shifts with immediate operands only arrive
               | with APX, where they are among the most commonly used
               | instructions (besides paired pushes/pops).
        
             | fweimer wrote:
             | It's more about the non-destructive destination part, which
             | can avoid a move. Compilers tend to prefer SHL/SAL of LEA
             | because its encoding is shorter:
             | https://godbolt.org/z/9Tsq3hKnY
        
           | Validark wrote:
           | Using an lea is better when you want to put the result in a
           | different register than the source and/or you don't want to
           | modify the flags registers. shlx also avoids modifying flags,
           | but you can't shift by an immediate, so you need to load the
           | constant into a register beforehand. In terms of speed, all
           | these options are basically equivalent, although with very
           | slightly different costs to instruction caches and the
           | register renaming in the scheduler. In terms of execution, a
           | shift is always 1 cycle on modern hardware.
        
         | orthoxerox wrote:
         | Well, Sawyer started writing Transport Tycoon in 1992, when
         | free or affordable C compilers were not as widely available.
         | Turbo C was never known for optimizations. GCC 1.40 was good
         | enough for Linus, but I guess Chris was already a good assembly
         | programmer.
        
       | 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.
        
         | gwern wrote:
         | End-to-end optimization in action! Although I'd've liked more
         | than 1 example (pathfinding) here.
        
         | nulltrace wrote:
         | The LEA-vs-shift thread here kind of proves the point.
         | Compilers are insanely good at that stuff now. Where they
         | completely fall short is data layout. I had a message parser
         | using `std::map<int, std::string>` for field lookup and the fix
         | was just... a flat array indexed by tag number. No compiler is
         | ever going to suggest that. Same deal with allocation. I spent
         | a while messing with SIMD scanning and consteval tricks chasing
         | latency, and the single biggest win turned out to be boring.
         | Switched from per-message heap allocs to a pre-allocated buffer
         | with `std::span` views into the original data. ~12 allocations
         | per message down to zero. Compiler will optimize the hell out
         | of your allocator code, it just won't tell you to stop calling
         | it.
        
         | hrmtst93837 wrote:
         | If you think compilers can't punch through abstractions, you
         | haven't seen what whole-program optimization does to an
         | overengineered stack when the programer gives it enough
         | visibility. They still miss intent, so the game-specific hack
         | can win.
        
       | 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.
        
               | mrguyorama wrote:
               | It's fascinating to live through the entire lifecycle of:
               | 
               | Weird thing happens. People make up reasons why. One
               | reason is _possible_. That becomes THE reason, and spread
               | wildly, without confirmation, as an accurate explanation.
               | "Actually that's not true". Now that not being the reason
               | is widely disseminated and if we are lucky the original
               | meme dies out!
               | 
               | But it took 30 years. For a very meaningless rumor.
        
           | 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.
        
             | tosti wrote:
             | I don't think Minecraft would be considered a cornerstone
             | of optimal programming.
        
               | kulahan wrote:
               | The entire program doesn't need to be a cornerstone of
               | optimal programming for this one example to hold true.
        
               | helterskelter wrote:
               | The 4 bit stuff is a hangover from Mojang having to
               | squeeze every bit of perf from their Java based engine
               | that they could. Their original sound engine was so
               | sketchy that C418's (music composer) minimalist sound is
               | partly because it really couldn't handle much more than
               | what got released.
               | 
               | MS has been loosening up on the 4 bits limit and have
               | created a CPP variant of Minecraft which performs better,
               | but they've also introduced their unified login garbage
               | that has almost made me give up Minecraft completely.
        
               | Pannoniae wrote:
               | Hey, this isn't entirely accurate!
               | 
               | The 4-bit stuff is a hangover from _Notch_ doing this (I
               | 'd maybe even say a similar-calibre programmer to Chris
               | Sawyer...). The sound has nothing to do with technical
               | limits, that's a post-facto rationalisation.
               | 
               | The game never played midi samples, it was always playing
               | "real" audio. The style was an artistic choice, many
               | similar retro-looking games were using chiptune and the
               | sorts. It's a deliberate juxtaposition...
               | 
               | The CPP variant doesn't really perform better anymore
               | either.
        
               | helterskelter wrote:
               | Fair enough, I mostly meant to point out some of those
               | design decisions predate MS, as much as I love to hate on
               | them. The music was just an interesting bit of trivia I
               | read the other day.
        
               | Pannoniae wrote:
               | Yeah, 100% :) Ironically, the design constraints are one
               | of the big things which made it work so much! If it was
               | designed in a "traditional" way, it would have been much
               | less ambitious.
        
               | imtringued wrote:
               | Bedrock Edition has a smaller simulation distance, which
               | is kind of the opposite you'd expect from the more
               | "optimized" version.
        
               | mort96 wrote:
               | Minecraft is, and always has been, handling vast amounts
               | of data at pretty good performance. It's not an
               | impossibly difficult task, many other people have made
               | voxel game engines which are better, but it's something
               | you can't do without paying attention to these things.
               | Every voxel engine with remotely reasonable performance
               | needs to carefully count bits used per block.
        
               | nitwit005 wrote:
               | You can find other people discussing implementing similar
               | games on YouTube, and the need to cram the representation
               | of blocks into as small a size as possible always comes
               | up.
               | 
               | Information about blocks is the overwhelmingly dominant
               | thing being stored in memory for those games, so
               | naturally reducing the size of that data becomes
               | important.
        
           | 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).
        
           | bombcar wrote:
           | Read all of the Factorio Friday Facts
           | https://factorio.com/blog/ - a number of the more obscure
           | bug/performance issues come down to making something fit
           | naturally into a value the CPU can handle.
        
           | plopz wrote:
           | One of the main issues with Kerbal Space Program is
           | instability caused by floating point numbers. I know
           | Starcraft 2 was built upon integers.
        
             | Gigachad wrote:
             | Floating point issues are less a problem of performance
             | here but one of precision. Particularly being a space game,
             | the coordinates can be massive resulting in the precision
             | deteriorating enough to cause issues.
        
         | 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.
        
           | ryandrake wrote:
           | Back in the early, early days, the game designer _was_ the
           | graphic designer, who _also was_ the programmer. So,
           | naturally, the game 's rules and logic aligned closely with
           | the processor's native types, memory layout, addressing,
           | arithmetic capabilities, even cache size. Now we have
           | different people doing different roles, and only one of them
           | (the programmer) _might_ have an appreciation for the
           | computer 's limits and happy-paths. The game designers and
           | artists? They might not even know what the CPU does or what a
           | 32 bit word even means.
           | 
           | Today, I imagine we have conversations like this happening:
           | 
           | Game designer: We will have 300 different enemy types in the
           | game.
           | 
           | Programmer: Things could be really, really faster if you
           | could limit it to 256 types.
           | 
           | Game designer: ?????
           | 
           | That ????? is the sign of someone who is designing a computer
           | program who doesn't understand the basics of computers.
        
             | WalterBright wrote:
             | I wrote the Intellivision Mattel Roulette cartridge game
             | back in the 1970s. It was all in assembler on a 10 bit (!)
             | CPU. In order to get the game to fit in the ROM, you had to
             | do every feelthy dirty trick imaginable.
        
               | DougN7 wrote:
               | I would love to hear more about that.
        
               | WalterBright wrote:
               | I wish I'd kept a listing of that and other projects I
               | worked on. But that never occurred to me.
               | 
               | A friend of mine wrote the Mattel Intellivision poker
               | game. I was playtesting it (a very boring job), and got
               | suspicious. I walked over to his desk and said the
               | program was cheating. It was looking at my hole cards. He
               | sighed and asked how I knew, and I replied it was
               | obvious. He said he didn't have room to add code to
               | improve its play otherwise. I don't know if he fixed it
               | or not.
        
               | biglost wrote:
               | Please, write a comment, pastebin, gist or whatever, I
               | would love to read it, that are stories in computer
               | science i enjoy the most
        
             | applfanboysbgon wrote:
             | Not all, or even most, games are made by billion dollar
             | studios. Overlapping roles are still the norm in small
             | studios. And even those that do have bespoke designer roles
             | would likely benefit from telling them that computers have
             | certain limitations where trade-offs in game design need to
             | be selected for, because many AAA games run like shit. Many
             | times for reasons other than the game design, sure, but
             | also sometimes because of ways that could be worked around
             | more easily if the game design were accomodating the
             | tradeoffs.
        
         | 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.
        
             | calvinmorrison wrote:
             | When Halo 2 (anniversary edition? ) was released there was
             | also a video on in the game about the development. The
             | point that always stuck with me was "you must nail that 2
             | seconds that will keep people playing forever". The core
             | mechanic of that game is just excellent.
        
               | ukuina wrote:
               | "30 seconds of fun"
               | 
               | https://youtu.be/0q69Msy8ttM?t=287
        
           | 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.
               | 
               | (adding this due to reply depth): Coordination is a big
               | aspect of design and can often be the most impactful to
               | the result.
        
               | 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.
        
             | WalterBright wrote:
             | > getting stuck in the walls
             | 
             | I remember the early Simpsons video game. Sometimes, due to
             | some bug in it (probably a sign error), you could go
             | through the walls and see the rendered scenery from the
             | other side. It was like you went backstage in a play. It
             | would have made a great Twilight Zone episode!
        
               | lukan wrote:
               | Those bugs I experienced in all sorts of games and via
               | cheats, fly mode, I intentionally went backstage to
               | explore.
        
               | jkestner wrote:
               | That immediately made me think of the Treehouse of Horror
               | episode where Homer got stuck in the third dimension.
        
               | WalterBright wrote:
               | Maybe that episode was inspired by the game bug!
        
           | crq-yml wrote:
           | There's an artistic thread in game coding - one that isn't
           | the norm, but which I think RCT is exemplary of - that holds
           | that mechanical sympathy is important to the game design
           | process. A limit set around NPOT maximums and divisions and
           | lengths of pathfinding is allowing the machine to opine, "you
           | will actually do less work if you set the boundary here".
           | Setting those limits tends to inform the shape of resulting
           | assets as something tiny and easy to hardcode.
           | 
           | The thing that changed during the 90's is that mechanical
           | sympathy became optional to achieving a large production. The
           | data input defining the game world was decoupled into assets
           | authored in disconnected ways and "crunched down" to
           | optimized forms - scans, video, digital painting, 3D models.
           | RCT exhibits some of this, too, in that it's using PCM audio
           | samples and prerendered sprites. If the game weren't also a
           | massive agent simulator it would be unremarkable in its era.
           | But even at this time more complex scripting and treating
           | gameplay code as another form of asset was becoming
           | normalized in more genres.
           | 
           | From the POV of getting a desired effect and shipping
           | product, it's irrelevant to engage with mechanical sympathy,
           | but it turns out that it's a thing that players gradually
           | unravel, appreciate and optimize their own play towards if
           | they stick with it and play to competitive extremes,
           | speedrun, mod, etc.
           | 
           | The 64kb FPS QUOD released earlier this year is a good
           | example of what can happen by staying committed to this
           | philosophy even today: the result isn't particularly
           | ambitious as a game design, but it isn't purely a tech demo,
           | nor does it feel entirely arbitrary, nor did it take an
           | outrageous amount of time to make(about one year, according
           | to the dev).
        
         | lifis wrote:
         | That makes no sense since multiplication has been fast for the
         | last 30 years (since PS1) and floating point for the last 25
         | years (since PS2) and anyway numbers relevant for game design
         | are usually used just a few times per frame so only program
         | size matters, which has not been significantly constrained for
         | the last 40 years (since NES)
        
           | applfanboysbgon wrote:
           | I wasn't talking about the specific example in the article.
           | There are many, many other ways in which numeric
           | characteristics can constrain game design, particularly if
           | your game has any kind of scale to it (say, simulations with
           | tons of moving parts or many NPCs, like RCT, or large open
           | worlds like Minecraft, or large multiplayer games like WoW,
           | as examples all mentioned in the thread).
           | 
           | If your game is small-scale, something like Super Mario
           | Bros., you _should_ be able to get away with not thinking
           | about it in theory. But even then people manage to write
           | simple games with bloated loading times and stuttery
           | performance, so never underestimate the impressive ability of
           | people who are operating solely at the highest level of
           | abstraction to make computers cry.
        
         | cogman10 wrote:
         | > 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.
         | 
         | I used to think like this, not anymore.
         | 
         | What convinced me that these sort of micro-optimizations just
         | don't matter is reading up on the cycle count of modern
         | processors.
         | 
         | One a Zen 5, Integer addition is a single cycle, multiplication
         | 3, and division ~12. But that's not the full story. The CPU can
         | have 5 inflight multiplications running simultaneously. It can
         | have about 3 divisions running simultaneously.
         | 
         | Back in the day of RCT, there was much less pipelining. For the
         | original pentium, a multiplication took 11 cycles, division
         | could take upwards of 46 cycles. These were on CPUs with 100
         | Mhz clock cycles. So not only did it take more cycles to
         | finish, couldn't be pipelined, the CPUs were also operating at
         | 1/30th to 1/50th the cycle rate of common CPUs today.
         | 
         | And this isn't even touching on SIMD instructions.
         | 
         | Integer tricks and optimizations are pointless. Far more
         | important than those in a modern game is memory layout. That's
         | where the CPU is actually going to be burning most it's time.
         | If you can create and do operations on a int[], you'll be MUCH
         | faster than if you are doing operations against a Monster[]. A
         | cache miss is going to mean anywhere from a 100 to 1000 cycle
         | penalty. That blows out any sort of hit you take cutting your
         | cycles from 3 to 1.
        
           | Pannoniae wrote:
           | This is all true but IMO forest for the trees.... For example
           | the compiler basically doesn't do anything useful with your
           | float math unless you enable fastmath. Period. Very few
           | transformations are done automatically there.
           | 
           | For integers the situation is better but even there, it
           | hugely depends on your compiler and how much it cheats. You
           | can't replace trig with intrinsics in the general case (sets
           | errno for example), inlining is at best an adequate heuristic
           | which completely fails to take account what the hot path is
           | unless you use PGO and keep it up to date.
           | 
           | I've managed to improve a game's worst case performance
           | better by like 50% just by shrinking a method's codesize from
           | 3000 bytes to 1500. Barely even touched the hot path there,
           | keep in mind. Mostly due to icache usage.
           | 
           | The takeaway from this shouldn't be that "computers are fast
           | and compilers are clever, no point optimising" but more that
           | "you can afford not to optimise in many cases, computers are
           | fast."
        
             | WalterBright wrote:
             | I originally got into writing compilers because I was
             | convinced I could write a better code generator. I
             | succeeded for about 10 years in doing very well with code
             | generation. But then all the complexities of the evolving
             | C++ (and D!) took up most of my time, and I haven't been
             | able to work much on the optimizer since.
             | 
             | Fortunately, D compilers gdc and ldc take advantage of the
             | gcc and llvm optimizers to stay even with everyone else.
        
               | tialaramex wrote:
               | The thing which would really help IMNSHO is to nail down
               | the IR to eliminate weird ambiguities where OK
               | optimisation A is valid according to one understanding,
               | optimisation B is valid under another but alas if we use
               | both sometimes it breaks stuff.
        
               | WalterBright wrote:
               | Yes, one of the unexpected problems I ran into is one
               | optimization undoing another one, and the optimizer would
               | flip-flop between the two states.
        
             | cogman10 wrote:
             | I actually agree with you.
             | 
             | My point wasn't "don't optimize" it was "don't optimize the
             | wrong thing".
             | 
             | Trying to replace a division with a bit shift is an example
             | of worrying about the wrong thing, especially since that's
             | a simple optimization the compiler can pick up on.
             | 
             | But as you said, it can be very worth it to optimize around
             | things like the icache. Shrinking and aligning a hot loop
             | can ensure your code isn't spending a bunch of time loading
             | instructions. Cache behavior, in general, is probably the
             | most important thing you can optimize. It's also the thing
             | that can often make it hard to know if you actually
             | optimized something. Changing the size of code can change
             | cache behavior, which might give you the mistaken
             | impression that the code change was what made things faster
             | when in reality it was simply an effect of the code
             | shifting.
        
           | rcxdude wrote:
           | Also, it's unusual for a game to be CPU bottlenecked
           | nowadays, and if it is, it's probably more constrained on
           | memory bandwidth than raw FLOPS.
        
             | eru wrote:
             | Yes, though it depends a bit on your style of game.
        
           | moregrist wrote:
           | > Integer tricks and optimizations are pointless.
           | 
           | They're not pointless; they're just not the first thing to
           | optimize.
           | 
           | It's like worrying about cache locality when you have an
           | inherently O(n^2) algorithm and could have a O(n log n) or
           | O(n) one. Fix the biggest problem first.
           | 
           | Once your data layout is good and your cpu isn't taking a 200
           | cycle lunch break to chase pointers, then you worry about
           | cycle count and keeping the execution units fed.
           | 
           | That's when integer tricks can matter. Depending on the micro
           | arch, you may have twice as many execution units that can
           | take integer instructions. And those instructions (outside of
           | division) tend to have lower latency and higher throughput.
           | 
           | And if you're doing SIMD, your integer SIMD instructions can
           | be 2 or 4x higher throughput than float32 if you can use
           | int16 / int8 data.
           | 
           | So it can very much matter. It's just usually not the lowest
           | hanging fruit.
        
             | Dylan16807 wrote:
             | > And if you're doing SIMD, your integer SIMD instructions
             | can be 2 or 4x higher throughput than float32 if you can
             | use int16 / int8 data.
             | 
             | Your float instructions can also be 2x the throughput if
             | you use f16. With no need to go for specific divisors.
             | 
             | For values that even can pack into 8 bits, you rarely have
             | a way to process enough at once to actually get more
             | throughput than with wider numbers.
             | 
             | I'm sure there's _a_ program where it very much matters,
             | but my bet is on it not even mildly mattering, and there
             | basically always being a hundred more useful optimizations
             | to work on.
        
               | benchloftbrunch wrote:
               | Problem with f16 is that hardware support is still "new"
               | and can't be relied on in consumer grade CPUs yet.
        
           | YesBox wrote:
           | > A cache miss is going to mean anywhere from a 100 to 1000
           | cycle penalty. That blows out any sort of hit you take
           | cutting your cycles from 3 to 1.
           | 
           | A good example of this is using std::vector<bool> vs.
           | std::vector<uint8_t> in the debug build vs release build.
           | 
           | vector<bool> is much slower to access (it's a dynamic
           | bitset). If you have a hot part of the code that frequently
           | touches a vector<bool>, you'll see a multiple X slowdown in
           | the debug build.
           | 
           | However, in the release build, there is no performance
           | difference between the two (for me at least, I'm making a
           | fairly complicated game). The cache misses bury it.
        
             | fragmede wrote:
             | Fascinating, that's counterintuitive. I'd think the point
             | of using vector <bool> is because the compiler would
             | optimize it to be a bit field which is fewer bits and thus
             | smaller and thus faster than using vector <uint_t8>. How
             | did you come to figure that out?
        
               | YesBox wrote:
               | I dont know how it's implemented by the standard/compiler
               | (not my domain). The performance differences are well
               | documented though.
               | 
               | I've used both in my pathing code and tested each in
               | debug/release.
               | 
               | Even if the std:: implementation was as fast as possible,
               | you're still adding bit manipulation on top of accessing
               | the element, so it will be slower no matter what you do.
        
         | exmadscientist wrote:
         | Related to that, for a consumer electronics product I worked on
         | using an ARM Cortex-M4 series microcontroller, I actually ended
         | up writing a custom pseudorandom number generation routine
         | (well, modifying one off the shelf). I was able to take the
         | magic mixing constants and change them to things that could be
         | loaded as single immediates using the crazy Thumb-2 immediate
         | instructions. It passed every randomness test I could throw at
         | it.
         | 
         | By not having to pull in anything from the constant pools and
         | thereby avoid memory stalls in the fast path, we got to use
         | random numbers profligately and still run quickly and
         | efficiently, and get to sleep quickly and efficiently. It was a
         | fun little piece of engineering. I'm not sure how much it
         | mattered, but I enjoyed writing it. (I think I did most of it
         | after hours either way.)
         | 
         | Alas, I don't think it ever shipped because we eventually moved
         | to an even smaller and cheaper Cortex-M0 processor which lacked
         | those instructions. Also my successor on that project threw
         | most of it out and rewrote it, for reasons both good and bad.
        
         | rkagerer wrote:
         | Now _that 's_ what being a _full_ stack programmer really
         | means.
        
         | WalterBright wrote:
         | I remember the older driving games. They'd progressively
         | "build" the road as you progressed on it. Curves in the road
         | were drawn as straight line segments.
         | 
         | Which wasn't a problem, but it clearly showed how the
         | programmers improvised to make it perform.
        
           | Sharlin wrote:
           | Limiting the drawing distance and rendering as little
           | geometry as possible is absolutely still a thing, devs just
           | can afford to hide it better these days. The golden rule of
           | graphics programming has always been "cheat as much as you
           | can get away with, and then a bit more".
        
         | danbolt wrote:
         | Yeah, I'm quite surprised at this comment. Commercial video
         | games are mass-produced products, and as much as I dislike
         | designers being bogged down in technical minutiae, having a
         | sense of industrial design for the thing you're making is an
         | incredible boon.
         | 
         | Fumito Ueda was notably quite concerned with the
         | technical/production feasibility of his designs for _Shadow of
         | the Colossus_. [1] _Doom_ was an exercise in both creativity
         | and expertise.
         | 
         | [1] https://www.designroom.site/shadow-of-the-colossus-oral-
         | hist...
        
           | astrange wrote:
           | > Fumito Ueda was notably quite concerned with the
           | technical/production feasibility of his designs for Shadow of
           | the Colossus. [1]
           | 
           | And he didn't really achieve it - the game runs very slowly
           | and has a good deal of cut content.
           | 
           | (I once got him in trouble because I found a GPL violation in
           | ICO. I assume the developer didn't pursue it because I don't
           | see the source code up anywhere.)
        
         | eru wrote:
         | Constraints breed creativity.
        
           | 7bit wrote:
           | Today, constraints are simply ignored. (Looking at you, we
           | devs and Microsoft devs).
        
       | 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.
        
           | lefty2 wrote:
           | > -1 still produces -1 instead of 0 That could be a problem
           | depending how you are using it.
        
         | adrian_b wrote:
         | It works fine when the value is negative.
         | 
         | However, there is a quirk of the hardware of most CPUs that has
         | been inherited by the C language and by other languages.
         | 
         | There are multiple ways of defining integer division when the
         | dividend is not a multiple of the divisor, depending on the
         | rounding rule used for the quotient.
         | 
         | The 2 most frequently used definitions is to have a positive
         | remainder, which corresponds to rounding the quotient by using
         | the floor function, and to have a remainder of the same sign
         | with the quotient, which corresponds to rounding the quotient
         | by truncation.
         | 
         | In most CPUs, the hardware is designed such that for signed
         | integers the division instruction uses the second definition,
         | while the right shift uses the first definition.
         | 
         | This means that when the dividend is a multiple of the divisor,
         | division and right shift are the same, but otherwise the
         | quotient may differ by one unit due to different rounding
         | rules.
         | 
         | Because of this, compilers will not replace automatically
         | divisions with right shifts, because there are operands where
         | the result is different.
         | 
         | Nevertheless, the programmer can always replace a division by a
         | power of two with a right shift. In all the programs that I
         | have ever seen, either the rounding rule for the quotient does
         | not matter or the desired definition for the division is the
         | one with positive remainder, i.e. the definition implemented by
         | right shift.
         | 
         | In those cases when the rounding rule matters, the worrisome
         | case is when you must use division not when you can use right
         | shift, so you must correct the result to correspond to rounding
         | by floor, instead of the rounding by truncation provided by the
         | hardware. For this, you must not use the "/" operator of the C
         | language, but one of the "div" functions from "stdlib.h", or
         | you may use "/" but divide the absolute values of the operands,
         | after which you compute the correct signed results.
        
       | 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.
        
         | plopz wrote:
         | I was pretty disappointed with how Factorio reworked how fluids
         | worked in the expansion. The old system had its quirks and the
         | new system is obviously more performant, but it throws realism
         | out the window which is a bummer.
        
           | Starlevel004 wrote:
           | The old system was nonfunctional and any base that used lots
           | of fluids (like modded ones, or new space age ones) were
           | constantly running up against nonsensical mechanics.
        
           | Cpoll wrote:
           | I don't miss it. I also found Satisfactory's old fluid system
           | (with concepts like sloshing) wildly unintuitive. I'll go so
           | far as to say that accurate fluid dynamics is detrimental to
           | any game that's not about beavers and water table management.
        
             | rkagerer wrote:
             | That's the second time I heard the beaver game come up
             | here... Guess I really ought to try it!
        
               | Linosaurus wrote:
               | It's rather neat, and recently hit 1.0.
               | 
               | That game, Timberborn, shares some design elements with
               | roller coaster tycoon.
               | 
               | A block based 3d world they can be modified by the
               | player.
               | 
               | Units walking around on player defined paths, with their
               | mood influenced by pretty bushes.
               | 
               | But there are no obvious performance considerations like
               | in the article.
        
       | 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.
        
         | dmitrygr wrote:
         | they will do it for unsigned. for signed they will do a bit
         | more to do the same rounding as C promises
        
           | grumbelbart2 wrote:
           | Here is how that looks like:
           | 
           | https://godbolt.org/z/rooee4esd
        
           | account42 wrote:
           | It's unfortunate that C tied overflow behavior to the
           | signedness of integer types.
        
         | mid-kid wrote:
         | Yeah. I'm surprised this along with the money thing are listed
         | in the article at all. These are the sort of things you learn
         | within the first month of writing assembly, and were widely
         | used across the industry at the time (and times prior). The bit
         | shifting optimization is performed by GCC even at -O0, and
         | likely already was at the time, as it's one of the simpler
         | optimizations to make. It's like calling "xor eax, eax" a
         | masterful optimization tactic for clearing a register.
         | 
         | Looking at the macro-level optimizations like the rest of the
         | article does is significantly more interesting.
        
           | ConceptJunkie wrote:
           | "XOR AXAX" was my license plate in the 90s.
        
             | dmitrygr wrote:
             | I had "PUSH EAX" and "BX LR" :)
        
         | account42 wrote:
         | Yeah and this can be verified with the minimum level of
         | research you'd expect for this kind of article, e.g. by firing
         | up compiler explorer.
        
         | spiffyk wrote:
         | That whole section is kind of weird. The mention of operator
         | overloading also seems out of place, since the operator is not
         | overloaded here at all.
        
       | sghiassy wrote:
       | Another great optimization is storing the year as two digits,
       | because you only need the back half...
       | 
       | ... oh wait, nvm. Don't preoptimize!
        
         | seba_dos1 wrote:
         | There's a vast space between premature optimization and not
         | caring about optimization until it bites you, and both extremes
         | make you (or someone else) miserable.
        
         | neonstatic wrote:
         | It's a fun optimization to make in the 9th decade of a century
         | :)
        
       | MisterTea wrote:
       | While it has been a while since playing RCT, one thing that was
       | really nice about the game is that it runs flawlessly under Wine.
       | 
       | I really wish I could see the source code.
        
       | troad wrote:
       | > When reading through OpenRCT2's source, there is a common
       | syntax that you rarely see in modern code, lines like this:
       | 
       | > NewValue = OldValue << 2;
       | 
       | I disagree with the framing of this section. Bit shifts are used
       | all the time in low-level code. They're not just some archaic
       | optimisation, they're also a natural way of working with binary
       | data (aka all data on a computer). Modern low-level code
       | continues to use lots of bit shifts, bitwise operators, etc.
       | 
       | Low-level programming is absolutely crucial to performant games.
       | Even if you're not doing low-level programming yourself, you're
       | almost certainly using an engine or library that uses it
       | extensively. I'm surprised an article about optimisation in
       | gaming, of all things, would take the somewhat tired "in ye olde
       | days" angle on low-level code.
        
         | Rendello wrote:
         | I learned these low-level bit tricks by reading TempleOS' HolyC
         | source code. I remember feeling like a genius when I worked out
         | what this line does:
         | 
         | dc->color=c++&15;
         | 
         | Hint: it's from this "Lines" demo program, whose source is
         | here:
         | https://web.archive.org/web/20180906060723/https://templeos....
         | 
         | And this is what it looks like when it runs (ignore the fact
         | it's running in Minecraft): https://youtu.be/pAN_Fza6Vy8?t=38
        
       | netcoyote wrote:
       | Warcraft 1 (1994), Warcraft 2 (1995), and StarCraft (1998) all
       | use power-of-2 aligned map sizes (64 blocks, 128 blocks, and 256
       | blocks) so the shift-factor could be pre-computed to avoid
       | division/multiplication, which was dang slow on those old 386/486
       | computers.
       | 
       | Each map block was 2x2 cells, and each cell, 8x8 pixels. Made
       | rendering background cells and fog-of-war overlays very
       | straightforward assembly language.
       | 
       | All of Warcraft/etc. had only a few thousand lines of assembly
       | language to render maps/sprites/fonts/fog-of-war into the
       | offscreen buffer, and to blit from the offscreen buffer to the
       | screen.
       | 
       | The rest of the code didn't need to be in assembly, which is too
       | time-consuming to write for code where the performance doesn't
       | matter. Everything else was written in portable assembler, by
       | which I mean C.
       | 
       | Edit:
       | 
       | By way of comparison, Blackthorne for Super Nintendo was all
       | 85816 assembly. The Genesis version (Motorola 68000) and DOS
       | version (Intel 80386) were manually transcribed into their
       | respective assembly languages.
       | 
       | The PC version of Blackthorne also had a lot of custom assembler
       | macros to generate 100K of rendering code to do pixel-scrollable
       | chunky-planar VGA mode X (written by Bryan Waters -
       | https://www.mobygames.com/person/5641/bryan-waters/).
       | 
       | At Blizzard we learned from working on those console app ports
       | that writing assembly code takes too much programmer time.
       | 
       | Edit 2:
       | 
       | I recall that Comanche: Maximum Overkill (1992, a voxel-based
       | helicopter simulator) was written in all assembly in DOS real
       | mode. A huge technical feat, but so much work to port to
       | protected mode that I think they switched to polygon-rendering
       | for later versions.
        
         | quietsegfault wrote:
         | Maximum overkill was an amazing game. I probably played
         | hundreds and hundreds of hours.
        
         | CursedSilicon wrote:
         | It's a shame that when a Redditor discovered the source code
         | for the original StarCraft "gold master" on a CD, they sent it
         | back to Blizzard in exchange for some fucking _blizzard merch_
         | [1]
         | 
         | EA a while back released the source code to (most) of the old
         | Command & Conquer games [2] though interestingly left out
         | Tiberian Sun and Red Alert 2, StarCraft's closest competitors
         | at the time.
         | 
         | Would've been nice for historical preservation to be able to
         | peek behind the curtain and see StarCraft's code in a similar
         | fashion
         | 
         | [1]
         | https://old.reddit.com/r/gamecollecting/comments/68xzxt/star...
         | 
         | [2] https://github.com/electronicarts
        
           | imtringued wrote:
           | This entire reddit thread aged really poorly now that
           | Blizzard is a shell of its former self. If anything, the
           | attitude in that thread is what paved Blizzard's decline:
           | complete disrespect for its origin.
           | 
           | The StarCraft source code is something that must be kept
           | behind closed walls, under tight control by Blizzard, even
           | though the original people working on the game at Blizzard
           | have already left and there is nothing to protect here other
           | than eternal shame.
        
           | Blikkentrekker wrote:
           | That person obviously did not want to be at risk for legal
           | issues from Blizzard by publhsing it though. I personally
           | wouldn't take that risk either.
        
         | Lorin wrote:
         | If you worked on Lost Vikings I'd like to thank you for the
         | entertainment during my childhood. Given your background did
         | you ever get involved in the demo scene?
        
         | ListenLinda wrote:
         | Were you at blizzard when they lost their source code server
         | and had no backups? I was there for a short time consulting
         | around the time WC3 was released.
        
         | wjnc wrote:
         | Both Comanche and Settlers 1 were so magic to me as a kid. You
         | learned to work with DOS in text mode. Most shiny on the PC was
         | Wordperfect. And suddely your text computer was capable of
         | displaying graphics and ... games. Hooked me for life.
        
         | Melatonic wrote:
         | Any idea how Total Annihilation did it in comparison?
        
       | evandale wrote:
       | The pathfinding section reminded me that there's a YouTube
       | steamer, Marcel Vos, who goes into a deep dive of how the
       | pathfinding works.
       | 
       | https://youtu.be/twU1SsFP-bE
       | 
       | He has lots of videos that are deep dives into how RCT works and
       | how things are implemented!
        
         | mmcconnell1618 wrote:
         | I've built a few transportation simulations where I started out
         | with pathfinding methods like A* but the compute cost doesn't
         | scale well with 10,000 or 100,000 agents running around. Pre-
         | computing flow fields for common map destinations is one of
         | those areas where you trade off storage for compute. The agents
         | just look for the signpost telling them "this direction to
         | destination x" instead of actually calculating a path.
         | 
         | https://en.wikipedia.org/wiki/A*_search_algorithm#
         | 
         | https://www.youtube.com/watch?v=zr6ObNVgytk
        
       | maxglute wrote:
       | Is there a place to find stories of recent game optimization?
       | What's most ridiculous on like quick inverse square route. As
       | someone who spent way too much time vraying in prior life, I
       | still can't believe we got real time ray tracing.
        
       | egypturnash wrote:
       | I have to wonder how much of the original assembly source looked
       | a _lot_ more succinct than whatever 's in OpenRCT due to the use
       | of macros. Looking up his gameography on Mobygames, Chris had
       | been writing stuff since 1984 when RCT came out in 1999, it's
       | hard to imagine he was still writing every single opcode out by
       | hand given that I had some macros in the assembler I was fooling
       | around with on my c64 back in the eighties.
        
       | Validark wrote:
       | "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"
       | 
       | Do we not know binary in 2026? Why is this a surprise to the
       | intended audience?
        
         | xeonmc wrote:
         | https://xkcd.com/2501/
        
       | raffraffraff wrote:
       | On huge games produced by large game studios, I wonder if the
       | idea of using a real world technical challenge as a "feature"
       | within the game is considered genius? Consider a coder and a game
       | designer who are on different teams and don't attend the same
       | meetings.
       | 
       | But if you look at creative writing, story arcs are all about
       | obstacles. A boring story is made interesting by an obstacle. It
       | is what our protagonist needs to overcome. A one-man-band game
       | dev who simultaneously holds the story and the technical
       | challenge their head, might spot the opportunity to use a glitch
       | or limitation as, I dunno, a mini game that riffs on the glitch.
        
       | rajan2 wrote:
       | This is insane!
        
       | rajan2 wrote:
       | This is Insane
        
       | rajan2 wrote:
       | This is insane
        
       | atrealadam wrote:
       | I'm quite surprised at this comment.
        
       | makapuf wrote:
       | Compilers won't do multiplication by power of two to bit shift
       | for you ? I remember reading in ~2000: the only thing writing
       | a<<2 instead of a/4 will do is make your compiler yawn
        
         | zekica wrote:
         | Even gcc's -O0 will do the bitshift, but even dividing with 5
         | on x86_64 will not do idiv:                       imul    rdx,
         | rdx, 1717986919             shr     rdx, 32             sar
         | edx             sar     eax, 31             sub     edx, eax
         | mov     eax, edx
        
       | bze12 wrote:
       | > This part is especially fascinating to me, since it turns an
       | optimization done out of technical necessity into a gameplay
       | feature.
       | 
       | Reminds me of blood moons in Zelda
       | https://www.polygon.com/legend-zelda-tears-kingdom/23834440/...
        
       | pmg102 wrote:
       | The article refers several times to the benefits of the game
       | designer and the coder being the same person. I've often felt
       | that this is the only way to build anything impressive, and in
       | fact I'm amazed that corporations with their hierarchical
       | organisation model ever get anything built at all but I suppose
       | you can brute force anything with enough employees.
       | 
       | It does make you wonder if the future of AI-assisted development
       | will look more like the early days of coding, where one single
       | mind can build and deliver a whole piece of software from
       | beginning to end.
        
       | ionwake wrote:
       | also I remember the excitement of a new game that looked
       | different to others.
       | 
       | Somehow even as a child I just knew that it would be a whole new
       | emergent game play experience.
       | 
       | Ofcourse I didnt know waht went into making Rolelrcoaster Tycoon
       | but I could just by a couple of screenshots how this was clearly
       | a ground up new game with new mechanics that would be extremely
       | fun to play.
       | 
       | I dont get this feeling anymore, as I just assyne everything is
       | just a clone of another game in the same engine generally.
       | 
       | Unless its been a decade in production like Breath of the Wild of
       | GTA 5 i just dont expect much.
        
       | almostdeadguy wrote:
       | The pathfinder algorithm is a great example of why constraints
       | are so important for creativity and creative development.
       | 
       | If AI has any benefit to creative endeavors at all it will be
       | because of the challenges of coaxing a machine defined to produce
       | an averaging of a large corpus of work (producing inherently
       | mediocre slop) provides novel limitations, not because it makes
       | art any more "accessible".
        
       | derodero24 wrote:
       | Biggest lesson from string matching work: layout beats
       | instructions every time. Batch your comparisons into a contiguous
       | buffer so the prefetcher can actually help, and you'll outperform
       | hand-rolled SIMD on random-access data. Compilers handle the
       | arithmetic tricks fine now -- they can't fix your cache misses
       | though.
        
       | throw_m239339 wrote:
       | Fantastic write-up, that's exactly why I came to HN many years
       | ago, to find such articles about mundane things or products, but
       | the technical aspect is just fascinating.
        
       | jwilliams wrote:
       | Definitely made me feel old to see bit-shifting needed an
       | explainer! I must admit as I was reading I was "why is he
       | explaining this? it's obvious!".
        
       | random__duck wrote:
       | So this is what programing on hard mode looks like ?
        
       | ConceptJunkie 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.
       | 
       | The author loses a lot of credibility by suggesting the compiler
       | won't replace multiplying or dividing by a factor of 2 with the
       | equivalent bit shift. That's a trivial optimization that's always
       | been done. I'm sure compilers were doing that in the 70s.
        
         | msk-lywenn wrote:
         | I wouldn't go that far, but yes, no compiler will leave that on
         | the table today or even twenty years ago. They do even more
         | impressive transformations than that on basic math.
        
         | nearbuy wrote:
         | The author is partly right here. If those values are ints,
         | you'll get something like this:                 sar eax, 0x1f
         | and eax, 7       add eax, edx       sar eax, 3
         | 
         | You get 4 instructions instead of one because value >> 3 rounds
         | towards negative infinity and value / 8 rounds towards zero.
         | 
         | And while this wouldn't apply to C++, in languages with checked
         | arithmetic, the left shift won't necessarily set the overflow
         | flag, so the compiler often can't use it.
        
       | TheGRS wrote:
       | This is a fun read, its one of my favorite games growing up by
       | far, countless hours sunk into it. I didn't need this write-up to
       | know that Chris Sawyer was god among men and that the open source
       | version is a huge labor of love, but its a good reminder :) I
       | will need to give OpenRCT a try some time, I've tried a little
       | OpenTTD and really enjoy it, but RCT was always my jam.
       | 
       | For the lesson here, I think re-contextualizing the product
       | design in order to ease development should be a core tenant of
       | modern software engineering (or really any form of engineering).
       | This is why we are usually saying that we need to shift left on
       | problems, discussing the constraints up-front lets us inform
       | designers how we might be able to tweak a few designs early in
       | order to save big time on the calendar. All of the projects that
       | I loved being a part of in my career did this well, all of the
       | slogs were ones that employed a leadership-driven approach that
       | amounted to waterfall.
        
       | hermitcrab wrote:
       | >the game was written in the low-level language Assembly
       | 
       | Surely it wasn't _all_ assembly. There is little to be gained in
       | performance from writing non-bottleneck parts of the code in
       | assembly.
        
         | its-summertime wrote:
         | > > What language was RollerCoaster Tycoon programmed in?
         | 
         | > It's 99% written in x86 assembler/machine code (yes,
         | really!), with a small amount of C code used to interface to MS
         | Windows and DirectX.
         | 
         | https://www.chrissawyergames.com/faq3.htm
        
           | hermitcrab wrote:
           | Wow. It reminds me of those guys who run a marathon carrying
           | a fridge. Impressive, but ...
        
       | ben7799 wrote:
       | I guess I'm showing my age but having read stuff like Michel
       | Abrash's books years ago this article was a bit underwhelming.
       | 
       | Like no one who wrote any code back in the 80s or 90s even for a
       | homebrew game was skipping this stuff, it was in almost every
       | book and tutorial. Stuff like bit shifting was extremely common
       | and a lot of games would have had design choices that were
       | informed by coding challenges. Lots and lots of code had data
       | structures aligned on byte/word boundaries or had data massaged
       | to fit into the limits of hardware in order to make reads happen
       | in a certain # of cycles, etc.. certainly almost all console
       | games had lots and lots of fascinating design choices like this.
       | 
       | This game may have been exceptionally well optimized but it feels
       | like if the original code is not in the public domain these
       | weren't the best examples.
       | 
       | When he started writing about their being a clean sheet re-
       | implementation I thought there was going to be a benchmark
       | comparison of the modern rewrite vs the original on old hardware
       | or something, that would have been interesting.
       | 
       | Thankfully or not I'm just barely young enough that I never had
       | to write anything professional in assembly, though if I had gone
       | into games maybe I would have.
        
       ___________________________________________________________________
       (page generated 2026-03-23 23:01 UTC)