IFComp 2020: Amazing Quest (Nick Montfort, C64 BASIC)

I have no business writing about IFComp. A huge number of incredibly impressive games have been entered into the competition over the years, but I’ve only ever played a tiny fraction of them, so I just don’t feel qualified to expound on the subject, which is why I haven’t. Instead, I’ve stuck to my comfort zone, which is whittering on about the BBC Micro.

But when I saw that one of the games in this year’s competition was written in BASIC for the C64, I knew I had to give it a go.

 

Yes or no?

What I discovered was that Amazing Quest by Nick Montfort (author of the ingenious wordplay game Ad Verbum) wasn’t really what I had hoped it would be.

What I’d been hoping for was a full-blown text adventure game on a retro platform. But what I actually got was a short twelve-line program that generates random responses to user-input.

That’s right: the input itself is completely irrelevant! The choices you’re invited to make when you play the game are binary yes/no ones, but they don’t affect the outcome of the game in any way whatsoever. The game simply ignores whatever you type into it. (So, to save time, you can just keep pressing Return or Enter.)

Consequently, I’m not sure how Amazing Quest should be classified. It’s not really a text adventure game. And it’s not a choice-based game either because your choices don’t matter. Perhaps it’s a sort of experimental piece of avant-garde retro computer art..? (I’m reaching here.)

But whatever it is, it’s certainly a bit of a puzzle! And it seems that I’m not the only one who was left feeling a little nonplussed.

[Update: The author responds!]

But my confusion didn’t stop me trying to hack the game to port it to the BBC Micro, as is my wont. In the process of doing so, I think I found a bug — if this curious, aimless gamealike can in fact be said to have a bug (but if it can, then I’m almost certain the bug is real, unlike last time).

 

Source code download decode reload

If you want to see the BASIC source code for Amazing Quest, then all you have to do is complete a run-through of the game — after which you’ll simply be dropped onto the BASIC commandline, where, if you type in the command LIST, the program will scroll up the screen in all its 8-bit glory. Which is fine and dandy — but I wanted to get a copy of the listing in a form that I could tinker with, and I’m not familiar enough with the C64 to be able to tinker under emulation.*

Fortunately, on viewing the Javascript file that’s loaded by the webpage for the game, I happened to quickly find a text-string that turned out to be a Base64 encoding of the tokenised BASIC program. I decoded the text by simply using the command base64 in the Terminal app on a Mac, which spat out the “binary” of the tokenised C64 BASIC program, which I was then able to detokenise (i.e. convert to plain text) using an MS-DOS executable called bastext (which I found online somewhere, a while ago, when I was porting Nellan Is Thirsty). Phew.

I now had a copy of the BASIC listing of the program in a plain text file.

 

Port or starboard?

Porting the program to BBC BASIC was straightforward because BBC BASIC and C64 BASIC are not-so-distant cousins, it turns out, and I soon had the game running in a Beeb emulator. And that’s when I found what, for the sake of convenience, I shall continue to call a “bug”.

One of the random events in the game is that, every turn, there’s about a 20% chance that your fleet of ships will be attacked. The listing suggests that it was the author’s intention that if you were attacked then there should be a chance that you wouldn’t lose any ships. But the bug in the code meant that if you were attacked you would in fact always lose a ship!

The bug in the C64 program

My fix for the bug was simple and involved implementing a 50% chance of losing a ship whenever you’re attacked:

The bugfix in my BBC BASIC port

Why I chose 50% I couldn’t really say. Why I even bothered investigating and messing with the game to this extent I couldn’t really say either…

This has probably been one of the “nichest” blogposts in an already extremely niche corner of what I laughably still call the “blogosphere”.**   But if you’ve read this far, then perhaps the joke’s on you. Don’t worry, though: you’re not alone because I, like several other people, have actually tried to play Amazing Quest — so the joke’s on us too.

 

Quest!

Anyway, I’ve no idea why you’d want to, but you can play my BBC Micro port of Amazing Quest under emulation in your web-browser:

Play my BBC Micro port of Amazing Quest by Nick Montfort

I provide my port of the game under the licence specified by Nick Montfort, the author of the original C64 program, viz. Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0).

 

Update: The author responds!

 

Update 2: Annotating the BASIC source code

I’ve decided to provide absurdly detailed annotations for every line of the original C64 BASIC program (because, I guess, life just isn’t tough enough right now…).

