Post subject: Zork (no, really!)
Joined: 7/25/2007
Posts: 109
You might think that the only reason to do a TAS of this text adventure would be for the whole grue thing. And yeah, that is half of the reason why I suggest it. >_> But besides that, the game also has some interesting bugs that might make a playaround viable. A long while back, there used to be this site that listed bugs in old Infocom games. I managed to save that site before the host went down, though.
1. Many commands which produce strange output in later versions, give no response at all in Version 2, and the game simply goes on to the next turn. For example, the commands READ MAILBOX, and EAT ME give no response at all in Version 2, but in the next version (Version 5), you get "How can I read a mailbox?" and "I don't think that the you would agree with you." -- Alan Franzman 2. THE SLIDE BUG: In Version 2, when you drop items down the slide, the game first tells you that it's done, then tells you it can't be done, and then removes the item from the game entirely. For example: EXAMINE "" >DROP TRIDENT IN SLIDE "The crystal trident falls through the slide and is gone." "I can't do that." The trident is now out of the game. -- Alan Franzman 3. The earliest releases of Zork I (Versions 2 and 5) can become very confused if you nest objects too deeply in your inventory, such as putting the lunch in the sack, then the sack in the coffin. You may get very spurious output from the INVENTORY command like "Such language in a high-class establishment like this!" messages, with other random junk interspersed in it. This can also lead to the object hierarchy getting screwed up, in such a way that an INVENTORY might claim that you are carrying a bunch of rooms around! ADDITIONAL: Technically this bug is present in all versions of the game. In Versions 2 and 5 it occurs when three objects are nested (i.e. canary in egg in sack). In later versions it only occurs when 7 objects are nested, which is difficult but not impossible to create. Take the canary, egg, sack, buoy, coffin, and (inflated) boat to the Shaft Room, and issue the following commands: PUT EGG IN SACK. PUT CANARY IN EGG. PUT SACK IN BUOY. PUT BUOY IN COFFIN. PUT BOAT IN BASKET. PUT COFFIN IN BOAT. LOOK. (As everything can't be carried simultaneously, some TAKE and DROP commands may need to be interspersed here.) When you LOOK, a random phrase from somewhere else in the game may be mixed with the output. In the Solid Gold version, the phrase "up to your ankles" (from when the water is rising in the Maintenance Room) will be before the description of the canary. In other versions there are different effects, some random phrases, and some gibberish. -- Michael the Great 4. In Versions 2 and 5, commands such as GO DOOR or GO TREE can send players into random locations. -- New Zork Times 5. In Version 5, the command HIT MIRROR WITH SWORD will generate combat responses, such as "The mirror parries", or "The mirror dies in a cloud of sinister black fog." In Version 2, you miss the mirror and the game crashes with a Stack Overflow error. -- Graeme Cree 6. In Versions 2 and 5, the command GIVE AXE TO TROLL will generate a response like: "The troll accepts your gift, and not having the most discriminating taste, eats it. The troll, dismarmed, is cowering and begging for forgiveness in the gutteral language of the trolls." (In Version 2, he "gleefully" eats it. Picky, picky.) Though the troll can no longer attack you, he still blocks the exits and parries, while continuing to beg for forgiveness. -- Graeme Cree 7. In Versions 2 and 5, if you give the troll to himself, he eats himself and disappears. He still bars you from leaving the room though, only now he can't be attacked. -- Graeme Cree NOTE: In fact, as Ethan Dicks has pointed out, virtually anything can be given to the troll (or thief) in this version: your hands, your eyes, the eastern wall, a compass direction, even "ME". The troll will then eat them and you will be unable to interact with them for the rest of the game (although you can still go "east" if you give "east" to the troll). The thief of course will leave them lying around instead of eating them. 8. In Versions 2 and 5, if you give the troll to the thief, the thief puts the troll in his bag, but the troll still blocks the exits. If the thief is killed, the troll reappears. -- Graeme Cree 9. In all versions after Version 5, if you try to use a compass direction while in the boat, the game will (reasonably) tell you that you can't go that way in a magic boat. In Versions 2 and 5, it will tell you that you can't go that way in a tan label. -- Graeme Cree 10. In all versions after Version 5, when you reach the portion of the river where the buoy is, the description will tell you that the buoy is "(outside the magic boat)". Versions 2 and 5, tells you that the buoy is "(in the room)"! -- Graeme Cree 11. If you try to GET OUT OF THE BOAT while floating down the river, all versions of the game tell you that you realize "just in time" that such a course would be suicidal. In all versions before and after Version 5, the game prevents you from leaving the boat. In Version 5, it lets you leave and kills you, despite having told you that you had realized not to do it. -- Graeme Cree 12. THE FLOODGATES BUG: In version 5, if the nest is not taken at location Up a Tree, the room description eventually changes to a torrential flood of output, several screens long, containing a mishmash of fragments of room, noun, and action descriptions that you see whenever you enter the room (except in superbrief mode), or do a LOOK. It isn't clear precisely what causes it, but it happens every time if you go down into the GUE, through the maze, grab the bag, come out through the Cyclops Room into the Living Room, and outside to the tree. It might be triggered by a certain point level, or something else. If you try to TAKE the nest after the bug has triggered, the game will lock up. Taking the egg before or after has no effect. -- Graeme Cree 13. In Versions 2, 5 and 15, shaking an open container that isn't empty may crash the game. This works with the sack, but not the bottle. -- New Zork Times 14. In Versions 2, 5, 15, 23, 25, 26, and 28, you can bump your head on things such as the river by trying to ENTER them. -- New Zork Times 15. THE WIMPY PHANTOM THIEF BUG: You know, that thief is really tough in combat. Why not fight his phantom instead? He doesn't fight back. In Versions 2, 5, 15, 23, 25, 26, 38, and 30, engage the thief in combat (outside of his lair). Wait until he robs you and leaves; you will see this message: The other occupant just left, still carrying his large bag. You may not have noticed that he robbed you blind first. At this point, continue to attack the thief, using the AGAIN command. You will continue to attack the no-longer-present thief, but he will not fight back, and eventually you can do enough damage to kill him. A couple of points; this won't work if you attack the thief with the sword, since when he "robs you blind", he takes the sword as well as your treasures. You must use the axe or nasty-looking knife. Also this only works with the AGAIN command, (with AGAIN representing ATTACK THIEF WITH AXE, or something like that) not by attacking him directly. Thirdly, it may take several tries to get this to work, since the thief does not always rob you in the middle of a fight. Sometimes he stays and fights (and kills you). Killing the phantom thief kills the real one as well, and you can now explore the game without interference from him, even when you find his lair. Sound great? Well, there is one slight hitch. Remember the things the thief took when he robbed you blind? He never had a chance to stow them in his lair, and they are nowhere to be found when you kill the phantom (his stiletto is left behind, but not his bag). This means any treasures that he took are out of the game, and the game may be unwinnable. Therefore, the only way to use this bug to your profit is to engage the thief in combat when you are carrying no treasures (except the sword, which he will take). -- Kevin (Piouhgd@aol.com) 16. In Versions 2, 5, 15, 23, 25, 26, 28, and 30, you can take the torch into the Gas Room without setting off an explosion, provided that it is inside the Raft or the Coffin (open or closed). -- Allen Garvin 17. In versions 5, 15, 23, 25, 26, 28, and 30, the single-line description of the torch and candles stays unchanged after they have been removed from their initial locations. For example, put the torch into the gold coffin, close it, then open it again. You will see: >OPEN COFFIN The gold coffin opens. Sitting on the pedestal is a flaming torch, made of ivory. OR: >OPEN COFFIN The gold coffin opens. On the two ends of the altar are burning candles. In versions 75, 76, 88, and 52, this is fixed, and you get these responses: >OPEN COFFIN Opening the gold coffin reveals a torch. >OPEN COFFIN Opening the gold coffin reveals a pair of candles. This doesn't seem to work in Version 2, if only because in that one, the coffin is so much smaller, and can't contain either the torch or candles. -- Graeme Cree 18. In Versions 2, 5, 15, 20, 23, 25, 26, 28, and 30 you can take the boat while already in it. DROP the (inflated) boat in the living room. Then GET IN BOAT, PUT BOAT IN CASE, and then GET ALL FROM CASE. The game will respond: magic boat: Taken. nail: It's not in that! At this point you will be both occupying and carrying the boat. A LOOK command (and attempting to move) will tell you that you are in the boat, but it will not show up in an INVENTORY command. Once you get out of the boat, it will be placed in your inventory. This assumes that you are playing the game from the original Infocom interpreter. Frotz 2.32 will put up with none of this nonsense, and will crash the game when you try to GET ALL FROM CASE. In versions 75, 76, 88, and 52 this does not work, because you are prohibited from PUTting things unless you TAKE them first. But the lack of such a prohibition in earlier versions may be responsible for numerous bugs. For example in versions 30 and earlier, you can take the raft to the Treasure Room, and PUT CHALICE IN RAFT without first defeating the Thief in combat. -- Allen Garvin 19. THE PICKUP BUG: In games like Planetfall and Enchanter, people have reported situations where one could exceed their normal weight limits, and carry something that they could not pick up themself, simply by having someone hand it to them. I have never regarded these as bugs, reasoning that if you're holding an armload of equipment it's easier to take something that someone gives you than bending over and picking it up. I can't defend this bug, though. In all versions up to Version 30 (except perhaps Version 2 again, where the containers are so much smaller), you can pick up items that you normally couldn't, simply by putting them into something else that you're already carrying. For example, try this: 1) Put the bell and axe (nothing else) into the gold coffin, and leave it open. 2) Have only the lunch, garlic and coffin (loaded with bell and axe) in your inventory, with the empty sack at your feet. If you then try to PICK UP SACK you will be told "Your load is too heavy.", but if you PUT SACK IN COFFIN it works, and you're told "Done." You can signifigantly exceed your weight limits this way if you pick up items by putting them in the coffin and then take them out of the coffin to make room for new items, but there are limits. Eventually things will start slipping from your fingers and falling to the ground when you try to take them out of the coffin. In versions 75 and later, you cannot put something into something else unless you already have it in your inventory when you issue the command. --Fredrik Ekman 20. THE PHANTOM BOAT: In Versions 2, 5, 15, 20, 23, 25, 26, 28, 30, 75 and 76, you may continue to occupy the boat after you've destroyed it. Inflate the boat, and drop it. GET IN BOAT, then CUT BOAT WITH SWORD. The game will reply: "Your skillful swordsmanship slices the magic boat into innumerable slivers which evaporate instantaneously." However, you will still be considered to be in the boat for all locational purposes, save that you will not be able to refer to it. If you were in the water when you destroyed the boat, you will still be afloat. Unfortunately, in most versions you will not be able to get out of the boat, (such as by saying DISEMBARK BOAT) not being able to refer to it, nor will you be able to give it directional commands (if you are in the river), which means that you are trapped, and the game is unwinnable. In versions 75 and 76, you will at least be able to escape the phantom boat by typing STAND, a command that exits the boat without directly referring to it. Earlier versions do not know this word. The same thing will happen if, instead of destroying the boat, you give it to the thief while inside it, and he leaves with it. -- Allen Garvin 21. If you knock the thief unconscious and try to take his stiletto, the game says that it is "white hot," and that you drop it -- obviously some sort of protective magic. However, in Versions 2, 5, 15, 23, 25, 26, 28, 30, and 75, if you try to PUT STILETTO IN SACK (for example) it will let you, but when the thief wakes up, he continues to attack you with the stiletto, even if you have moved it to a different room! In later versions, it refuses to let you put it in the sack at all, saying that you don't have the stiletto. -- B.J. Parker 22. BIRTHDAY CANDLES BUG: In all versions except the Solid Gold (and Mini-Zork), Zork 1 apparently uses trick relighting candles. The first time you go to the Temple, type BLOW OUT CANDLES, *without* taking them, then type LOOK. The description will say "On the two ends of the altar are burning candles.", but if you examine them, it will say that they are out. If you try to light them, you will be told "The candles are lit," instead of "already lit" which is what it says when they are burning. If you do an INVENTORY command, the candles will not say "(providing light)". However, the room description is correct, and all of the other indicators are wrong. The candles *are* still lit and will quickly become too small to use unless you put them out for real, by taking them and either dropping them, or lighting and blowing them out again. The only reliable way to blow out the candles is to hold them while you do it. This bug drove me, well, buggy, until I tracked it down, trying to figure out why my candles were being destroyed even though I knew I had blown them out. -- Graeme Cree 23. Here's a variation on the Starcross disk bug. If the sack and the bottle are both empty, it's possible to put the sack in the bottle, then put the bottle in the sack, causing both to vanish. This exists in all versions except the Solid Gold, Mini-Zork, and Version 2 (because the bottle is too small to hold the sack in that one). -- New Zork Times 24. BOTTLE BUG: In all versions except the Solid Gold and MiniZork, you can POUR WATER regardless of whether the bottle is open or closed. -- Graeme Cree 25. TRAPDOOR BUG: In versions 75, 76 and 88, there is a way to open the trapdoor from the inside. At the beginning of the game, enter the following commands: N. E. OPEN WINDOW. W. W. TAKE LAMP. MOVE RUG. LIGHT. OPEN TRAPDOOR. D. N. G. When you enter the G (or AGAIN) command, you will be told "The trap door opens." You can then return to the Living Room, and the trapdoor will not be barred behind you when you descend to the Cellar later. The order of the commands matters. If you reverse the order of LIGHT, and OPEN TRAPDOOR, then you will be told "It is already on" when you type AGAIN. For some reason the AGAIN command thinks you are referring to the lamp. It isn't clear if this is a bug or a playtesting device that Infocom forgot to remove. -- Allen Garvin NOTE: Matthew Russotto suggests that this bug is a combination of another bug wherein AGAIN did not check to see if the object referred to was still present, and a deliberate change that made AGAIN ignore directional commands. The Phantom Thief Bug was another manifestation of this, but that bug was patched separately, rather than fixing the underlying problem. Therefore, AGAIN in this case refers to your opening of the trapdoor three moves ago, because all commands in between have been directional. He points out that similar tricks can be used to disarm the troll, and to attack him when he is not present (a la, the Phantom Thief Bug). From the Troll Room, the commands TAKE AXE, S, AGAIN, will let you take the axe while you are in the cellar, even though it will be invisible to LOOK and INVENTORY commands. Also from the Troll Room, the commands, ATTACK TROLL WITH SWORD, S, AGAIN, gives you the Phantom Troll Effect (similar to the Phantom Thief), allowing you to attack the Troll while he isn't present, and can't fight back. 26. Another container bug, but this one is in all versions, even the Solid Gold edition (Except for Version 2 with its notoriously small containers again). If you put the (inflated) raft in the coffin, then the coffin in the raft, both will disappear. This does not work the other way around, however. If you put the coffin in the raft first, then the raft in the coffin, you get a message saying that there is no room. In Mini-Zork, this bug not only appears, but the game locks up to boot (no pun intended). -- Graeme Cree 27. THE RAFT OF HOLDING: When you put objects in the raft and then deflate it, they effectively cease to exist, allowing you to carry as much weight and as many objects as the raft will hold (it won't hold the gold coffin, unfortunately, and of course sharp objects are out). This is because the game handles the pile of plastic and the raft as two separate objects, and there's no provision for handling any objects inside the raft when the switch is made. -- Stu Galley in The New Zork Times 28. TEETH OVERBOARD: All versions of the game (except Mini-Zork) have an odd problem synonyming the word overboard (which really shouldn't be a noun at all, should it?). In versions 2-28, overboard is a synonym for object 212, in version 30 object189, and in versions 75 onward as teeth (!), as can be seen by typing EXAMINE OVERBOARD on the first turn of the game. -- Gunther Schmidl
There could be more to find on other sites or by experimentation. If we're speaking strictly speed, then bug 4 may be able to get us to the end without doing much, but of course, that wouldn't be interesting. It'd be cool if a glitchy playthrough could be made using all the bugs on the list. The only problem will come in the form of actually finding an old enough version to exploit most of them.
Joined: 1/11/2009
Posts: 44
I think this could be viable. It might be tricky finding the best speed to go so that people can follow the action while still getting a good sense of WTF. This site has a long list of bugs that weren't on Graeme Cree's site; using as many glitches as possible off of both lists would be quite a task. Finding an old version isn't a problem; the Interactive Fiction Archive has patches to convert Infocom games from one version to pretty much any other here. This has got my interest. It'd be cool to see a playaround, even if it didn't get published.
Active player (447)
Location: Houston
Joined: 12/16/2008
Posts: 458
Location: Houston
was waiting from someone to do an old infocom game, I can't wait
Post subject: Rise from the grave, Zork thread.
Active player (381)
Joined: 9/25/2011
Posts: 652
I'm doing a Zork I run (yes, really!), specifically a 100% run aiming for vault. One big question I have, though is: What's the end screen for this game? I could pull up the score screen after hitting a score of 350 to show that I've achieved 100%, but that still leaves a few screens untraversed in the game. Since they don't count for score, one could argue that they aren't really needed to "beat" the game. Alternatively, I could end after it dumps you to the command prompt, but it would make for a very confusing and even less entertaining run, and there'd be no easy way for viewers to verify that 100% was actually reached. On a separate topic, I don't see any specific glitches that will help me out in this run, though feel free to suggest.
DrD2k9
He/Him
Editor, Judge, Expert player (2329)
Location: US
Joined: 8/21/2016
Posts: 1147
Location: US
Isn't there an exit to go through to complete the game? I think the pathway doesn't open up until all the treasures are in the trophy case thing inside the house. If I remember correctly, once the path opens, taking it leads to an endgame which is essentially the lead-in to Zork 2. EDIT: With being able to buffer key-presses, this could be a really quick run. Also I think you can stack directions and commands. Typing 'NW' will go Northwest Typing 'N W' will go north then west. (it may require a comma between commands 'N, W' ) I believe you can even do room actions within a stream of direction commands. 'N W Pickup X E E S' EDIT 2: I'd be willing to help if you want to collaborate.
Player (27)
Location: Amsterdam
Joined: 8/29/2011
Posts: 1207
Location: Amsterdam
Great idea, and a classic worthy of a run.
c-square wrote:
One big question I have, though is: What's the end screen for this game? I could pull up the score screen after hitting a score of 350 to show that I've achieved 100%, but that still leaves a few screens untraversed in the game.
In a game that keeps its own score-out-of-maximum or completion percentage, a 100% run means using that value from the game, and not something arbitrary like "enter all rooms".
DrD2k9 wrote:
this could be a really quick run.
Yep. Well we also have CD-Man in one nanosecond :) Frankly I think the best way of showing this run is via a text transcript (with all inputs and outputs, so the player can read at his own speed). I suppose site policy requires a movie (e.g. on Youtube) but you could easily have a text file in addition to that.
Active player (381)
Joined: 9/25/2011
Posts: 652
Happy to have the help! It looks like I might have bitten off more than I can chew. There’s a bunch of things that have to go just right on the run (killing the Troll in one hit, getting the egg stolen early, not getting hurt by the thief x2, having candles blown out on one particular move) that it’s going to take a lot of work finding the right starting clock time to set the RNG properly.
Active player (381)
Joined: 9/25/2011
Posts: 652
DrD2k9 wrote:
Typing 'N W' will go north then west. (it may require a comma between commands 'N, W' )
I can confirm this works (with commas)
DrD2k9 wrote:
I believe you can even do room actions within a stream of direction commands. 'N W Pickup X E E S'
This only works up to the pickup part. For example, the first commands in the game chain like this "N,N,U,GET EGG" and that works. But this gives an error: "N,N,U, GET EGG,D"
DrD2k9 wrote:
EDIT 2: I'd be willing to help if you want to collaborate.
Sounds great! FYI, if we have to do much RNG digging, my part may have to wait until the new year, as my free time is very limited right now.
Post subject: Baseline TAS for Apple II
Sand
He/Him
Player (147)
Joined: 6/26/2018
Posts: 210
Here's a first draft of a TAS of Zork I for Apple II. It uses revision 88 of the game, which, according to a survey by Data Driven Gamer, is "by far the most widely distributed version". The total time is 18871 frames or 5:14.52, in 258 moves. User movie #638893574504520990 Link to video (Since this TAS is for Apple II, I debated about whether to add it to this thread here in the MS-DOS Games forum or start a new thread in the Other Computers forum. I decided it's probably better to keep all the discussion for the game in one thread, even if it's a multi-platform game.) You may have seen that I've been working on scripting TASes with Lua and that I used the technique to improve the "fastest eaten by a grue" TAS. This is a straightforward extension to the full game. I didn't do independent route research for this run; instead I implemented Gym Slow's RTA route, unmodified except for a SUPER command at the beginning to activate superbrief mode. There are various small changes that would make the run faster taking the Apple II platform into account, as well as potential larger route changes taking advantage of the RNG manipulation that is possible in a TAS. I haven't done any of those. My idea was to let this run serve as a baseline to compare future improvements against. The run being scripted in Lua (actually Fennel) means that there is a script (100%.fnl) that contains a lot of code like this:
;; "80-COLUMN DISPLAY? (Y/N)"
(enter-text "n")

;; Skip over some loading. Get to somewhere near the first input prompt.
(savestate.next-frame 300)

(enter-commands ["super"])

(wait-for-keyboard-ready)
(savestate.next-frame 1) ;; RNG manipulation (avoid songbird).
; TODO use Shift or other keys for RNG manipulation.

;; Enter the schedule of commands.
;; TODO joining with "." can save time and alter RNG.
(enter-commands
  [
   ;; Climb the tree, get egg, and climb down again.
   "n"
   "n"
   "u"
   "get egg"
   "d"

   ;; Enter the house.
   "s"
   "e"
   "open"

   ;; ...
   ])
You can modify the run by modifying the script and re-running it in BizHawk. However, even small changes will require re-syncing RNG. I haven't done anything yet to automate RNG manipulation, which is needed to avoid unwanted random encounters, win combat quickly, and prevent certain other random events. I implemented RNG manipulation in this run by inserting 1-frame delays, though I think there are better ways to do it. You can see one instance of RNG manipulation in the snippet above, the (savestate.next-frame 1). More about RNG below. Other notes on this run and thoughts on possible improvements: 40-column mode vs. 80-column mode. The first thing the game does is offer a choice between 40-column mode and 80-column mode. 80-column mode looks nice: it has more text on the screen and uses both upper- and lowercase letters. I did a quick test of commands up to collecting the sword and lamp in the living room; it was 1279 frames in 40-column mode and 1341 frames in 80-column mode. So I guess 40-column mode is generally faster. But the choice of 40 or 80 columns could be considered a speed/entertainment tradeoff. Ultimately, I'd like to make the control script adaptively manipulate RNG, such that the run can be played back in either mode. Brief versus verbose. Besides the default verbose mode, the game offers a brief mode (which only shows you the long description of a room the first time you enter it, and a short description thereafter) and a superbrief mode (which always shows you the short description). Superbrief obviously saves time, as screen output on the Apple II is less than instant. Like the number of columns, the level of output verbosity could be considered a speed/entertainment tradeoff. (And similarly, any change will require re-synchronizing RNG.) RNG. Speaking of RNG, the good and bad news is that the RNG in the Apple II version is very sensitive, changing with granularity smaller than a frame. I suspect (but have not confirmed) the Apple II port of Zork uses the built-in KEYIN random number generator, which simply rapidly increments a 16-bit number whenever waiting for keyboard input. This is good in the sense that, in principle, you can probably get any RNG output you need with less than a frame of waiting. It's bad in that it's hostile to console verification, and the Virtu core doesn't provide easy access to sub-frame inputs, as far as I know. In this run, I've done RNG manipulation by manually inserting frame delays. But I also experimented with toggling the Shift key while entering commands, and that alone seems to change the CPU cycles enough to affect RNG. I think it should be possible to eventually do such RNG manipulation automatically, using the script, but it will require more work to understand the game's memory layout, in order to read the results of commands. You can see a listing of the part of the ROM that increments the 16-bit random number in the technical reference manual:
C83B:E6 4E          57 GETKEY  INC  RNDL      ;BUMP RANDOM SEED
C83D:D0 02   C841   58         BNE  GETK2
C83F:E6 4F          59         INC  RNDH
C841:AD 00 C0       60 GETK2   LDA  KBD       ;KEYPRESS?
C844:10 F4   C83B   61         BPL  GETKEY    ;=>NOPE
C846:8D 10 C0       62         STA  KBDSTRB   ;CLEAR STROBE
C849:60             63         RTS
Connecting commands with dots. It's possible to enter multiple commands at once, separated by periods. This capability is given in the manual. (c-square, your N,N,U,GET EGG,D in Post #476712 would have worked if you used periods instead of commas.) It was the main trick used to speed up the "fastest eaten by a grue" TAS, where it enabled ending input early. I didn't use this feature in this run, but I did a quick test, and to my dismay it appears that entering multiple commands on one line is slightly faster. N.N.U.GET EGG.D was 1690 frames in my test, compared to 1709 frames with the commands on separate lines. I say "to my dismay" because this would be a pretty extreme speed/entertainment tradeoff, a small gain in speed for a large decrease in entertainment. It's nicer to watch a run where the results of commands are close to the commands that caused them, not separated from them by pages of scrolling. I'm tempted not to make use of this trick, except perhaps when traversing long distances, such as through the mazes. Even if you were to enter every command of the run on one long line, it wouldn't allow ending input super early, because you would still have to press keys to scroll through all the [MORE] prompts and get to the ending. I tried joining just the last few commands, and buffering a Space press to clear the final [MORE] (as in Post #536873), but it doesn't work in this full-game run: after winning, the game seems to clear the keyboard buffer, or something, such that you have to wait for the final [MORE] to appear before you can dismiss it. Two-part commands. The Gym Slow route occasionally splits one command into two parts, in order to reduce keystrokes. This is when you enter a partial command, then the game asks for clarification, and you enter the word that's missing. For example, the route uses PUT SOLID and CASE to put the ("solid") gold coffin in the trophy case, because that saves having to enter the word IN:
>PUT SOLID
WHAT DO YOU WANT TO PUT THE SOLID IN?
>CASE
DONE.
This is good for an RTA run on a modern computer where the output is instantaneous, but on the Apple II it's probably better to spend some extra frames on input so the game doesn't have to emit so much output: PUT SOLID IN CASE. (Incidentally, these two-part commands don't work when entered as a single line separated by a period: you can't do PUT SOLID.CASE.) A similar case is in the dam maintenance room, where this route does PUSH ALL. There's only one thing in the room you need to push, but this command results in a screenful of output as you try to push every item (including the one you need to push). It may be faster to be more specific in the command so that there's less output. Early thief kill? This route leaves the battle with the thief until near the end. But before that, there are two occasions where you enter the thief's hideout in order to warp to the temple; each of these displays a long message about the thief entering the room and requires RNG manipulation to avoid getting killed or wounded (which would reduce carrying capacity). It may be possible, with RNG manipulation, to kill the thief at the first encounter. This would ease the rest of the run, as there would be no need to worry about random thief encounters or combat in the hideout. I haven't yet checked to see how combat works in the source code. It's conceivable that a route would call for getting robbed by the thief at a time when you're carrying lots of treasure far from the house, as that would effectively warp the treasure to be near the house, and free up your carrying capacity to pick up more items. I hope that's not required, because it would require careful manipulation. The thief's attack isn't a random probability per room; he actually physically moves around the cave at random. The route already uses the "raft of holding" bug (bug #27 in Graeme Cree's list) for the hardest part of inventory weight management. Vampire bat RNG manipulation? Normally, you're supposed to use the garlic to incapacitate the bat, which otherwise picks you up and drops you in a random place in the coal mine. It may be possible to instead allow the bat to grab you, and manipulate it to drop you deep in the mine, saving one of the traversals through the mine. (The current route traverses the mine a total of four times, twice heading in and twice heading out.) It's possible for the bat to drop you outside, in the squeaky room, so it wouldn't be necessary to have the garlic even on the way out. Bypass torch/dumbwaiter puzzle? Bug #41 in Nathan Simpson's list says that you don't need the torch in order to get light into the drafty room past the coal mine. There's a trick where you can store the candles in the sack and still fit through the small opening. I don't think it would save a ton of time, because you'd still need to get to the end of the mine and back to put the coal in the dumbwaiter, but it could free up routing possibilities with regard to when the torch gets deposited in the trophy case. The page says it only works if you extinguish and re-light the candles, so it would require getting the matchbook from the dam lobby, which the route currently doesn't do.
Skilled player (1746)
Joined: 7/1/2013
Posts: 458
Exciting stuff, Sand!
Editor, Experienced player (534)
Joined: 11/8/2010
Posts: 4113
Very nice progress, Sand! I agree that the period trick can probably be used for directional input as it's otherwise just boring movement.
Post subject: RNG manipulation with ASCII codes
Sand
He/Him
Player (147)
Joined: 6/26/2018
Posts: 210
Sand wrote:
RNG. Speaking of RNG, the good and bad news is that the RNG in the Apple II version is very sensitive, changing with granularity smaller than a frame. I suspect (but have not confirmed) the Apple II port of Zork uses the built-in KEYIN random number generator, which simply rapidly increments a 16-bit number whenever waiting for keyboard input. This is good in the sense that, in principle, you can probably get any RNG output you need with less than a frame of waiting. It's bad in that it's hostile to console verification, and the Virtu core doesn't provide easy access to sub-frame inputs, as far as I know. In this run, I've done RNG manipulation by manually inserting frame delays. But I also experimented with toggling the Shift key while entering commands, and that alone seems to change the CPU cycles enough to affect RNG. I think it should be possible to eventually do such RNG manipulation automatically, using the script, but it will require more work to understand the game's memory layout, in order to read the results of commands.
The source code for many versions of Infocom's Z-Machine interpreters is online, so we can easily see how the RNG works in the Apple II version. As I suspected, the Apple II Z-Machine interpreter (ZIP) uses the KEYIN random number generator built into the Apple II firmware, which rapidly increments a 16-bit counter at addresses 0x4e and 0x4f (which the ZIP calls RNUM1 and RNUM2) while waiting for a key to be pressed. But there's a twist: after a key is typed, the ZIP mixes the ASCII code into the RNG state. This is good news, because it means you can manipulate RNG by introducing variations into the commands you enter. In particular, you can easily affect RNG state by swapping lowercase letters to uppercase, with no loss of time. The ZIP's GETKEY subroutine calls the Apple II RDKEY subroutine, which in turn calls KEYIN. KEYIN returns an ASCII code in the A register and leaves RNUM1 and RNUM2 in some hard-to-predict state. At the end of GETKEY, the ZIP further tweaks the random number state by adding the ASCII code into RNUM1 and xoring it into RNUM2: https://github.com/erkyrath/infocom-zcode-terps/blob/d5ac95a838/apple/zip/machine.g#L100
	ADC RNUM1		;FUTZ WITH RANDOM
	STA RNUM1
	EOR RNUM2
	STA RNUM2
Here's an example of how these observations could be applied. Certain rooms in the overworld are designated "forest rooms". Whenever you take a turn in a forest room, there's a 15% random chance that the game will print the message "You hear in the distance the chirping of a song bird." (Which is a hint for a later puzzle.) We want to avoid that random event, because every line of output takes time. Suppose you enter the command get egg while in the room UP-A-TREE, and, in addition to picking up the egg, you unluckily get the songbird message. Then you can backtrack and try the command get egG. If that doesn't work, try get eGg, then get eGG, and so on. If you exhaust all the letter case possibilities of the most recent command, you can continue with the next most recent, and so on, until you get the RNG you need. I hope to automate this style of RNG manipulation to enable flexible experimentation with routing. As things stand now, if you wanted to try s.e instead of n.e to get behind the house at the beginning of the run, for example, it would desync all later random events. Automated RNG manipulation might also make it possible to play (what is effectively) the same run in either 40-column more or 80-column mode, or in either brief more or verbose mode. That would not normally be possible, because the timing, and hence the RNG, in the various modes would be different. But with a Lua script automatically adjusting the letter case of commands to make sure random rolls always come out the right way, it may be possible for the same route to work in either mode. One movie might have get eGg where the other has get EGg, but they would be effectively the same. Automated RNG manipulation may also make the run robust to timing changes in a future version of the emulator. The main RNG events I can think of that will require manipulation are the songbird, troll and thief combat, candles being blown out in TINY-CAVE, and bat random drops. (I was surprised to learn that the movement of the thief is not random: rather he warps from room to room in order, skipping certain rooms.) The hardest one will probably be the thief combat, where we'll need a series of low-probability attack and defense rolls. But even that I think will be tractable.
Post subject: Loud Room input illusion
Sand
He/Him
Player (147)
Joined: 6/26/2018
Posts: 210
Sand wrote:
Connecting commands with dots. It's possible to enter multiple commands at once, separated by periods. This capability is given in the manual. (c-square, your N,N,U,GET EGG,D in Post #476712 would have worked if you used periods instead of commas.) It was the main trick used to speed up the "fastest eaten by a grue" TAS, where it enabled ending input early. I didn't use this feature in this run, but I did a quick test, and to my dismay it appears that entering multiple commands on one line is slightly faster. N.N.U.GET EGG.D was 1690 frames in my test, compared to 1709 frames with the commands on separate lines. I say "to my dismay" because this would be a pretty extreme speed/entertainment tradeoff, a small gain in speed for a large decrease in entertainment. It's nicer to watch a run where the results of commands are close to the commands that caused them, not separated from them by pages of scrolling. I'm tempted not to make use of this trick, except perhaps when traversing long distances, such as through the mazes.
There's another place where joining multiple commands with dots doesn't work. The Loud Room, where—amazingly—the game sets up a simulation of the main game loop, complete with fake > prompt, in order to implement the echo puzzle. If there are any commands remaining when you enter the Loud Room (P-CONT is not empty), they get "lost in the noise".
The Troll Room There is a bloody axe here. >e.e.e.echo.get bar East-West Passage Round Room Loud Room On the ground is a large platinum bar. The rest of your commands have been lost in the noise. >
If you include additional commands after the command to solve the puzzle, it looks like they are ignored.
Loud Room On the ground is a large platinum bar. >echo.e The acoustics of the room change subtly. Loud Room On the ground is a large platinum bar. >
Post subject: Automated RNG manipulation
Sand
He/Him
Player (147)
Joined: 6/26/2018
Posts: 210
Sand wrote:
I hope to automate this style of RNG manipulation to enable flexible experimentation with routing. As things stand now, if you wanted to try s.e instead of n.e to get behind the house at the beginning of the run, for example, it would desync all later random events. Automated RNG manipulation might also make it possible to play (what is effectively) the same run in either 40-column more or 80-column mode, or in either brief more or verbose mode. That would not normally be possible, because the timing, and hence the RNG, in the various modes would be different. But with a Lua script automatically adjusting the letter case of commands to make sure random rolls always come out the right way, it may be possible for the same route to work in either mode. One movie might have get eGg where the other has get EGg, but they would be effectively the same. Automated RNG manipulation may also make the run robust to timing changes in a future version of the emulator.
I've done the automation of RNG manipulation and used it to execute the same route as before under all 4 settings combinations of 40/80 columns and brief/superbrief. Here is how the times work out, from the fastest to the slowest settings:
ColumnsBrevityFramesTime
40superbrief187755:12.92
80superbrief198275:30.45
40brief234336:30.55
80brief251396:58.98
As you can see, 80 columns is about 6% slower than 40 columns, and brief mode is about 25% slower than superbrief mode. Even though it's the slowest combination, I plan to use "80 columns, brief" as the settings for the run, in a speed/entertainment tradeoff. Brief mode shows the room descriptions that are never shown in superbrief mode. 80 columns has distinct uppercase and lowercase letters, which is nicer to read and makes the mechanism of RNG manipulation visible. User movie #638973083207267679 is the movie file for "80 columns, brief". Here are encodes for all 4 combinations of settings: 40 columns, superbrief (5:12.92) Link to video 80 columns, superbrief (5:30.45) Link to video 40 columns, brief (6:30.55) Link to video 80 columns, brief (6:58.98) Link to video As before, the commands of the route are input by a Fennel script. The big enhancement in this revision of the script is automatic RNG manipulation by means of varying letter case. As I've discussed, the Apple II Z-machine interpreter's RNG is sensitive to timing at almost the cycle level, so any small change, such as from 40 to 80 columns or from brief to superbrief mode, requires redoing every manipulation. Luckily, the RNG state does not depend only on timing—it also mixes in the player's keyboard input. Whether letters are uppercase or lowercase doesn't matter to the game's text parser, but uppercase and lowercase letters do affect the game's RNG state differently. When the route needs a certain RNG outcome, it tries the command and checks if it worked; if not, then it backtracks, flips the case of an earlier letter, and tries again. (In 40-column mode, there's no visual difference between uppercase and lowercase, but this procedure is still happening.) For example, during the troll battle, we need a few random events to go our way:
  1. The troll must not attack us immediately on entering the room (68% chance)
  2. We must get a one-hit kill on the troll (2 in 9 chance)
  3. We must get the shortest combat result remark (1 in 3 chance)
Here are the case variations that lead to the desired outcome under each of the 4 combinations of settings. Case variation can reach back more than one command if necessary, as far as it needs to.
ColumnsBrevityCommand
40superbriefN;n;hit iT
80superbriefn;n;Hit it
40briefn;n;hit It
80briefn;n;hIT IT
Whenever you see capital letters, some kind of RNG manipulation is happening. If there's no obvious purpose to the manipulation, it's probably to prevent the thief from moving items on his circuit through the dungeon. Even though it's using the same route, this version of "40 columns, superbrief" is slightly faster than the one in Post #537196, because of the use of case variation rather than frame delays for RNG manipulation. How this looks in the code is, ranges of commands can be annotated with "must-not" and "must" events. As the script enters commands, it registers BizHawk events representing undesired or unnecessary outcomes. If, while entering the range of commands, any "must-not" events happen, or if, after entering the range of commands, not all of the "must" events have happened, the script backtracks and tries a different case variation. For example, below, we annotate the n command that enters the Troll Room with a "must-not" event for the VILLAIN-BLOW function being called. On the hit it command to attack the troll, we put a "must" event for the enemy being hit with a certain outcome (KILLED) and random remark (3).
(enter-commands
  [
   ; ...

   ;; Get the painting and go back to the cellar.
   "s"
   "e"
   "get"
   "w"
   "n"

   ;; Defeat the troll. The troll has a <PROB 33> chance of attacking as
   ;; soon as we enter the room, before we get a chance to act.
   [{:must-not [(make-check-exec-zaddr VILLAIN-BLOW-ZADDR)]}
    [
     "n"
     ]]
   ;; Require a one-hit kill with the shortest remark
   ;; "The troll takes a fatal blow and slumps to the floor dead."
   ;; https://github.com/historicalsource/zork1/blob/34cc828c4f/1actions.zil#L3574
   [{:must [(make-hero-blow-res-remark KILLED 3)]}
    [
     "hit it"
     ]]

   ; ...
   ])
The battle with the thief, near the end of the run, is the most RNG-intensive part of the run. Here, I relaxed the manipulation to require only certain combat outcomes, without also requiring the shortest random remark. This was just to speed up the manipulation. By my estimate, an optimal fight, including remarks, has about a 1 in 3000 chance. Ignoring remarks, it's about 1 in 60. It may be possible to do a more thorough optimization when the run is close to being finished. I also haven't checked to see if the slight difference in remark lengths actually makes a difference with respect to time. With automatic RNG manipulation in place, I now have freedom to experiment with changes to the route.
Post subject: Route changes, virtual memory
Sand
He/Him
Player (147)
Joined: 6/26/2018
Posts: 210
I started experimenting with changes to the route and measuring the effect they have on completion time. After a few easy wins, I encountered an interesting situation: a faster way to do one segment that ends up being slower overall by the end of the run. After investigation, I suspect that the cause is the game's virtual memory system, which swaps content into memory from the floppy disk. Here's a summary of the effects of changes so far:
Change
Time difference
Comment
Baseline (Post #538557)
25139 frames
Change ulysse to odysse
−34 frames
?
Combine two-part commands (e.g. put solid;caseput solid in case)
−1479 frames
Use more conventional words (pealring, heapcoal)
+3 frames
?
Stack directional commands (w;s;e;uw.s.e.u)
−781 frames
temple rather than pray after getting coffin
+152 frames
???
The first change in the table, changing ulysse to odysse, I did for reasons of canonicity. (The cyclops is Greek, so he should know the Greek name, not the Latin name; also the acrostic in the black book spells out ODYSSEUS.) The strings are the same length, so it should be an inconsequential change—but it saves 34 frames. I am not sure, but the reason may have something to do with the virtual memory concerns I'll describe below. Joining two-part commands, as suggested in Post #537196, proves to be a big win on the Apple II implementation. Even thought it's more keystrokes, it saves a round trip through the parser and interpreter. It possibly also avoids some code paths that then don't have to be paged into memory. I thought the words peal and heap in the Gym Slow route were a bit weird and wanted to change them to the more normal ring and coal. This again should be an inconsequential change, but it results in a minuscule loss of frames. Why? I'm not sure. Stacking commands with . also turns out to be a big win. But, now that I've seen how it looks in a movie, I'm not so sure I like it and may decide to restrict myself to one command per input line. The last one is the interesting one. After getting to the gold coffin, you need to use a warp to return it to the trophy case, because it's too large to get out any other way. The Gym Slow route uses the pray command to warp back out to the forest, then re-enters the house from outside. I had the idea of instead using temple to warp to the thief's hideout, then take the secret passage back to the living room. This is how it breaks down:
pray frames
pray command
temple frames
temple command
Difference
4334
open it
4334
open it
0
4511
u.s
4511
u
0
4623
pray
4547
tEmPLE
−76
4721
e.s.e.w.w
4656
d.E.e
−65
4940
put solid in case
4814
put solid in case
−126
5146
open trap
4983
open trap
−163
By the time the two routes reconverge with put solid in case, the temple route is 126 frames ahead. But the lead doesn't remain constant: you can see that in the next command, it grows to 163 frames. The temple route's lead grows and shrinks until eventually it falls behind around frame 15000 and finishes over 150 frames slower. The left side of the graph below compares the long-term timing differences of the two slightly different routes. Two subgraphs. The left is a line graph with the title "Frames to reach each input line". The x-axis is "line" and the y-axis is "frames". There are two series: PRAY and TEMPLE. The two series are identical until about line 250/frame 5000, then TEMPLE starts to have slightly fewer frames per line. At about line 800/frame 15000, PRAY moves ahead and finishes at about line 1600 about 150 frames faster. The subgraph on the right is titled "Swaps". Its y-axis is "frame" and is aligned with the left subgraph. Two columns of a few hundred horizontal lines, colored to match the PRAY and TEMPLE lines in the other graph, show at what frames swaps occurred. They are identical up to about frame 5000, then they diverge. What's going on? I thought about it a lot. My best guess is that is has to do with the virtual memory system of the Z-machine. The game's story file is over 80 KB large, but the Apple II has only 64 KB of memory (and not all of that can be freely used). So what the game does is swap information from the disk into memory on demand. A page table keeps track of what pages in memory correspond to what pages on disk. Once the page table is full, every page that is swapped in from disk necessarily evicts a page that already existed; if that other page is needed again later, it will have to be swapped back in. I suspect that the praytemple route change, even though it uses fewer and faster commands, perturbs the contents of the page table in such a way that eventually requires doing more swaps and spending more time waiting for the disk. The right-hand side of the graph above shows the frames at which swaps occur in the two routes. They are identical up to the route change; then they diverge. There are some similar patterns, but one is not simply the shift of the other. In total, the pray route uses 655 swaps and the temple route 670. This kind of thing is hard to optimize, when a small change can have far-reaching and unpredictable effects. It does suggest a heuristic, which is to try to minimize the ranges of addresses that are accessed. I haven't checked, but that may be what's going on with the ulysseodysse change. In the source code, ODYSSEUS is the main syntax element, while ULYSSES is marked as a synonym. It may be that these two words are stored in different tables in memory, and that odysse requires only one memory access while ulysse requires two. That's just a guess, I haven't checked. I plan to try switching some commands to use the main word rather than synonyms, even when the synonym is shorter. It may explain why pealring had heapcoal an effect. In this case, though, RING and COAL are the main syntax elements. Maybe the lists of synonyms is near a page boundary, or something. I haven't checked. Differences in page tables is also the kind of thing where changes elsewhere in the route may end up making the temple variant faster after all.
Patashu
He/Him
Joined: 10/2/2005
Posts: 4093
Brought this up to a friend who knows more Apple II: "Ah yup, that's a thing - one extra cycle per instruction that reads two bytes, if the first byte is at an address ending in 0xFF. Though looking at my 6502 reference, not for all instructions that can cross page boundaries? Only some have the +1 cycle annotation."
Puzzle gamedev https://patashu.itch.io Famitracker musician https://soundcloud.com/patashu Programmer, DDR grinder, enjoys the occasional puzzle game/shmup.
Sand
He/Him
Player (147)
Joined: 6/26/2018
Posts: 210
The 6502 taking an extra cycle for certain instructions when they span a 256-byte boundary is a thing, but that's not what I'm referring to. The ZIP virtual memory system is responsible for loading parts of the game from disk into memory, on demand as they become required. The size of pages in the virtual memory system also happens to be 256 bytes, but it's not related to the 6502 page boundary thing. The Z-machine program counter ZPC is a 24-bit integer, even though the Apple II only has a 16-bit address space. There's a routine called NEXTPC that tries to retrieve the next byte of "Z-code" (code for the abstract virtual Z-machine). When NEXTPC reaches the end of a page, it needs to ensure that the next page is loaded into memory somewhere. It first checks the page table PTABL/PTABH to see if the required page of Z-code (on disk, in 24-bit Z-address space) has already been stored in memory (in Apple II 16-bit address space). If not, the virtual memory searches for an unused page in memory, or evicts an existing page to make room for the new page that is now required. (The page eviction is supposed to use some kind of LRU strategy, though I suspect there is some weird behavior because access "timestamps" are only 8-bit and cyclic.) Small changes in how the page table is populated could cause long-term and hard-to-predict changes in the frequency of disk access. There are places in the game code (now I'm talking about the virtual Z-code of the game itself, not the 6502 assembly code of the Z-machine interpreter) that iterate through a list of candidates, for example SEARCH-LIST that consults a table of synonyms. If such a table happened (purely by chance in the compilation process) to cross a page boundary, you might get large timing differences depending on whether the thing you are searching for is early or late in the table (before or after the page boundary). Searching for something near the beginning of the list might require only 1 page to be loaded from disk, while searching to the end might require loading 2 pages. I guess it would, in principle, be possible to audit the Z-code story file to find all such tables that cross page boundaries. But it shows how it could be better, in some cases, to use a longer synonym to refer to something: even if it requires more keystrokes, it might avoid a disk access.

1763438920