I’ve been working on a tool-assisted speedrun (TAS) of Zork I (revision 88) for the Apple II. What makes a speedrun tool-assisted is that, rather than being played live with a controller or keyboard, it is played in an emulator, taking advantage of the ability to pause, rewind, make savestates, and inspect the contents of memory. A TAS is developed in a process of incremental optimization, with the goal of producing a recorded sequence of timed button presses that, when played back in the emulator, finish the game quickly. A TAS is kind of the limit of “what if you had perfect reflexes and foresight and you had a run where everything went right?”
You can see the development history of the Zork I TAS, so far, starting with this post at the TASVideos.org forum. I’ve just finished a draft of a new, faster route through the game. The main feature of the route is manipulating the thief into carrying treasure. I’m posting the route here to see if anyone has ideas for improvement as I go about the process of optimization. Here’s a video of the TAS being played back on an emulator, alongside a monitor of the player’s and the thief’s inventories:
- Text transcript
- Emulator input file, source code, and alternative versions (40-column mode, SUPERBRIEF mode)
To run the TAS yourself, you will need to set up the BizHawk emulator for Apple II, load the Zork I disk image, and play the input file (.bk2 format).
A note on timing: TASes are usually measured according to real time elapsed (or, equivalently, by frame count), starting when the emulator powers on and ending at the final input. That’s the standard this TAS uses; in particular, it’s not necessarily trying to minimize the move count. The distinction matters on a slow computer like the Apple II, because not all moves take an equal amount of time. This run makes 236 moves in 20685 frames (5:44.75 at 60 FPS). The category is any%, which in the case of Zork I is the same as 100%, because you can’t finish the game with less than the maximum number of points.
This TAS is a bit special in that the whole thing is coordinated by a program—necessary to cope with the game’s randomness. The program is here; go to line 494 for the commented sequence of commands. The program is written in Fennel, a Lisp that compiles to Lua, and controls the emulator via BizHawk’s Lua integration. The biggest advantage of a tool-assisted speedrun, in the case of Zork I, is the ability to control the outcome of random events. When an undesirable random event happens (such as getting hit in combat), the Fennel program backtracks and tries again, altering the letter case of commands to tweak the state of the game’s random number generator (RNG).
A primary constraint on a Zork I speedrun is the limited carrying capacity of the player character. The player can carry at most 95 units of weight. (You would think it would be 100 units, but the WEIGHT routine includes the size of the WINNER object itself, which is 5 units!) That leads to the main idea of this route: getting the thief to carry treasure for us.
Unlike the player, the thief has unlimited carrying capacity. He wanders the dungeon along a set route and has a high chance of picking up any treasure he finds. With RNG manipulation we can make the chance 100%. The thief drops treasure periodically at his lair, the TREASURE-ROOM, which is conveniently located just 3 moves away from the LIVING-ROOM and the trophy case where you need to eventually deposit the treasures. We can instantly summon him to the TREASURE-ROOM by entering the room. The main constraint on the thief’s ability to help with treasure collection is that he does not steal from rooms the player has not yet been to. The major activity of this route is efficiently “visiting” rooms that contain treasure, in order to make the treasure eligible for taking by the thief. (There are, however, several special cases that require special handling, such as a few rooms and items that are marked “sacred” and are off-limits to the thief.) The only treasure that must be held by the thief at some point is the EGG, because only the thief can open it to expose another treasure (the CANARY) that is inside.
This is a schematic summary of the route. Here’s a map to follow along.
- Get the
EGG,SWORD, andLAMP, defeat the troll, then go through the maze, getting theBAG-OF-COINSon the way. The cyclops is located at the entrance to the thief’sTREASURE-ROOM. Scare away the cyclops, which opens a quick path back to theLIVING-ROOM. Drop theEGGandBAG-OF-COINSfor the thief to pick up later; also drop theSWORDto free up 30 units of inventory space. (We won’t need the sword again until the final battle with the thief.) Enter theTREASURE-ROOMand use a magic word to warp toNORTH-TEMPLE. - Get the
COFFIN(which is a “sacred” item the thief cannot move for us), use a magic word to drop it in theTREASURE-ROOM, then warp right back. Get theTORCHandSCEPTRE, which are not only treasures but also tools we will need later. Take theBELL,BOOK, andCANDLESto theENTRANCE-TO-HADES(manipulating RNG to keep theCANDLESfrom blowing out) and do a ritual to open the gates. Visit the crystalSKULLpast the gates, leaving it for the thief to get. Then go to the dam area, passing through theEW-PASSAGEon the way to earn 5 points. - In the dam
MAINTENANCE-ROOM, get theSCREWDRIVERand theWRENCHand use theWRENCHto drain theRESERVOIRand make it passable. Visit theTRUNKandTRIDENTpast the reservoir, solve theLOUD-ROOMpuzzle to make theBAReligible for taking by the thief, then take theINFLATABLE-BOATandPUMPtoWHITE-CLIFFS-SOUTH, adjacent to the river. - Inflate the boat and use it to cross the river. On the river there is a
BUOYthat contains anEMERALD. The thief cannot enter water rooms, so we must collect theEMERALD, not just visit it. Take theSHOVELand dig up theSCARAB, leaving it for the thief. Go toARAGAIN-FALLSand wave theSCEPTREto make a rainbow bridge. Take the bridge down to the base of the waterfall, visiting thePOT-OF-GOLD, and return to the house through the overworld. Drop off the junk and treasure we’re carrying, except for theLAMP,SCREWDRIVER,TORCH, andGARLIC, which we’ll need in the coal mine. - Take the
TREASURE-ROOMwarp toNORTH-TEMPLEagain, then warp fromMIRROR-ROOM-2toMIRROR-ROOM-1. Holding theGARLICincapacitates the bat, which lets us get theJADE. (TheBAT-ROOMis a sacred room, so we have to pick this one up.) Put theSCREWDRIVERandTORCHin the dumbwaiter. Dispose of theGARLICin order to take advantage of the bat’s attack. When theGARLICis not present, the bat seizes you and takes you to a random room in the vicinity of the coal mine. By manipulating RNG, we force the bat to drop us at the far end of the mine, saving travel time. Take theCOAL, walk out through the mine, place theCOALin the dumbwaiter, and lower the dumbwaiter. Use the bat to warp over the mine again, drop everything to get through the narrow passage, collect the items from the dumbwaiter, and convert theCOALinto theDIAMONDusing the machine. Raise the dumbwaiter, walk back out through the mine, recollect theTORCHandDIAMONDfrom the dumbwaiter, and take the slide back to theCELLARbelow theLIVING-ROOM. - Take a short detour to the
GALLERYto get thePAINTING. The thief could get this one, but we’re now too close to the end of the run for him to have time to get there. Go to theLIVING-ROOMto drop off the treasure we’re carrying. Go through the cyclops hole, pick up theSWORD, and enter the thief’s lair. - Entering the
TREASURE-ROOMcauses the thief to warp there, along with the treasure he’s carrying. We manipulate an ideal sequence of random combat outcomes: 3MISSEDs from the thief, and 2SERIOUS-WOUNDs and aKILLEDfrom the player. There is now a pile of treasure here we have to carry to the nearbyLIVING-ROOM. We start with theCANARY(plus as much else as we can carry): we must take it outside to the forest for one final piece of treasure. Use a magic word to warp toNORTH-TEMPLE, then another to warp fromSOUTH-TEMPLEtoFOREST-1. Wind theCANARYto make the songbird drop theBAUBLE. Re-enter the house and drop off the treasure. Make two round trips through the cyclops hole to fetch the rest of the treasure.
I think there’s potential to make the route still faster. I’ve marked some potential changes with TODO comments in the source code. These are some ideas I have:
- Save the
COFFINfor last, rather than doing the awkward back-and-forth warp as soon as we get it. Collect theCOFFINon the final go-round with theCANARY, because we have to pass through that area anyway. - Maybe also leave the
TORCHfor last, or just visit it for the thief to pick up. TheCANDLESwork equally well as a light source in the dumbwaiter puzzle in the coal mine. - Consider collecting treasure as long as there’s inventory space. I haven’t audited which segments of the route have excess inventory capacity that might be used for carrying treasure. It’ll be worth it if we can save one round trip during the final treasure haul.
- Dropping the
LAMPfor the final treasure haul will leave room for slightly more treasure per trip. There are 2 dark rooms between theTREASURE-ROOMand theLIVING-ROOM, but entering the first dark room is safe, and the second one can be RNG-manipulated to prevent a grue attack. - I want to try a reordering where you do the coal mine before the river area. The slide after the coal mine returns you basically to the house, which is relatively close to the bottom of the waterfall. Waving the
SCEPTREfrom the bottom works to get up to theSCARABandEMERALD, then return to the house through the overworld.
That’s in addition to some micro-optimizations having to do with the syntax of commands, which I’ll make a separate post about.