Oh, before I dive in, let me just mention that the code is hard to read because the author has used a very terse coding style. He has omitted spaces. He has concatenated multiple statements onto a single line. And he has inserted data-values throughout the program, wherever they would fit, instead of grouping them together at the end of the program, which would have improved its readability. (Far be it from me to suggest that the terseness was due to the author wanting to obfuscate the code for some reason.)

If you LIST the original source code after the program has ended, all the BASIC keywords (such as IF, PRINT, and DATA) appear in lowercase, but I reproduce them below in uppercase because that’s how they were output by bastext.

0 d=11:POKE53280,d:POKE53281,d:PRINT"{clear}{ct n}{light gray}"SPC(94)"AMAZING QUEST{down*2}":PRINT"The gods grant victory.

Line 0: A value of eleven is assigned to the variable d, which is then used to configure the background colour of the screen — I think. (I’m not familiar with the hardware details of the C64.) Next, the name of the game, “Amazing Quest”, plus some C64-specific control characters, is printed at the top of the screen, preceded by 94 spaces and followed by the intro message “The gods grant victory.”

1 DIMa$(99):j=-1:FORi=.TO4:READm(i):FORk=1TOm(i):READa$(j+k):NEXT:j=j+k:NEXT:PRINT"Now to home!

Line 1: Memory is allocated for an array named a$ which can store 99 strings of text. (I found that that was overkill, as the program actually only uses 45 strings.) A loop then reads the text-strings into the array. The text-strings can be found embedded in data statements scattered awkwardly throughout the program. Finally, the message “Now to home!” is printed on screen.

2 PRINT"{down}You";:j=6:FORi=1TO4:PRINT" "a$(j+RND(0)*m(i));:j=j+1+m(i):NEXT:PRINT".":PRINT"S"a$(RND(0)*5);

Line 2: The word “You” is printed on screen, and then a message is constructed which describes the player’s current status: the message consists of a random combination of the text-strings in the array a$. The first string in the status-message is a verb of motion or discovery, and will be one of the following: “alight on”, “are blown to”, “behold”, “detect”, “find”, “land on”. The second string in the message is the indefinite article “a”. The third string is an adjective describing the location at which the player has arrived, and will be one of “brutal”, “dark”, “dim”, “diverse”, “dry”, …, “uniform”, and “wet”: see lines 7 to 9 for the full list. The fourth string is a noun identifying the type of location, and will be one of “area”, “capital”, “moon”, “land”, “palace” and “settlement”: see line 11. (Therefore, an example status-message might be “You are blown to a dark moon”.) The status-message is printed and terminated by a full stop. Then, one of five actions is printed. The possible actions, which all begin with S, are “Sneak up and raid”, “Speak plainly”, “Sacrifice to the gods”, “Seek out help”, and “Send gifts”: see lines 3, 4 and 7 (where the actions appear without the initial S).

3 INPUT"-Y/n";z$:WAIT162,128:DATA5,neak up and raid,peak plainly,acrifice to the gods

Line 3: The player is presented with a choice of typing either “Y” or “N” to indicate whether or not they wish to perform the action that has just been printed on screen by line 2. “Y” is capitalised in order to suggest that it’s the default choice (and to imply that if the player doesn’t type any letters and simply presses Enter instead then the program will assume “Y”). The program waits for the player to type something and to press Enter. Whatever the player types is stored in the variable z$ — which is never referred to again by any part of the program: i.e. the player’s input is completely ignored! The program then pauses for a random length of time, up to a maximum of about two seconds, apparently to give the false impression that some sort of computation or processing is going on. (The rest of line 3 consists of a data statement containing the action strings mentioned in the notes on line 2, above.)

4 r=RND(0):IFr<.2THENPRINT"Attacked"RIGHT$(", a ship lost",-13*(r<.2))"!":GOTO2:DATAeek out help

Line 4: A random floating-point number between zero and 1 (with whatever precision the C64 offers) is generated and stored in the variable r, which is then used to determine whether or not the player’s fleet of ships will be attacked. If the value of r is less than 0.2 — which obviously has about a 20% chance of being true — then an attack is considered to have taken place, and the word “Attacked” is printed on screen. r is then tested again to determine whether the player has lost a ship in the attack. If that second test of the value of r succeeds, then the string “a ship lost” is printed after the word “Attacked”. (In the original version of the program, that second test of r on line 4 had a bug, whereby the test would always succeed (as long as the first test of r had already succeeded, of course) — which made the second test pointless, and meant that whenever an attack occurred the player would always lose a ship. The author has since changed the second test from r<.2 to r<.1 so that it succeeds just 50% of the time, with the result that now only half of the attacks will end in the loss of a ship.) An exclamation mark is printed to terminate the message about the attack. The program then jumps back to line 2. (The rest of line 4 consists of a data statement containing one of the action strings mentioned in the notes on line 2, above.)

