Logo Adventure for C64 Terrapin Logo
I love Terrapin Logo ! I got away with not having to write a parser, by simply using the Logo top-level read-eval-print loop as the parser, and defining Logo words like LOOK , N , S , E , W , TAKE , EXAMINE , etc. So it’s really easy to cheat by examining and modifying the state of the world, but that helps you learn Logo!
The ADVENTURE word starts the game by switching to text mode ( NODRAW ), printing an introduction, setting up all the data structures by calling INIT , and printing a description of the current room by calling LOOK . First it sets [ADVENTURE] to be the startup function, so it starts the game automatically when you load it.
PR [WELCOME TO LOGO ADVENTURE]
PR [WRITTEN BY DON HOPKINS]
PR [TYPE HELP FOR HELP]
MAKE "STARTUP [ADVENTURE]
A good place to start our walk through the code would be the HELP word, which just prints out a bunch of helpful tips and wishes you luck, which should give you an idea of what’s to come:
PR [TO MOVE, TYPE]
PR [N, S, E, W]
PR [FOR NORTH, SOUTH, EAST, WEST]
PR [TYPE LOOK TO SEE WHAT ROOM YOU]
PR [ARE IN. YOU CAN GET AND DROP ITEMS.]
PR [INVENT SHOWS YOUR INVENTORY.]
PR [THE WORD "IT" MEANS THE LAST THING YOU]
PR [REFERRED TO.]
PR [THERE ARE SOME SPECIAL THINGS YOU CAN]
PR [DO, LIKE SAYING EXAMINE SOMETHING.]
PR [TYPE SCORE TO SEE YOUR SCORE, AND]
PR [DONE TO QUIT.]
PR [GOOD LUCK!]
The INIT word sets up our model of the Adventure universe, which is very simple: some rooms, some items, and a player.
MAKE "ITEMS [[1 0 SWORD] [1 0 HATCHET] [1 0 SHIELD] [2 100 GOLD] [2 100 DIAMOND] [2 50 AMULET] [3 0 SCREWDRIVER] [4 0 MACHINE] [0 100 WAND] [5 200 CROWN]]
MAKE "RMOVES [[0 2 3 0] [0 0 4 1] [1 4 0 0] [2 0 0 3] [0 0 0 0]]
MAKE "RNAMES [[YOU ARE IN THE WEAPON SHOP.] [THIS IS THE VAULT.] [THIS ROOM IS THE TOOLSHED.] [THIS IS THE ALTAR ROOM.] [YOU ARE IN A SECRET INCANTING ROOM.]]
MAKE "RNUM 1
INITITEMS :ITEMS 1
Rooms are numbered from 1 (to match 1-based Logo list indexes). The information about each room is stored in lists, indexed by the room number.
The RNAMES list is simply a list of room names, which are lists of words describing the room. The RMOVES list contains a list of four numbers per room. Those numbers tell the room number you can move to in each direction (in the order [North, East, South, West]), or 0 if you can’t move in that direction.
It’s possible to define one-way or crooked doors this way, but if you want a normal two-way door, each room should point back and forth to one another in opposite directions. But if you really want to make a twisty little maze of passages, then knock yourself out! But please give your players a lot of items to drop behind so they can map our your mazes.
The ITEMS list contains a bunch of lists describing the room number, score (a number) and name of each item (a string). The first thing in the list is the number of the room containing the item, or -1 if the item is in the player’s inventory, or 0 for nowhere. The second thing in the list is the item score: how many points you get for having that item in your inventory. The third thing in the list is the item name.
The most important but simplest part of the model is the player’s room number, RNUM . The N , S , E and W words move from room to room by changing the player’s room number by calling MOVEDIR (and waving the wand transports you to the secret incanting room by magically changing RNUM ).
Theoretically, if the room number were 0, the player would be nowhere at all, and if it were -1, the player would be carrying themselves in their own inventory! But don’t worry: that could never happen, unless you used magic or hacked into the system, which is pretty easy since the system is a top-level Logo interpreter.
Finally INIT calls INITITEMS passing it the list of ITEMS as :I and the number 1 as :F . This recursively defines a bunch of magical functions referring to each of the items by name, which also remember the last item you referred to as the pronoun IT .
Those magical functions accomplish two things: We can refer to items by their name without typing additional quotes (by defining a function named for each item), and it automatically remembers the last item we referred to in the global variable IT , by calling the word SETIT .
TO INITITEMS :I :F
IF :I =  STOP
TEST :F = 1
IFT DEFINE LAST FIRST :I LPUT LPUT WORD "" LAST FIRST :I [OP SETIT] []
IFF DEFINE LAST FIRST :I 
INITITEMS BF :I :F
Now we’re doing some real fun list processing and function definition! The :I parameter is the list of items to recurse over, and the :F parameter controls whether we should initialize the items (1) or clean them up (0). The expression FIRST :I is the current item, and LAST FIRST :I is the name of the current item.
Finally INITITEMS recurses on itself to process the rest of the list, BF :I, which means (don’t giggle) “but first”, that is: all elements of :I but the first.
To explain what the magical functions do, I will give an example: When the item’s name is “SWORD”, it will define a function named SWORD with no parameters, whose body is [ OP SETIT “SWORD] , which outputs the result of calling SETIT with the parameter “SWORD”. The SETIT word sets the global variable IT to “SWORD” and returns “SWORD”. So we can say GET SWORD , then say DROP IT .
It’s helpful to know the definition of LPUT , which takes two parameters “thing” and “list”, and returns a copy of “list” with “thing” appended to the end. And also WORD , which takes two things and concatenates them into a word, which is just used in this case to convert a string to a word (like a LISP symbol). And DEFINE takes two arguments: the name of a function, and a list. The first element of the list is the list of parameters (an empty list in our case). The subsequent elements are list expressions to evaluate ( [ OP SETIT “SWORD] in our case).
Let’s unpack those expressions in the IFT clause by inserting some indentation and parenthesis and comments to make it look like LISP (since Logo is essentially LISP without parens, whose parser necessarily knows the number of parameters each function takes), and linking to the Logo function documentation.
(LAST (FIRST :I)) ; function name to define is the item name
(LPUT ; function body is a list of lists
(LPUT ; expression is a list like [OP SETIT "SWORD]
(WORD ; concatenate the two parameters to get word "SWORD
"" ; this just converts the string to a word
(LAST (FIRST :I))) ; the word is the item name
[OP SETIT]) ; output result of SETIT with item name appended
[]))) ; empty function with zero parameters to append to
Ugh! But if you think that’s hard to understand, take a look at some of the FORTH code I was writing at the time !
Finally, if the :F parameter to INITITEMS is not 1, then it executes the IFF clause which removes the function definition, by going DEFINE LAST FIRST :I  . This doesn’t actually ever get called, apparently, but there you go.
The final sneaky trick to integrate the game with the Logo top level interpreter is the CMD word, which prints a “COMMAND” prompt, and jumps to the Logo TOPLEVEL to read and interpret a command from the player. Each Adventure word can finally call CMD at the end to show a prompt to the player.
My coffee is wearing off now so I’m going to take a break explaining things for now, but you can read the rest of the program here:
TO N MOVEDIR 1 END TO E MOVEDIR 2 END
TO S MOVEDIR 3 END
TO W MOVEDIR 4 END
TO MOVEDIR :DIR MAKE "TRYMOVE ITEM :DIR ITEM :RNUM :RMOVES TEST :TRYMOVE = 0 IFT PR [YOU CAN'T GO THAT WAY.] IFT CMD PR "OK. MAKE "RNUM :TRYMOVE LOOK END
TO INVENT PITEMS - 1 CMD END TO LOOK PR ITEM :RNUM :RNAMES PITEMS :RNUM CMD END
TO PITEMS :LOC PITEMS2 :LOC :ITEMS END
TO PITEMS2 :LOC :I IF :I =  STOP IF FIRST FIRST :I = :LOC PRINT LAST FIRST :I PITEMS2 :LOC BF :I END
TO IT OP :IT END TO EVERYTHING OP "EVERYTHING END
TO GET :ITEM TEST :ITEM = "EVERYTHING IFT GETALLITEMS :ITEMS IF IHAVE? :ITEM ( PR [YOU ALREADY HAVE] PERIOD :ITEM ) CMD IF NOT HERE? :ITEM SEENO :ITEM PUTITEM :ITEM ( - 1 ) PR SE :ITEM "TAKEN. CMD END
TO TAKE :THING GET :THING END
TO GETALL GETALLITEMS :ITEMS END
TO GETALLITEMS :I IF :I =  CMD TEST :RNUM = ITEMLOC LAST FIRST :I IFT PUTITEM LAST FIRST :I ( - 1 ) IFT PR SE LAST FIRST :I "TAKEN. GETALLITEMS BF :I END
TO DROP :ITEM TEST :ITEM = "EVERYTHING IFT DROPALLITEMS :ITEMS IF NOT IHAVE? :ITEM PR SE [YOU'RE NOT CARRYING THE] WORD :ITEM "! CMD PUTITEM :ITEM :RNUM PR SE :ITEM "DROPPED. CMD END
TO DROPALL DROPALLITEMS :ITEMS END TO DROPALLITEMS :I IF :I =  CMD TEST ITEMLOC LAST FIRST :I = ( - 1 ) IFT PUTITEM LAST FIRST :I :RNUM IFT PR SE LAST FIRST :I "DROPPED. DROPALLITEMS BF :I END
TO HERE? :ITEM LOCAL "LOC MAKE "LOC ITEMLOC :ITEM OP ANYOF - 1 = :LOC :RNUM = :LOC END
TO ITEMLOC :ITEM OP ITEMLOC2 :ITEM :ITEMS END
TO ITEMLOC2 :ITEM :I IF :I =  OP 0 IF LAST FIRST :I = :ITEM OP FIRST FIRST :I OP ITEMLOC2 :ITEM BF :I END TO PUTITEM :ITEM :LOC MAKE "ITEMS PUTITEM2 :ITEM :LOC :ITEMS END
TO PUTITEM2 :ITEM :LOC :LIST IF :LIST =  OP  IF LAST FIRST :LIST = :ITEM OP FPUT FPUT :LOC BF FIRST :LIST BF :LIST OP FPUT FIRST :LIST PUTITEM2 :ITEM :LOC BF :LIST END
TO SEENO :I PR SE [I SEE NO] SE :I "HERE! CMD END TO IHAVE? :ITEM OP - 1 = ITEMLOC :ITEM END TO PERIOD :WORD OP WORD :WORD ". END
TO WAVE :ITEM IF NOT IHAVE? :ITEM PR SE [YOU ARE HOLDING NO] PERIOD :ITEM CMD IF NOT :ITEM = "WAND NOTHING IF ALLOF NOT :RNUM = 4 NOT :RNUM = 5 PR [NOTHING HAPPENS.] CMD PR [POOF! THE SCENE CHANGES!] IF :RNUM = 4 MAKE "RNUM 5 ELSE MAKE "RNUM 4 LOOK END TO FIX :ITEM IF IHAVE? :ITEM PR [YOU HAVE TO DROP IT TO FIX IT!] CMD IF NOT HERE? :ITEM SEENO :ITEM IF NOT :ITEM = "MACHINE PR [YOU CAN'T FIX THAT!] CMD IF NOT ITEMLOC "WAND = 0 PR [THE MACHINE IS NOT BROKEN!] CMD IF NOT IHAVE? "SCREWDRIVER PR [YOU DON'T HAVE THE PROPPER TOOLS TO] PR [FIX IT] CMD PR [YOU FIX THE MACHINE WITH YOUR TRUSTY] PR [SCREWDRIVER. UPON BEING FIXED, THE] PR [MACHINE STARTS UP AND PRODUCES A WAND!] PUTITEM "WAND 4 CMD END TO EXAMINE :ITEM IF NOT HERE? :ITEM ( PR [I SEE NO] :ITEM [HERE!] ) CMD IF :ITEM = "WAND PR [IT BEARS A FADED INSCRIPTION:] PR ["WAVE ME AND YOU'LL BE GLAD."] CMD IF NOT :ITEM = "MACHINE PR SE [I SEE NOTHING SPECIAL ABOUT THE] PERIOD :ITEM CMD IF NOT 0 = ITEMLOC "WAND PR [IT SEEMS TO BEAR THE MARKS OF A HASTY] PR [REPAIR JOB.] CMD PR [IT IS BROKEN! YOU COULD FIX IT WITH] PR [THE RIGHT TOOL.] CMD END TO SCORE PR ( SE [YOUR SCORE IS] SCORE2 :ITEMS [POINTS.] ) CMD END
TO SCORE2 :LIST IF :LIST =  OP 0 IF NOT FIRST FIRST :LIST = - 1 OP SCORE2 BF :LIST OP ( ITEM 2 FIRST :LIST ) + SCORE2 BF :LIST END
TO NOTHING PR [NOTHING HAPPENS.] CMD END TO GAME OP "ADVSAVE END
TO DONE IF NOT :RNUM = 5 NOTHING LOCAL "SCORE MAKE "SCORE SCORE2 :ITEMS IF :SCORE = 0 NOTHING PR SE [YOUR SCORE IS] :SCORE IF :SCORE = 550 PR [PERFECT!] ELSE PRINT [THERE'S MORE TREASURE, THOUGH.] DONE END
LogoFox make it easy for anyone to create a logo. Simply like or unlike the logos you see and LogoFox will adapt accordingly. See in real time how your logo will look like in a website or phone.
MVP Logo Generator
phabricator - Open software engineering platform and fun adventure game
README.md DQN Adventure: from Zero to State of the Art
GitHub is where people build software. More than 28 million people use GitHub to discover, fork, and contribute to over 85 million projects.
How I created and illustrated my conference talk for Southeast Ruby 2018 (Video coming soon) Reducing Enum...
Six months ago we started a Play Framework project using Kotlin, and we wanted to share with you this extraordinary adventure. We faced exciting challenges we'd like to share with you using this blog post, from the project configuration with Kot...
Contents: Hunt for the data race Fine-tuning global initialization First attempt at using tools &mut self vs. &...