Skip to content

@Trevoke /rpcfn9.rb
Created

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
All default tests pass. No extra credit.
#!/usr/bin/env ruby
class Game
def initialize(story_path, options={})
@input = options.fetch(:input) { $stdin }
@output = options.fetch(:output) { $stdout }
@inventory = {}
raw_data = open story_path
parse raw_data
end
def play!
start!
execute_one_command! until ended?
end
def start!
set_room '@end_of_road'
end
def execute_one_command!
command = @input.gets.chomp
commands = command.split(" ")
known = we_know_this commands[0]
if known
commands[0] = known
execute commands.join " "
else
unknown_command command
end
end
def ended?
@current_room == @rooms['@depression']
end
private
def open file
raw_data = ""
begin
File.open(file, 'r') { |f| raw_data << f.read }
rescue
throw ArgumentError, "File not found or impossible to open."
end
raw_data
end
def set_room room
@current_room = @rooms[room]
# I hate edge cases! :)
if @current_room[:visited?] && @current_room[:title] == "at end of road again"
@output.puts "You're at end of road again."
else
@output.puts @current_room[:description]
@current_room[:visited?] = true
end
display_items
end
def parse raw
# Necessary first line because the data file has inconsistent
# splits, specifically between action and synonyms.
raw.gsub! /\n\s+\n/, "\n\n"
items = raw.split("\n\n")
rooms = items.find_all { |item| item[0..3] == "Room" }
objects = items.find_all { |item| item[0..5] == "Object"}
commands = items.find_all { |item| item[0..7] == "Synonyms"}
actions = items.find_all { |item| item[0..5] == "Action" }
roomify rooms
objectify objects
commandify commands
actify actions
end
def roomify rooms
@rooms = {}
rooms.each do |room|
room.scan /Room (.*):/
identifier = $1
room.scan /Title: (.*)/
title = $1
room.scan /Description:.(.*)Exits/m
description = $1
description = description.strip.squeeze(' ').gsub("\n", "")
room.scan /Objects:.(.*)/m
objects = $1
# Why yes, I hack for fun and profit. Do you like ugly code?
unless objects.nil?
objects = objects.split
room.scan /Exits:.(.*)Objects/m
else
room.scan /Exits:.(.*)/m
end
exits = {}
exit_string = split_array_with_delimiter_and_clean $1, "\n"
exit_string.each do |string|
# Looks like "east to @building"
# I want ["east", "building"]
temp = split_array_with_delimiter_and_clean string, " to "
exits[temp[0]] = temp[1]
end
@rooms[identifier] = {}
@rooms[identifier][:title] = title
@rooms[identifier][:description] = description
@rooms[identifier][:exits] = exits
@rooms[identifier][:objects] = objects
end
end
def objectify objects
@objects = {}
objects.each do |object|
object.scan /Object (.*):/
identifier = $1
object.scan /Terms: (.*)/
terms = split_array_with_delimiter_and_clean $1, ','
description = object.scan /Description: (.*)/
@objects[identifier] = {}
@objects[identifier][:terms] = terms
@objects[identifier][:description] = description.flatten
end
end
def commandify commands
commands = commands[0][10..-1]
commands.split "\n"
@commands = {}
commands.each do |command|
command_and_synonyms = split_array_with_delimiter_and_clean command, ':'
main = command_and_synonyms[0]
synonyms = split_array_with_delimiter_and_clean command_and_synonyms[1], ','
@commands[main] = main
synonyms.each do |synonym|
@commands[synonym] = main
end
end
end
def actify actions
@actions = {}
actions.each do |action|
action.scan /Action (.*):/
identifier = $1
action.scan /Terms: (.*)/
terms = split_array_with_delimiter_and_clean $1, ','
action.scan /(\{\{\{.*\}\}\})/m
code_to_eval = $1
@actions[identifier] = {}
@actions[identifier][:terms] = terms
@actions[identifier][:code] = code_to_eval
end
end
def we_know_this command
# Alright; this is kinda hackish. I'm lazy. Sue me.
# I mean, don't. Jeez. Where's your sense of humor?
if @commands.keys.include? command
return @commands[command]
else
return false
end
end
def execute command
# I know, I am joining an array just to resplit it.
# Doing this does make the code a little more readable, though.
split = command.split " "
command = split[0]
arg = split[1..-1].join " "
case command
when "west", "east", "north", "south"
try_to_go command
when "look"
@output.puts @current_room[:description]
display_items
when "drop"
drop arg
when "inventory"
display_inventory
when "take"
pick_up arg
end
end
def try_to_go direction
if @current_room[:exits][direction]
set_room @current_room[:exits][direction]
else
@output.puts "There is no way to go in that direction"
end
end
def display_items
return unless @current_room[:objects]
@current_room[:objects].each do |item|
@output.puts @objects[item][:description]
end
end
def display_inventory
if @inventory.empty?
@output.puts "You're not carrying anything."
else
@inventory.each do |item, properties|
@output.puts properties[:terms]#[0]
end
end
end
def pick_up arg
accepted_arguments = []
corresponding_object = @objects.find { |x, i| find_casecmp i[:terms], arg }
unless corresponding_object
@output.puts "This item doesn't exist!!"
return
end
corresponding_object = corresponding_object[0]
unless @current_room[:objects].include? corresponding_object
@output.puts "This item is not there!"
return
end
@inventory[corresponding_object] = @objects[corresponding_object]
@current_room[:objects].delete corresponding_object
@output.puts "OK"
end
def drop item
corresponding_object = @inventory.first { |x, i| find_casecmp i[:terms], item }
unless corresponding_object
@output.puts "You don't have this item!"
return
end
@current_room[:objects] ||= []
@current_room[:objects] << corresponding_object[0]
@inventory.delete corresponding_object[0]
end
def player_has? item
temp = @inventory.first { |x, i| find_casecmp i[:terms], item }
temp ? true : false
end
def player_in? room
@rooms[room] == @current_room
end
def unknown_command command
raise ArgumentError, "You entered \"#{command}\", an unknown command."
end
def split_array_with_delimiter_and_clean array, delimiter
array.split(delimiter).map { |x| x.strip }
end
def find_casecmp array, item
found = false
array.each do |x|
if x.casecmp(item) == 0
found = true
break
end
end
found
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.