Friday, June 22, 2012

Land of Lisp chapter 5

Chapter 5 is exciting. We get to actually write a bit of an engine (if that's not glorifying it too much) for a text adventure game. Okay, it's 3 rooms and 4 objects and no interactions to speak of yet. That's not the good part. More content can be added easily enough by extending the global variables. But I'm getting ahead of myself.

One language syntax that was introduced is quasiquoting. Basically this is a syntax that allows you to embed bits of code inside a data list. This is familiar to me since I work with a scripting language that does this by default. In Lisp

notice the backtick instead of single quote. That indicates quasiquoting 'mode' (not sure mode is the right term in the same way it applies to data/code mode but whatever). The comma indicates the beginning of a code block and Lisp automatically returns to data mode once the list closes. In this case I could have left the parentheses around foo off, but I left them in to make the end of code mode more explicit.

In the builtin scripting language for UC4

So this might have been a concept unique to Lisp(s) once upon a time, but no longer.

The second bit of syntax is the use of #' to indicate that the following symbol is a function name. This is necessary because Common Lisp maintains separate namespaces for variables and functions. Apparently this means it is called a Lisp-2 language. This in comparison to Scheme which, as a Lisp-1 language, maintains a single namespace containing both functions and variables. So you couldn't have a variable named car in Scheme (car is a standard builtin function in Lisp, I assume it is in Scheme as well?).

There is also some more discussion of functional programming but that isn't why I'm working through this so I'm not going to spend more time on it.

This chapter also introduces the conceptual architecture for the Wizard's Adventure Game and constructs a good bit of basic functionality. We view the game as a mathematical graph; each room is a node and the paths between them are edges in the graph. Why this particularly matters enough to bleed ink on I don't rightly know but I'm not a game designer (yet?). It's as good as any visualization and probably better than some.Using the parlance of nodes and edges we proceed to build the tables for rooms and the paths between those rooms as global variables.

I'll link to the full (and developing, I'm not going to keep versions for each chapter) code for the game at the bottom. In the code above we have a pair of associative array... er, lists describing the nodes and the vital information for the paths. We also build a series of functions to pull out the specific room information. These functions (which I'm not going to bother copying in here, download the source if you're curious) use the assoc function to retrieve a specific element from the associative list.

which we can then pull the description from. We also created a list of objects and a list defining where those objects are located.

We also used the append function to join lists together. This is conceptually equivalent to string concatenation except it applies to lists. We used append in conjunction with apply basically to flatten out nested lists created by mapcar. mapcar is what's called a higher-order function because it takes another function as a parameter and does something with it. In this case, the function is executed against each item in a list

and the returns are consed together into a unified list.

The apply function is also higher-order and it takes a list of inputs and feeds them one piece at a time to the function parameter. This is a convenient way to remove a layer of nesting from a list, but I don't entirely grok the logic. I'll need to feel this one out some more.

In this case we apply-ed the append function to the nested lists created by mapcar to get a flat list of symbols that reads like text. One thing I forgot to discuss at the top is the deliberate use of symbols instead of strings. This isn't imposed by the language, but the language makes it much easier to work with lists of symbols instead of strings, apparently. We could certainly use the string data type for this, but then functions like cadr would work differently on them and would require more code.

We then proceed to take all these things and build a rudimentary user interface. Functions are defined for look, walk, and pickup. Of these, only pickup is really interesting; it uses push to add a variable to the front of the object locations associative list with the value of body. Since assoc only returns the first item in the list that matches this effectively hides the original entry and the item now is only visible in inventory (also a function built at the end of the chapter).

Here's the link to the code as it currently exists. Bear in mind that this will be built upon further in later chapters and I'm not going to bother keeping multiple copies. If you want chapter-by-chapter code buy the book.

No comments: