Skip to content

Instantly share code, notes, and snippets.

@ashleygwilliams
Last active December 30, 2015 03:29
Show Gist options
  • Save ashleygwilliams/7769697 to your computer and use it in GitHub Desktop.
Save ashleygwilliams/7769697 to your computer and use it in GitHub Desktop.

fork this repo: [email protected]:ashleygwilliams/rpcfn-interactive-fiction.git

The Challenge

In this challenge, you'll implement an interactive fiction game which mimics the first few rooms 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.

The full story file can be found at =data/petite_cave.if=. This format is one I've invented for this challenge. There is no formal specification for it. Your program is only required to be able to parse the provided file petite_cave.if in order to satisfy the challlenge.

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=.

You may find it helpful to define these methods in the class =Game= and then execute the story scripts in the context of your Game object using =#instance_eval=.

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 [[http://www.ruby-doc.org/docs/ProgrammingRuby/taint.html][$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 as well, all
  the better.

Hints

To get an idea of how the finished product should behave, spend a few minutes playing the original Colossal Cave Adventure. If you are on Ubuntu you can install it with:
`apt-get install bsdgames`

Or you can play it online here: [[http://www.ifiction.org/games/play.phpz?cat=&game=1&mode=html]]

There are a number of potential ways to go about parsing the story DSL:
- You could write a basic [[http://en.wikipedia.org/wiki/Recursive_descent_parser][recursive-descent parser]].
- You could use regular expression methods, like [[http://ruby-doc.org/core/classes/String.html#M000812][=String#scan=]]
- You could use Ruby's standard [[http://ruby-doc.org/core/classes/StringScanner.html][StringScanner]] library
- You could use regular expression substitutions to convert the text into
  valid Ruby code, and then [[http://ruby-doc.org/core/classes/Kernel.html#M005922][=#eval()=]] the story definition.

Conclusion

Feel free to [[mailto:[email protected]][contact me]] if something about the challenge is unclear. Good
Luck, and happy hacking!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment