To create the final piece of code to help us visualize our game world, we need to describe the objects on the floor at a given location, which a player can pick up and use.
To do so, we first create a list of the objects:
> (defparameter *objects* '(whiskey bucket frog chain))
*OBJECTS*
We can also create a second variable, *object-locations*
, to track the location of each object in the form of an alist:
(defparameter *object-locations* '((whiskey living-room) (bucket living-room) (chain garden) (frog garden)))
Next, we write a function that lists the objects visible from a given location:
(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)))
This objects-at
function declares a new function named at-loc-p
using the labels
command . (Remember that the labels
function allows you to define functions locally.) Since the at-loc-p
function won’t be used elsewhere, we can just declare it directly within objects-at
, hiding it from the rest of the code in our program.
The at-loc-p
function takes the symbol for an object and returns t
or nil
, depending on whether that object exists at the location loc
. It does this by looking up the object in the obj-locs
alist. Then, it uses eq
to see whether the location it finds matches the location in question .
Why did we name this function at-loc-p
? When a function returns nil
or a truth value, it’s a Common Lisp convention to append a p
to the end of that function’s name. For instance, you can check that the number 5 is odd by calling (oddp 5)
. Such true/false functions are called predicates, which is why we use the letter p
.
The remove-if-not
function in the last line of the listing , as you might expect, removes all things from a list for which a passed-in function (in this case, at-loc-p
) doesn’t return true. Essentially, it returns a filtered list of objects consisting of those items for which at-loc-p
is true.
Here’s what object-at
looks like in action:
> (objects-at 'living-room *objects* *object-locations*)
(WHISKEY BUCKET)
Now we can write a function to describe the objects visible at a given location:
(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)))))
In this listing, describe-objects
first creates the describe-obj
function . This function generates a pretty sentence stating that a given object is on the floor, using quasiquoting . The main part of the function 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 .
Let’s try running describe-objects
:
> (describe-objects 'living-room *objects* *object-locations*)
(YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET ON THE FLOOR)
Perfect!
13.58.120.57