Adventures in Clojure: Saving the Day

Adventures in Clojure: Saving the Day 2014-07-19T10:04:32-05:00

See the previous installment here.

It’s a big part of the philosophy of Clojure programming that you keep mutable state (data that can change) separate from immutable state (data that cannot change). There are a number of good reasons for doing this; the one that the Clojure books usually trot out is that it makes supporting concurrency easier because you don’t need to synchronize reads and writes to data that never changes.

Concurrency isn’t all that important to a single-user text adventure game; but keeping the mutable and immutable state separate is still a good idea. To see why, let’s enumerate what’s in each category:

Immutable state:

  • The world map
  • The descriptions of each room and each thing.
  • The initial location of the player and the various other things.

Mutable state:

  • The current location of the player and other things.
  • What the player is currently carrying.
  • Flags indicating which puzzles have been solved.
  • A score, if any.

In short, the mutable state is all of the stuff you need to save when you save the game. The immutable state is everything else.

In a language like Java, all of these entities would be built up out of Java objects, each of which has its own internal state. And while it’s possible to keep the immutable state and the mutable state separate in a Java program, that’s not the usual way. And so when you want to save a game, you have to save a network of interconnected objects in such a way that you can rebuild that network later. This is doable—Java has support for it built-in to the language—but it’s tricky.

In Clojure, by comparison, it’s straightforward: put all of the mutable state in a handful of data structures (e.g., nested maps). On save, write them out; on restore, read them back in.

Consequently, my game breaks the mutable state up into three pieces:

  • The player map, which contains the player’s location, health, and so forth.
  • The inventory map, which contains a (possibly empty) set of “things” for each container in the game. Containers include the player himself (because he can carry things), rooms (because they contain things) and also things like desks, cupboards, boxes, and so forth.
  • The fact-base, which records facts about what has gone on in the game so far: rooms visited, puzzles solved, doors unlocked.

These data structures can grow arbitrarily complicated, and it will still be possible to write them out with one function call each, and to read them back in again. Saving and restoring the game is thus a simple affair.

My adventure game hasn’t progressed to the point where saving the game is necessary; but the capability for it needs to be built into the program architecture from the beginning, or you’re going to have problems when the time comes. I intend to be ready. In the meantime, I’ll have more to say about all of these things in future installments.


Browse Our Archives

Follow Us!