lisp:land-of-lisp:ch5

This commit is contained in:
Logen Kain 2017-06-24 21:43:28 -07:00
parent 187b842f0c
commit 6215a98407
2 changed files with 330 additions and 0 deletions

View File

@ -0,0 +1,63 @@
(defparameter *nodes* '((living-room (you are in a the living-room.
a wizard is snoring loudly on the couch.))
(garden (you are in a beautiful garden.
there is a well in front of you.))
(attic (you are in the attic.
there is a giant welding torch in the corner.))))
(defun describe-location (location nodes)
(cadr (assoc location nodes)))
(defparameter *edges* '((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder))))
(defun describe-path (edge)
`(there is a ,(caddr edge) going ,(cadr edge) from here.))
(defun describe-paths (location edges)
(apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
(defparameter *objects* '(whiskey bucket frog chain))
(defparameter *object-locations* '((whiskey living-room)
(bucket living-room)
(chain garden)
(frog garden)))
(defun objects-at (loc objs obj-locs)
(labels ((at-loc-p (obj)
(eq (cadr (assoc obj obj-locs)) loc)))
(remove-if-not #'at-loc-p objs)))
(defun describe-objects (loc objs obj-loc)
(labels ((describe-obj (obj)
`(you see a ,obj on the floor.)))
(apply #'append (mapcar #'describe-obj (objects-at loc objs obj-loc)))))
(defparameter *location* 'living-room)
(defun look ()
(append (describe-location *location* *nodes*)
(describe-paths *location* *edges*)
(describe-objects *location* *objects* *object-locations*)))
(defun walk (direction)
(let ((next (find direction
(cdr (assoc *location* *edges*))
:key #'cadr)))
(if next
(progn (setf *location* (car next))
(look))
'(you cannot go that way.))))
(defun pickup (object)
(cond ((member object
(objects-at *location* *objects* *object-locations*))
(push (list object 'body) *object-locations*)
`(you are now carring the ,object))
(t '(you cannot get that.))))
(defun inventory ()
(cons 'items- (objects-at 'body *objects* *object-locations*)))

View File

@ -0,0 +1,267 @@
;;Wizard's Adventure
;Our game world.
;A house with a garden
;In the garden there is a well, chain?, and frog
;In the house there is a living room with a ladder that goes to an attic
;So, we have three different locations. Living room, attic, and garden
;; Look at Land Of Lisp book for graphics
;;We need to be able to:
;;Look around, walk to different locations
;;Pick up objects, perform actions on objects that have been picked up
;;We will be able to see three kinds of things from any loaction
;; Basic scenery
;; One or mor paths (edges) to other locations
;; Objects that can be picked up and manipulated
;; This will contain a list and discription of the locations in our game:
(defparameter *nodes* '((living-room (you are in a the living-room.
a wizard is snoring loudly on the couch.))
(garden (you are in a beautiful garden.
there is a well in front of you.))
(attic (you are in the attic.
there is a giant welding torch in the corner.))))
;; Now we will want to create a function to deal with associating what we need
;; for example:
;; (assoc 'garden *nodes*)
;; >> (GARDEN (YOU ARE IN A BEAUTIFUL GARDEN. THERE IS A WELL IN FRONT OF YOU.))
;; The car of the cdr seems appropraite to me here, but let's see.
(defun describe-location (location nodes)
(cadr (assoc location nodes)))
;yup, same idea I had, except less verbose
;; Time to create the edges to define our paths
(defparameter *edges* '((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder))))
;; Now to actually use this...
(defun describe-path (edge)
`(there is a ,(caddr edge) going ,(cadr edge) from here.))
;; Note the backtick this time. This works like the single quote, but
;; if we put a comma before something like (caddr edge) it actually computes it
;; Instead of simply looking at it as a symbol
;; This is called quasiquoting which allows us to create chunks of data
;; That have small chunks of data in them
;; Okay, the above was just a simple test to play with backquoting, so...
;; here's for real
;; damn, just relaized the above has "path" and the below is "paths"
;; Guess they are both used
(defun describe-paths (location edges)
(apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
;; Wow, that's a few new things
;; Other languages would use some sort of for loop to run through the edges
;; then cram descriptions of each path together using a temp variable
;; The inner part basically does this (cdr (assoc 'living-room *edges*))
;; >> ((GARDEN WEST DOOR) (ATTIC UPSTAIRS LADDER))
;; Next we convert the edges to descriptions.
;; (mapcar #'describe-path '((GARDEN WEST DOOR) (ATTIC UPSTAIRS LADDER)))
;; >> ((THERE IS A DOOR GOING WEST FROM HERE.)
;; (THERE IS A LADDER GOING UPSTAIRS FROM HERE.))
;; In effect, with mapcar here, we could add in any number of edges.
;; So first we grab the edges available from our location
;; Then we loop over them to grab descriptions
;; mapcar takes a another function and applies it to every member of a list
;; Example:
;; (mapcar #'sqrt '(1 2 3 4 5))
;; >> (1 1.4142135 1.7320508 2 2.236068)
;; Or perhaps a less dumb example that uses the 1+ function that simply
;; adds 1 to a number (provided by me)
;; (mapcar #'1+ '(1 2 3 4 5))
;; >> (2 3 4 5 6)
;; yet another example, provided by book
;; (mapcar #'car '((foo bar) (baz qux)))
;; >> (foo baz)
;; Back to the function
;; (append) takes multiple lists and combines them into a single list
;; So instead of:
;; ((THERE IS A DOOR GOING WEST FROM HERE.)
;; (THERE IS A LADDER GOING UPSTAIRS FROM HERE))
;; We get:
;; (THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE)
;; Book example:
;;(append '(mary had) '(a) '(little lamb))
;; >> (MARY HAD A LITTLE LAMB)
;; The (append) function requires it to be given seperate lists,
;; Not one big list, so we need (apply)
;; (apply #'append '((mary had) (a) (little lamb)))
;; >> (MARY HAD A LITTLE LAMB)
;; don't quite get (apply), but it seems to allow us
;; to run (append) on each list and return the result
;; examples for study and clarity
;; We run (append) on a list of lists
;; (append '((Hi) (bye)))
;; >> ((HI) (BYE))
;; We do the same thing, but add (apply)
;; (apply #'append '((Hi) (bye)))
;; >> (HI BYE)
;; So after all that, the function takes two parameters
;; The players current location,
;; as well as an alist of edges/paths for the game map
;; Since assoc returns both the key and the value
;; from the alist (association list, dictionary?)
;; we take the (cdr) of that so we only grab the value
;; Next we take (mapcar) to map the describe-path function
;; against each edge that we find
;; Finally, we concat the lists for describing all paths into one list
;; Now it's time for objects
(defparameter *objects* '(whiskey bucket frog chain))
;; and their locations
(defparameter *object-locations* '((whiskey living-room)
(bucket living-room)
(chain garden)
(frog garden)))
;; Now for the fun function to list visible objects
(defun objects-at (loc objs obj-locs)
(labels ((at-loc-p (obj)
(eq (cadr (assoc obj obj-locs)) loc)))
(remove-if-not #'at-loc-p objs)))
;; We create (at-loc-p) here because it's not needed anywhere else
;; We put a -p on it because that's lisp convention for functions
;; That return t or nil
;; (remove-if-not) removes all things from a list for which a passed-in function
;; (at-loc-p in this case) does not return true
;; Essentially, it returns a filtered list of objects consisting
;; of thsoe itmes form which at-loc-p is true
;; So really, our function takes our location, Our objects, and our object locations
;; It checks the objects against the object locations to see if they exist
;; in our current location
;; Now that we have a function that can find objects in a location,
;; we need to describe them
(defun describe-objects (loc objs obj-loc)
(labels ((describe-obj (obj)
`(you see a ,obj on the floor.)))
(apply #'append (mapcar #'describe-obj (objects-at loc objs obj-loc)))))
;; Not sure I'd think of anything like this on my own, but this
;; seems to describe every object it can find.
;; First we create a function (describe-obj) that is just there to
;; have a way to print out objects found on the floor
;; the main part consists of calling (objects-at) to find the objects
;; at the current location, mapping describe-obj across this list
;; of objects and finally appending the descriptions
;; into a single list
;; So... (objects-at) returns a list of found objects in the area
;; (describe-obj) is then mapped to each element of that list.
;; Or in more procedual words, the list that resulted from
;; running (objects-at) is passed element by element into the
;; (describe-obj) private function
;; Time to make our (look) function
;; And finally create a global variable state to define our current location
(defparameter *location* 'living-room)
(defun look ()
(append (describe-location *location* *nodes*)
(describe-paths *location* *edges*)
(describe-objects *location* *objects* *object-locations*)))
;; Time to walk
(defun walk (direction)
(let ((next (find direction
(cdr (assoc *location* *edges*))
:key #'cadr)))
(if next
(progn (setf *location* (car next))
(look))
'(you cannot go that way.))))
;; Note that the above two functions are not functional.
;; They deal in global variables
;; First we look up the available walking paths in *edges* table
;; using the current location
;; This is used by (find) to locate the path marked with the appropriate
;; direction
;; Note that find searches a list for and item, then returns that found item
;; The direction (such as west, upstairs, etc) will be the cadr of each path
;; so we tell find to match the direction against the cadr of all paths
;; in the list
;; We do this by passing a keyword parameter to find.
;;example:
;; (find 'y '((5 x) (3 y) (7 z)) :key #'cadr)
;; >> (3 y)
;; It finds the first item in a list that has the symbol y in the cadr location
;; the (cadr) location, in this case,
;; means it checks the second item of each list
;; A keyword parameter has two parts
;; The first is the name (:key in this case)
;; The second is the value (#'cadr in this case)
;; Anywya, once we have the correct path we then store the result
;; in the variable next
;; Then the if expression check to see if next has a value
;; if it does, then it adjusts the player's position
;; calls look, and retrieves the description for the new location
;; otherwise, it does something like "invalid direction" instead of a new look
;;Whelp, time to pick up those objects
(defun pickup (object)
(cond ((member object
(objects-at *location* *objects* *object-locations*))
(push (list object 'body) *object-locations*)
`(you are now carring the ,object))
(t '(you cannot get that.))))
;; I almost get it, we check to see if the object we try to pickup
;; Is part of the visible objects at our location
;; If so we push it out of the list, and say we are carrying it
;; Otherwise, we can't get that
;; First we check to see if the object is on the floor in the curent location
;; We use (objects-at) to generate a list of objects in the current location
;; we then use member to check if the obj we specified is in that list
;; if it is, we use the push command to push command to push a new item onto
;; the *object-locations* list consisting of the time and its new location
;; The new location will just be "body" for the player's body
;; The push command simply adds a new item to the front of a list
;; variable's list
;; Oh, time for an inventory
(defun inventory ()
(cons 'items- (objects-at 'body *objects* *object-locations*)))