public
Last active

RPCFN #9

  • Download Gist
action.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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
helper.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
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
item.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
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
loader.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
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
play.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#!/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
player.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
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
room.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
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
world.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.