Skip to content

Instantly share code, notes, and snippets.

@tanzeeb
Created May 24, 2010 17:50
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save tanzeeb/412189 to your computer and use it in GitHub Desktop.
RPCFN #9
class Action
attr_accessor :name, :terms, :code
def initialize name
@name = name
@terms = []
end
def setup attribute, *args
case attribute
when :terms
@terms += args.first.downcase.split(', ')
when :code
@code = args.first
end
end
end
module Helper
def ended?
blackboard[:finished]
end
def player_in? room
current_room.name == room
end
def player_has? item
inventory.include? item
end
def player_has_nothing?
inventory.empty?
end
def player_inventory
Item.list @items.values_at(*inventory)
end
def room_has? item
current_room.items.include? item
end
def room_inventory
Item.describe @items.values_at(*current_room.items)
end
def room_description
[ current_room.description, room_inventory ].join("\n")
end
def room_summary
[
current_room.describe,
current_room.visited ? nil : room_inventory
].compact.join("\n")
end
def room_exit? direction
current_room.exit? direction
end
def entrance_room
@rooms.values.select do |room|
eval( room.exit?(:enter).last ).first
end.compact.first
end
def current_room
blackboard[:current_room]
end
def inventory
blackboard[:inventory]
end
def item_name term
Item.find(@items,term)
end
end
class Item
attr_accessor :name, :terms, :description
def initialize name
@name = name
end
def self.list items
items.collect { |item| item.terms.first }.join("\n")
end
def self.describe items
items.collect { |item| item.description }.join("\n")
end
def self.find items, term
return if term.empty? || items.empty?
items.collect do |name,item|
name unless item.terms.grep(/#{term}/i).empty?
end.compact.first
end
def setup attribute, *args
case attribute
when :description
@description = args.compact.join(" ")
when :terms
@terms = args.first.split(', ')
end
end
end
module Loader
TOKENS = [
[/^(\S+)\s?(\S*):/,
'"setup :#{$1.downcase}, \"#{$2}\" do |entity|"'],
[/^(\s*)$/,
'"\n entity\nend\n\n"'],
[/^\s{2}(\S+):\s*(.*)$/,
'"\n entity.setup :#{$1.downcase}, #{$2.empty? ? "nil" : "\"#{$2}\""}"'],
[/^\s*\{\{\{$/,
'".to_s + [ nil"'],
[/^\s*\}\}\}$/,
'"].compact.join(\"\\\n\")"'],
[/^\s{4}(.+)$/,
'", %`#{$1}`"']
]
def load story_path
story_file = File.open(story_path).read.chomp.concat("\n ")
transform story_file
end
def transform story
story.split("\n").collect do |line|
TOKENS.collect do |pattern,replacement|
eval replacement if line =~ pattern
end.compact.first
end.join("")
end
end
#!/usr/bin/env ruby
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
require 'loader'
require 'world'
class Game
include Loader
attr_accessor :world
def initialize(story_path, options={})
@input = options.fetch(:input) { $stdin }
@output = options.fetch(:output) { $stdout }
@world = World.new load(story_path)
end
def play!
start!
execute_one_command! until ended?
end
def start!
@output.puts @world.start
@output.print "> "
end
def execute_one_command!
input = @input.gets
output = @world.execute!(input)
@output.puts output unless output.empty?
@output.print "> " unless ended?
end
def ended?
@world.ended?
end
end
if $PROGRAM_NAME == __FILE__
story_path = ARGV[0]
unless story_path
warn "Usage: #{$PROGRAM_NAME} STORY_FILE"
exit 1
end
game = Game.new(story_path)
game.play!
end
require 'action'
module Player
[:north,:south,:east,:west,:enter,:exit].each do |direction|
define_method :"do_#{direction}" do |*args|
room, guard = room_exit? direction
allowed, message = eval guard
[
message && message.empty? ? nil : message,
allowed ? {
:move_to => room,
:finished => direction == :exit
} : {}
]
end
end
def do_look *args
[ room_description, {} ]
end
def do_take *args
name = item_name args.join(" ")
room_has?(name) ? [ "OK", {:add_inventory => name} ] : [nil,{}]
end
def do_drop *args
name = item_name args.join(" ")
player_has?(name) ? [ "OK", {:remove_inventory => name} ] : [nil,{}]
end
def do_inventory *args
[
player_has_nothing? ? "You're not carrying anything" : player_inventory,
{}
]
end
def do_quit *args
[nil, {:finished => true}]
end
def add_action action
action.terms.each do |term|
self.class.send(:define_method, :"do_#{term}") { eval action.code }
end
end
def add_synonyms attribute, args
args.each do |synonym|
self.class.send(:alias_method, :"do_#{synonym}", :"do_#{attribute}")
end
end
def method_missing method, *args
args.empty? ? [nil, {}] : self.send(:"#{method} #{args.shift}", *args)
end
end
class Room
attr_accessor :name, :title, :description, :exits, :items, :visited
GUARDS = {
:default => '[true,nil]',
:none => '[false,"There is no way to go in that direction"]'
}
def initialize name
@name = name
@exits = {}
@items = []
@visited = false
end
def visit!
@visited = true
end
def describe
@visited ? "You're #{title}." : description
end
def exit? direction
@exits[direction] || [nil, GUARDS[:none]]
end
def setup attribute, *args
case attribute
when :title, :description
send :"#{attribute}=", args.compact.join(" ")
when :exits
args.compact.each do |arg|
direction, location = arg.split(' to ')
room, guard = location.split(' guarded by:')
@exits[direction.to_sym] = [ room, guard || GUARDS[:default] ]
end
when :objects
@items += args.compact
end
end
end
require 'room'
require 'item'
require 'action'
require 'helper'
require 'player'
class World
include Helper
include Player
attr_accessor :rooms, :items, :blackboard
def initialize story
@blackboard = { :inventory => [] }
@rooms = {}
@items = {}
eval story
end
def start
update! :move_to => entrance_room.name
end
def execute! command
method, args = command.split(' ')
message, changes = send("do_#{method}".to_sym, args)
[
message, update!(changes)
].flatten.compact.join("\n")
end
def update! changes
messages = []
blackboard.merge!(changes)
if move_to = blackboard.delete(:move_to)
blackboard[:current_room] = @rooms[move_to]
messages << room_summary
current_room.visit!
end
if item = blackboard.delete(:add_inventory)
blackboard[:inventory] << current_room.items.delete(item)
end
if item = blackboard.delete(:remove_inventory)
blackboard[:current_room].items << inventory.delete(item)
end
messages
end
def setup entity_type, name
case entity_type
when :room
@rooms[name] = yield Room.new(name)
when :object
@items[name] = yield Item.new(name)
when :action
add_action yield(Action.new(name))
when :synonyms
yield self
else
add_synonyms entity_type, name.split(', ')
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment