Skip to content

Latest commit

 

History

History
188 lines (169 loc) · 8.33 KB

README.org

File metadata and controls

188 lines (169 loc) · 8.33 KB

Challenge: Interactive Fiction

Introduction

Interactive fiction (IF) games, also known as text adventures, are computer games in which you must rely on your imagination to provide the visuals. They represent one of the earliest forms of computer entertainment. Originating in the 1970s, they reached their zenith in the 1980s, with classic Infocom games such as Zork and The Hitchiker’s Guide to the Galaxy. At their best, interactive fiction games offer rich interaction, engrossing storylines, and phenomenal writing. While most gamers have moved on to more graphically rich games, there remains a strong community of interactive fiction writers and players to this day.

I’ve always loved interactive fiction. Like most people who got into programming young, as a teenager I first taught myself to program in order to write my own games. The very first program I ever wrote was a tiny text adventure game, written in the REXX programming language.

While I’ve since moved on to writing other kinds of software, I still think writing interactive fiction engines is a great way to get a feel for a new language. Unlike many canned programming challenges which primarily test your knowledge of pure computer science concepts, writing a text adventure game exercises many skills which translate directly to typical real-world applications.

In order to write a successful IF engine, you must deal with challenges such as:

  • Modeling the interactions of real-world objects (such as rooms, items, and players) in software.
  • Interpreting a Domain-Specific Language (DSL) in order to load games.
  • Dealing with unpredictable user input.

Writing an IF engine is a fun way to learn how to tackle these problems in a new language, and the skills you come away with can be applied directly to a wide array of applications.

The Challenge

In this challenge, you’ll implement an interactive fiction game which mimics a tiny subset of the grandaddy of all text adventures, Collossal Cave. In order to succeed, your program will need to read in a “story” in the form of a simple DSL, interpret user commands, and track the player’s progress and inventory. If you get all that working without too much trouble, I’ve also included an “extra credit” challenge to implement basic “puzzle” functionality in the game.

Here’s a sample interaction with a finished implementation of the challenge:

    $ bin/play.rb data/petite_cave.if
    You are standing at the end of a road before a small brick building. Around
    you is a forest. A small stream flows out of the building and down a gully.
    > north
    There is no way to go in that direction.
    > east
    You are inside a building, a well house for a large spring.
    > There are some keys on the ground here.
    > There is food here.
    > There is a shiny brass lamp nearby.
    > There is a bottle of water here.
    > get keys
    OK
    > get food
    OK
    > get lantern
    OK
    > get water
    OK
    > inventory
    You are currently holding the following:
    > Set of keys
    > Tasty food
    > Brass lantern
    > Small bottle
    > west
    You're at end of road again.
    > s
    You are in a valley in the forest beside a stream tumbling along a rocky bed.
    > s
    At your feet all the water of the stream splashes into a 2-inch slit in the
    rock. Downstream the str eambed is bare rock.
    > s
    You are in a 20-foot depression floored with bare dirt. Set into the dirt is
    a strong steel grate mo unted in concrete. A dry streambed leads into the
    depression.
    > unlock grate
    The grate is now unlocked
    > enter
    You are in a small chamber beneath a 3x3 steel grate to the surface. A low
    crawl over cobbles leads inward to the west.

Here’s a sample of the story DSL which defines the adventure:

Room @end_of_road:
  Title: at end of road again
  Description:
    You are standing at the end of a road before a small brick building.
    Around you is a forest.  A small stream flows out of the building and
    down a gully.
  Exits:
    east to @building
    enter to @building
    south to @valley

Room @building:
  Title: inside building
  Description:
    You are inside a building, a well house for a large spring.
  Exits:
    west to @end_of_road
    exit to @end_of_road
  Objects:
    $keys
    $lamp
    $food
    $water_bottle

:

Object $keys:
  Terms: Set of keys, keys
  Description: There are some keys on the ground here.

Object $lamp:
  Terms: Brass lantern, brass lamp, lamp, lantern
  Description: There is a shiny brass lamp nearby.

Getting Started

Here are steps for getting started on your entry:

  1. Clone the Github project avdi/rpcfn-interactive-fiction
    git clone git://github.com/avdi/rpcfn-interactive-fiction.git
        
  2. Install Cucumber, if you don’t have it already
    gem install cucumber
        
  3. Run the acceptance tests by running Rake:
    cd rpcfn-interactive-fiction
    rake
        

    You should see failure messages. That’s because the implementation hasn’t been written yet! Making the tests pass is up to you.

  4. I’ve provided a skeleton bin/play/rb to start you off. Edit that file to implement your interactive fiction engine.
  5. Drive your development by running rake periodically to see what’s left to implement.
  6. Make sure to manually test your implementation by running it standalone:
    ruby bin/play.rb data/petite_cave.if
        

Extra Credit

If you want an extra challenge, run

rake extra_credit

and write code to make those tests pass as well. In order to make the extra credit features work, your engine will have to evaluate arbitrary scripts from the story file in order to implement guard conditions and custom actions.

The code executed by the guard/action part of the story file expects a simple API to be made available by your implementation:

  • #blackboard should return a hash. The blackboard is a place for story scripts to stow arbitrary story-specific values.
  • #player_in?(room_id) should return whether the player is in the specified room.
  • #player_has?(object_id) should return whether the player has the specified item in their inventory.
  • Exit guard clauses return an Array of [ALLOW, MESSAGE]. ALLOW is a boolean indicating whether the player’s attempt to exit the room was allowed. MESSAGE must be shown to the user if provided.
  • Action scripts return an Array of [MESSAGE, BLACKBOARD]. Message must be shown to the user if non-nil. The values in BLACKBOARD should be merged into the Hash returned by #blackboard.

The reason story scripts do not directly set values in the blackboard is so that it is possible to implement story script execution inside of =$SAFE= jails. For extra, extra credit, write your implementation so that all story scripts are executed under $SAFE level 4.

Requirements

  • You must use only Ruby standard libraries in your implementation.
  • Your entry must at minimum pass the tests in features/petite_cave.feature
  • Your entry must be capable of running as a standalone executable. It must accept a single argument, the path of the story file. E.g.:
    ruby bin/play.rb data/petite_cave.if
        
  • Your entry must run under Ruby 1.8.7. If it runs under 1.9, all the better.

Hints

There are a number of potential ways to go about parsing the story DSL:

  • You could write a basic recursive-descent parser.
  • You could use regular expression methods, like =String#scan=
  • You could use Ruby’s standard StringScanner library
  • You could use regular expression substitutions to convert the text into valid Ruby code, and then =#eval()= the story definition.