From 6215a98407d2d3867ce46e9c5ab49851fac25f14 Mon Sep 17 00:00:00 2001 From: Logen Kain Date: Sat, 24 Jun 2017 21:43:28 -0700 Subject: [PATCH] lisp:land-of-lisp:ch5 --- .../ch5/wizard_adventure--no-comments.lisp | 63 +++++ lisp/land-of-lisp/ch5/wizard_adventure.lisp | 267 ++++++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 lisp/land-of-lisp/ch5/wizard_adventure--no-comments.lisp create mode 100644 lisp/land-of-lisp/ch5/wizard_adventure.lisp diff --git a/lisp/land-of-lisp/ch5/wizard_adventure--no-comments.lisp b/lisp/land-of-lisp/ch5/wizard_adventure--no-comments.lisp new file mode 100644 index 0000000..6808f9f --- /dev/null +++ b/lisp/land-of-lisp/ch5/wizard_adventure--no-comments.lisp @@ -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*))) diff --git a/lisp/land-of-lisp/ch5/wizard_adventure.lisp b/lisp/land-of-lisp/ch5/wizard_adventure.lisp new file mode 100644 index 0000000..21c2363 --- /dev/null +++ b/lisp/land-of-lisp/ch5/wizard_adventure.lisp @@ -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*)))