5 IFr<.4THENPRINT"Well-you see an amazing "MID$("sea.sky.sun.",1+INT(RND(0)*3)*4,4):GOTO2

Line 5: Line 5 will be executed (or, strictly speaking, interpreted) if and only if the r-test at the beginning of line 4 failed. That will happen 80% of the time, which means that line 5 has an 80% chance of being executed. If line 5 is indeed executed, then we now know that the value of r must be greater than (or equal to) 0.2. The first thing that line 5 does is test the value of r again, this time to see if it’s less than 0.4. If it is, then we now know that r must have a value between 0.2 and 0.4, which will occur 20% of the time, which means that there’s a 20% chance that the remainder of line 5 will be executed. What the remainder of line 5 does is print the message “Well — you see an amazing”, followed by a random selection of one of three words: “sea”, “sky”, or “sun”. The program then jumps back to line 2.

6 d=d-1:PRINT"Yes! You win "MID$("jewels.cattle.bread.",1+INT(RND(0)*3)*7,7):IFd>.GOTO2

Line 6: Line 6 will be executed if and only if the first r-tests on lines 4 and 5 both failed. That will happen 60% of the time, so line 6 has a 60% chance of being executed. The first thing that line 6 does is decrement the value of the variable d by 1. Then it prints the message “Yes! You win” followed by a random selection of one of three words: “jewels”, “cattle”, or “bread”. The value of d is then tested to see if it’s greater than zero. If it is, then the program jumps back to line 2. But if d is equal to zero, then line 10 will be the next line to be executed (see below).

7 DATAend gifts,6,alight on,are blown to,behold,detect,find,land on,2,a,a,18,brutal
8 DATAdark,dim,diverse,dry,dusty,fine,fortified,hexagonal,huge,luminous,pious,proud
9 DATAretrograde,spare,tiny,uniform,wet,11,area,capital,moon,land,palace,settlement

Lines 7-9: Data statements containing some of the numerical and textual data that drives the program. See previous notes for explanations of how the program uses these data-values.

10 PRINT"{down}At last, the battered shuttle brings youalone home to family, hearth, rest.

Line 10: Line 10 will be executed if and only if the test of the variable d on line 6 failed. That will happen when line 6 has been executed a total of eleven times and the “You win” message has therefore been printed on screen eleven times also. Thus, the player needs eleven “wins” to complete the game. Line 10 simply prints the “victory” message on screen (“At last, the battered shuttle…”). The program then ends because line 11 contains no executable code.

11 DATAport,city,outpost,planet,stronghold:REMix! (c) 2020 nick montfort, nickm.com

Line 11: A data statement containing some of the textual data that drives the program. See previous notes for explanations of how the program uses these data-values. The last BASIC statement on line 11 is a comment (indicated by the BASIC keyword “rem”, which the author has cunningly disguised by immediately following it with the characters “ix!” to create the word “remix!”). The comment includes a copyright notice.

The original C64 BASIC source code, obtained by typing LIST after the program had ended

* The author has provided a screenshot of the program source, but the code is in a compressed form, probably tokenised C64 BASIC. Not very readable. Certainly not editable. [Update 3: The author informs me that it’s not tokenised BASIC in the screenshot. I now believe that the screenshot shows the program as it would appear on screen if you typed it into a real C64 using the abbreviated uppercase form of C64 BASIC.]

† Yes, I said “niche corner”. Deal with it.

** Yes, I’m now implying that a sphere can have corners. Deal with it.

This entry was posted in Uncategorized and tagged , , , , , , , , . Bookmark the permalink.

