Hi! I'm Luca Scalzotto
I am a passionate programmer, fanatic gamer and creative hobbyist.
Back to posts

Scripting a Spelunky 2 Speedrun | Part 1: This Doesn't Exist?

When you look around on the internet, it looks like there's nothing that hasn't been done before. However, I managed to find one of those things, and it turned into quite the project.

This post was originally published on techtroupe.io.

I’m a big fan of all kinds of speedruns. It’s not that I’m really any good at them myself - although I did make an attempt once or twice - but looking at videos of other people speedrunning games is always baffling to me. The amount of skill and time that goes into being able to play a game that quickly, and often near-flawlessly is extremely impressive. It’s a level of dedication for games I’ve never really had myself, where you have to know the game inside out to find all the micro-optimizations, and then spend enough time to be able to actually pull off a run where you manage to cut every possible corner.

Within speedrunning there are various different categories, like for just completing a game, getting every single collectible, etc. However, a category that’s a bit less official (yes, speedruns have official rankings and everything, check speedrun.com if you’re in for a rabbit hole), but equally (if not more) technically impressive to watch are tool-assisted speedruns (or TAS, for short). A tool-assisted speedrun generally is a speedrun in an emulator, performed by a script. The goal of these runs is to implement the theoretical perfect run, which is generally near-impossible (or at the very least impractical) for humans to perform. Nowadays lots of emulators have support for some sort of scripting, so you could have a massive file containing all the specific button presses for each frame.

Input for a TAS of a Game Boy Advance game.

Let’s make a small sidestep to a game I’ve been playing a lot lately: Spelunky 2. Of course, this is a game I also like watching speedruns of. Then, not too long ago, I suddenly realised I had never seen a TAS of Spelunky 2 (or the original Spelunky for that matter). I looked around for a bit, and I did manage to find one sort-of TAS for Spelunky, but it’s been modded quite heavily to allow this run to even exist. So it definitely gets an A for effort (and being the only one out there that I could find), but I wasn’t entirely satisfied. And that is when I realised what I had to do…

Wanting to fill this void in the internet that is the absence of a Spelunky 2 TAS, I started doing some research. How do people even make a TAS? Is it something I could do? Can I just script input and send it to my OS? After a bit of searching the internet, the answers to those questions turned out to be: with great effort, yes, and - quite surprisingly - yes. I found out that TAS runs are generally made for older games that run in an emulator, which makes sending input on just the right frame and reading the game state that much easier. This is something I never realised, but actually makes a lot of sense.

The BizHawk emulator playing Super Mario Bros., with a hex editor viewing the memory next to it.

Since Spelunky 2 just runs on Windows, I wouldn’t have any of the luxuries you get when scripting for an emulator. That wasn’t going to stop me though. I quickly hacked together some code in Go to see how hard it would be to emulate keyboard input and send it to Windows, and this turned out to be way easier than I expected it to be. So with that knowledge I was sure: I would attempt to make a tool-assisted speedrun for Spelunky 2.

As I’m writing this it’s been a couple of weeks since I first started researching all of this, and without spoiling too much I can safely tell you: boy did I ever underestimate every single aspect of this idea. But over the next couple of posts I’ll take you through the process of my attempt at scripting a Spelunky 2 speedrun. In the next part we’ll dive into maybe the most important bit of the foundation: how to send input to the OS - preferably without having to write hundreds of lines of code (hint: it involves a custom scripting language). Stay tuned!