From: Sean O'Halpin Date: 2005-10-06T10:41:44+09:00 Subject: [Solution] Lisp Game #49 I wasn't going to finish this but somehow I couldn't stop myself.... :) It's not tested & debugged but I had to post it before I spent way too much time on it. Now if I can integrate it with IFMapper... ---- CODE --- #$DEBUG_GAME = true def dbg(*args) puts *args if $DEBUG_GAME end class Object def in?(container) container.include?(self) end end module Attributes def has(*names) self.class_eval { names.each do |name| # with args = setter # without = getter define_method(name) {|*args| if args.size > 0 instance_variable_set("@#{name}", *args) else instance_variable_get("@#{name}") end } # also define traditional setter= define_method("#{name}=") {|*args| instance_variable_set("@#{name}", *args) } end } end end module Directions def directions(*directions) directions.each do |name| self.class.class_eval { define_method(name) { go(name) } } end end end class GameObject extend Attributes has :identifier, :name, :description def initialize(identifier, &block) @identifier = identifier instance_eval &block end end class Thing < GameObject has :location, :portable def initialize(identifier, &block) # put defaults before super - they will be overridden in block (if at all) # e.g. light, dark, etc. @portable = true super end end class Room < GameObject has :exits end class Game include Directions attr_accessor :name, :rooms, :location, :things def initialize(name, &block) @name = name @rooms = {} @things = {} # read game definition instance_eval &block end def room(identifier, &block) @rooms[symbol(identifier)] = Room.new(symbol(identifier), &block) end def thing(identifier, &block) @things[symbol(identifier)] = Thing.new(symbol(identifier), &block) end def match_thing(*identifiers) thing = nil identifiers.each do |identifier| if thing = @things[identifier] break end end thing end def symbol(s) s.to_s.to_sym end def start(room_identifier) @location = @rooms[room_identifier] end def display_list(things) if things.size > 2 things[0..-2].join(", ") + " and " + things[-1] else things.join(" and ") end end def describe_path(direction, path) "a #{path} going #{direction}" end def describe_exits(location) "There is " + display_list(location.exits.map {|direction, (path, destination)| describe_path(direction, path) }) + "." end def describe_floor(location) things = @things.select{|key, thing| thing.location == location.identifier} if things.size > 0 "You can also see " + display_list(things.map{|key, thing| "a #{thing.name}"}) + "." else "" end end def main_loop print "> " while input = gets input.chomp! case input when 'exit', 'quit' break when 'help' puts "Sorry pal! You're on your own here :)" else begin #puts "#{input}" tokens = input.split cmd = tokens[0] rest = tokens[1..-1].map{|x| x.to_sym}.reject{|x| x.in? [:the, :to, :on, :in, :a, :with]} instance_eval { send(cmd, *rest) } rescue Exception => e dbg e.to_s puts "Eh?" end end print "> " end end # commands taking symbols def in_location(location_id, thing_id) @things[thing_id].location == location_id end def is_here(thing_id) in_location(@location.identifier, thing_id) end def player_has(thing_id) in_location(:player, thing_id) end def player_in(location_id) @location.identifier == location_id end def move_to(location_id, thing_id) @things[thing_id].location = location_id end def move_here(thing_id) move_to(@location.identifier, thing_id) end def destroy(thing_id) move_to(:nowhere, thing_id) end def pick_up(thing_id) move_to(:player, thing_id) end # verbs def look puts location.description puts describe_exits(location) puts describe_floor(location) end def go(direction) dest = @location.exits[direction] if dest @location = @rooms[dest[1]] look else puts "You can't move in that direction" end end alias :walk :go def inventory carried = @things.select{|key, thing| player_has(thing.identifier)} puts "You are carrying " + if carried.size > 0 "a #{display_list(carried.map{|key, thing| thing.name})}" else "nothing" end end alias :i :inventory def get(*args) if thing = match_thing(*args) if is_here(thing.identifier) if thing.portable pick_up(thing.identifier) puts "OK - you picked up the #{thing.name}" else puts "You try and you try but you just can't pick up the #{thing.name}" end else puts "You can't see a #{thing.name} here" end else puts "Get what?" end end alias :take :get def drop(*args) if thing = match_thing(*args) if player_has(thing.identifier) move_here(thing.identifier) puts "OK - you dropped the #{thing.name}" else "You're not carrying a #{thing.name}" end else puts "Drop what?" end end def examine(*args) if obj = match_thing(*args) if is_here(obj.identifier) puts "Looks like #{obj.description}" else puts "You can't see that here" end else puts "Don't know what you're talking about" end end alias :exam :examine end def game(name, &block) puts "Welcome to #{name}\n\n" g = Game.new(name, &block) g.look g.main_loop end # Game definition game "Ruby Adventure" do directions :east, :west, :north, :south, :upstairs, :downstairs alias :up :upstairs alias :down :downstairs # rooms room :living_room do name 'Living Room' description "You are in the living-room of a wizard's house. There is a wizard snoring loudly on the couch." exits :west => [:door, :garden], :upstairs => [:stairway, :attic] end room :garden do name 'Garden' description "You are in a beautiful garden. There is a well in front of you." exits :east => [:door, :living_room] end room :attic do name "Attic" description "You are in the attic of the wizard's house. There is a giant welding torch in the corner." exits :downstairs => [:stairway, :living_room] end start :living_room # things thing :wizard do name "wizard" description "a typical wizard - pointy hat, wand, unkempt beard, smelly feet" location :living_room portable false end thing :self do name "Yourself" description "your same ol' ugly self" portable false end thing :bottle do name 'whiskey bottle' description 'a half-empty whiskey bottle' location :living_room end thing :bucket do name 'bucket' description 'a rusty bucket' location :living_room end thing :welded_bucket do name "bucket" description "a bucket with a chain welded to it" location :nowhere end thing :filled_bucket do name "bucket" description "a bucket full of water with a chain welded to it" location :nowhere end # chain thing :chain do name 'chain' description 'a sturdy iron chain' location :garden end thing :frog do name 'frog' description 'a wide-mouthed frog' location :garden end # verbs def splash(*args) thing, *rest = *args if thing == :wizard and player_in(:living_room) and player_has(:filled_bucket) if player_has(:frog) puts "The wizard awakens and sees that you stole his frog. He is so upset he banishes you to the netherworlds - you lose! The end." break else puts "The wizard awakens from his slumber and greets you warmly. He hands you the magic low-carb donut - you win! The end." break end elsif thing == :bucket puts "The bucket has nothing in it" else puts "With what?" end end def attach(*args) subject, object, *rest = *args if [subject, object].in?([[:chain, :bucket], [:bucket, :chain]]) and player_in(:attic) and player_has(:bucket) and player_has(:chain) destroy(:bucket) destroy(:chain) pick_up(:welded_bucket) puts "The chain is now securely welded to the bucket." else puts "Interesting..." end end alias :weld :attach def fill(*args) thing, *rest = *args if thing == :bucket and player_in(:garden) and player_has(:welded_bucket) destroy(:welded_bucket) pick_up(:filled_bucket) puts "You have filled the bucket with water" elsif thing == :bucket if player_in(:garden) puts "You can't reach the water in the well. You'll need something to lower the bucket down" elsif player_has(:bucket) puts "Fill it with what?" else puts "You don't have the bucket!" end else puts "Hmmmm...." end end alias :dunk :fill end __END__ get bucket go west get chain east upstairs weld chain to bucket down west fill bucket east splash wizard --- END CODE --- Regards, Sean