lisp:land-of-lisp:ch5
This commit is contained in:
parent
187b842f0c
commit
6215a98407
63
lisp/land-of-lisp/ch5/wizard_adventure--no-comments.lisp
Normal file
63
lisp/land-of-lisp/ch5/wizard_adventure--no-comments.lisp
Normal 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*)))
|
267
lisp/land-of-lisp/ch5/wizard_adventure.lisp
Normal file
267
lisp/land-of-lisp/ch5/wizard_adventure.lisp
Normal 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*)))
|
Loading…
x
Reference in New Issue
Block a user