14 Responses to IFComp 2020: Amazing Quest (Nick Montfort, C64 BASIC)

  1. nickmontfort says:

    I commend you on very thoroughly playing Amazing Quest! I intend the game not to make fun of anyone, but to be fun, although perhaps it’s a niche sort of fun. It seems like you’ve gotten some enjoyment out of the time you spent with the game.

    You are right, too, that the second “r>.2” in line 4 is a mistake. I meant to type “r>.6” which results in a ship being lost 50% of the time (conditioned on an attack having happened). Thanks—I’ll have to fix that.

    • Ant says:

      You are right, too, that the second “r>.2” in line 4 is a mistake.

      It’s actually r<.2. But the real issue (if I understand C64 BASIC correctly) is that in your original program if the first r<.2 test on line 4 succeeds, then the second test will always succeed because the value of r will still be the "old" value that was assigned at the beginning of the line with r=RND(0): i.e. the value of r will always be the same in both tests on line 4. The result is that in your original program if an attack happens then a ship is always lost.

      See my fixed version of the line in the second code screenshot in the blogpost, above. (In BBC BASIC you have to use RND(1) rather than RND(0) if you want to generate a random float between zero and one, but that's the only "gotcha" to be aware of, I think.)

  2. nickmontfort says:

    Yes, we both understand perfectly, my > was a typo for < when I commented, and my original ,2 was wrong.

    Your change to .5 was a fine one, although it means a ship will be lost only 3/8 of the time after an attack. Nothing wrong with that, I've just decided to go with .6 when I fix this.

    • Ant says:

      I don’t want to belabour this because I know it’s not really the point (if there is one!), but let me just state explicitly that the “bug” arises because in your original C64 BASIC program the variable r is re-used without being re-assigned after its initial assignment to RND(0).

      But how are you arriving at a value of 3/8? If I understand my BBC BASIC version correctly, an attack will occur roughly one fifth of the time: i.e. when r<0.2 where r=RND(1). If an attack occurs, then a ship will be lost about half the time: i.e. when RND(1)<0.5.

  3. nickmontfort says:

    “Re-using” r isn’t a problem if it’s done properly. I just made a mistake at first, using “2” instead of “6.” I check to see if r>.2; if so, an attack happens. Now I check again, knowing r>.2, to see if that same r>.6. It is half the time, which is what I want to check. Try it yourself if you like.

    • Ant says:

      Oh, I see: you actually intended to use r>.2 in the program. That wasn’t clear to me. I thought you were saying that the > was only a typo in your comment on this blogpost and that you had actually intended to use a < in the program itself!

      But what you actually want is for an attack to take place about 80% of the time (r>.2), rather than 20% of the time (r<.2)? And you want half the attacks to result in the loss of a ship?

      In that case, yes, the first test on line 4 should be r>.2 and the second should be r>.6

      (EDIT: I should add that my bugfix completely avoids re-using the variable r on line 4 and instead simply calls the RND function for a second time but without assigning the result to a variable.)

    • Ant says:

      Hang on: if you want the first test on line 4 to be r>.2 then line 5 will only be executed if r<=0.2 which means that if line 5 is executed then the test on line 5 will always succeed! (The test on line 5 is r<.4.) Which means that line 6 will never be executed, and the game will never end!

      (Btw, when I say “executed” I do of course mean “interpreted”. (Phew. Got out of that one.))

  4. nickmontfort says:

    Turned out to be much easier to fix this than to discuss it! I have the change up here and will see about putting it out to the Comp tomorrow:

    https://nickm.com/if/amazing_quest/

    You can LIST 4 to see the change I made. No need to finish a game; you can press ESC, which serves as the STOP key, as soon as things begin, if you like.

    Thanks again for letting me know about the bug.

    • Ant says:

      So you’ve stuck with r<.2 as the first test on line 4, which means that, each turn, there’s a 20% chance that the player will be attacked. You’ve then changed the second test on line 4 to r<.1 which means that half the attacks will result in the loss of a ship.

      That’s in effect equivalent to my original bugfix (see my blogpost, above), which also uses r<.2 as the first test on line 4 but then uses RND(1)<.5 as the second test: i.e. in my bugfix on line 4 there’s a second explicit call to the RND function (but this time the result isn’t assigned to a variable). The second test will succeed 50% of the time. The end result is the same: the chance of an attack occurring is 20%, and half the attacks will result in the loss of a ship.

      (Perhaps my fix incurs a slight cost in processing time because it calls RND once more than yours, but if so then it’s imperceptible to the player. And anyway BBC BASIC is fast as flip.)

  5. Pingback: The Golden Baton (1981) | Renga in Blue

  6. Pingback: Amazing Quest – Interactive Fiction

  7. nickmontfort says:

    Ant, you’ve gotten further into this than almost anyone else, which I greatly appreciate. I hope the work of porting this was interesting and in some ways enjoyable for you.

    Since we have been having a technical discussion, I wanted to point out one thing. You suggest that the PNG image is “probably tokenised C64 BASIC.” I understand why you’d think that, but it isn’t. As unusual as it may seem, I’ve actually provided a view for people that represents how I typed the code in and would show others the only possible way to type it in. See for instance:

    https://www.c64-wiki.com/wiki/BASIC_keyword_abbreviation

  8. Pingback: Amazing Quest – Intractive Fiction

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s