Lab 13: Interactive Fiction

Objectives

So this is the last lab. It's not graded and there's no due date. If you're reading this, you're likely finished with everything else and are either bored or just looking for something to do while waiting to get graded.

The goal, then, is to have a bit of fun, and hopefully learn something about using files and YAML.

Let's get to it!

Interactive Fiction

Before the advent of blazing fast, 256MB, 1.21 jigawatt video cards, adventure games didn't have fancy graphics and pre-rendered cut scenes. Us old farts would sit down in front of monochromatic monitors and play so-called "text adventure" games, where movement and actions were carried out via text commands, and lush textual descriptions were provided for the benefit of active imaginations.

Turns out some folks still find text adventures interesting, though they tend to be referred to as "interactive fiction" now. Wikipedia (who else?) has a good article on them.

The goal of this lab is to implement a very basic interface to an interactive fiction (IF) game. The game will support navigation between any number of "rooms", with the objective being to find the "exit" (though you can easily change this).

Instead of writing a program that implements a "hardcoded" game, however, you're going to write a program that loads the description for a set of rooms from a file (formatted in YAML), and therefore allows different games to be played depending on what files are loaded.

Our file format

Here are the contents of "hauntedhouse.yaml" -- a file describing a fairly small IF "map":

  start:
    name: the start room
    description: an elegant chamber with a huge crystal chandelier
    exits:
      south: [zombie room,  a greenish hue]
      east:  [room of mice, a grayish writhing mass]

  zombie room:
    name: a room of zombies
    description: a group of freaky looking zombies heading your way.
    exits:
      north: [start, a bright passageway]

  room of mice:
    name: a room of mice
    description: at least a dozen huge, hairy, black rats!
    exits:
      west: [start, a bright passageway]
      east: [exit,  a blinding light]

  exit:
    name: the exit!

The idea is that each first-level entry in the file (identified by keys in a hash named, in this example, "start", "zombie room", "room of mice", "exit") corresponds to a room, and each room itself consists of a hash of items that comprise the room's name, description, and exits. The exits are also represented as a hash of direction names (east, north, west, south) to arrays; the first element of the array being the key of the room in that direction, and the second element a description to be shown to the player.

Note that YAML allows keys, values, and array elements to be written without quotes, though after loading they can be accessed as strings.

Gameplay

The challenge is to go from the file format described above to a functioning game --- a session with our (fairly spartan) game is shown below:

$ ruby if.rb

What game file to load?
hauntedhouse.yaml

You are now in the start room.
Looking around, you see an elegant chamber with a huge crystal chandelier

There are 2 exits:
  - To the south there lies a greenish hue
  - To the east there lies a grayish writhing mass

Where would you like to head?
south

You are now in a room of zombies.
Looking around, you see a group of freaky looking zombies heading your way.

There are 1 exits:
  - To the north there lies a bright passageway

Where would you like to head?
north

You are now in the start room.
Looking around, you see an elegant chamber with a huge crystal chandelier

There are 2 exits:
  - To the south there lies a greenish hue
  - To the east there lies a grayish writhing mass

Where would you like to head?
east

You are now in a room of mice.
Looking around, you see at least a dozen huge, hairy, black rats!

There are 2 exits:
  - To the west there lies a bright passageway
  - To the east there lies a blinding light

Where would you like to head?
east
You've reached the exit... congratulations!

Pretty nifty, huh?

Step 1: Load the file

Clearly, the first thing that needs to be done is to load the contents of the file into a form that is easily used within a Ruby program. Because we're using YAML as a file format, this is remarkably easy to do! The following bit of code we discussed in lecture loads the hash shown above into the rooms variable:

  rooms = {}
  File.open("hauntedhouse.yaml") do |f|
    rooms = YAML::load(f)
  end

Note that at this point,

You can also iterate over items -- e.g., you can get at all the exits from the "room of mice" by doing:

  rooms['room of mice']['exits'].each do |dir, name_desc|
      # dir => "west", "east"
      # name_desc[0] => "start", "exit"
      # name_desc[1] => "a bright passageway", "a blinding light"
  end

After you do a git pull in your repository, you'll find a data file ("hauntedhouse.yaml", described above) and some sample code in "if.rb".

Step 2: Take it away!

You've got your data loaded -- it's up to you now to implement a working game interface! Feel free to jazz it up a bit; our example above is a little boring. You can use random strings, for instance, as "lead-ins" to the various descriptions that get printed out.

Much more interesting than that would be to expand on the contents of the IF data file itself (and use that content in your program). Here are some ideas:

There are a lot of possibilities, really. If you come up with a particularly interesting map or game engine, we'd love for you to send it in for us to play through!

Enjoy.