Here's my solution.
I started by translating the tutorial line-for line, then I refactored
it into something that made sense for me.  I tried to keep the generic
game engine separate from the part that defined this specific game.  
I added a few new actions and the ability to type just 'west' instead
of 'walk west'.

To run the game, load the file in IRB.  If you run it from the command
line it performs a automatic walkthrough.

This was fun, and I think I learned a few things about method_missing
and define_method.
-Adam

PS:  Is there a better way to do the following?  Can it be done in a
single line?:

def describe_set set
	s=""
	set.each {|o| s+= "There is a #{o} here. " }
	s
end


SOLUTION:
----
#SPELS game ported from lisp
#Author: Adam Shelly
#Run from IRB to play adventure.  Run from Command line to see walkthrough.
$Interactive = __FILE__ != $0

# # Game Mechanics # #
# This section is independent of any particular map, object list, rules, etc...

class Room
	attr_reader :name, :description, :exits
	def initialize name, desc, *exits
		@name = name
		@description = desc+"\n"
		@exits = exits
	end
end

class ObjectTracker
	def initialize objs
		@obj_loc = objs		
	end
	def objs_in loc
	    @obj_loc.reject{|o,l| l!=loc}.keys
	end
	def move_object obj, from, to
		if @obj_loc[obj] == from
			@obj_loc[obj] = to
		end
	end
	def describe_floor loc, pre, post
	  s = ""
		objs_in(loc).each{|o| s += pre + o + post}
		s
	end
end 	

class Map
	def initialize a
		@map = a
	end
	def get_room location
		@map.find {|r| r.name == location}
	end
	def describe_location location
		get_room(location).description
	end
	def paths location
		get_room(location).exits
	end
	def describe_paths location, pre, mid, post
		s=""
		paths(location).each {|p| s+= pre + p[1]+ mid + p[0] + post}
		s
	end
	def room_in_direction dir, loc
		 if p = paths(loc).find{|p| p[0]==dir}
       p[2]
     else
       nil
    end
	end
end

# # Game Definition # #
#the Map , Ojbect List, and possible actions are here.
module GameDef
	def init_game
		map = Map.new  [ Room.new(living_room, "You are in the living_room
of a wizards house. There is a wizard snoring loudly on the couch.",
[west, door, garden] ,[upstairs, stairway, attic]),
						Room.new(garden, "You are in a beautiful garden. There is a well
in front of you.", [east, door, living_room]),
						Room.new(attic, "You are in the attic of the wizard's house.
There is a giant welding torch in the corner.", [downstairs, stairway,
living_room])]
		objects = ObjectTracker.new({whiskey_bottle => living_room, bucket
=> living_room, frog => garden, chain=>garden})
		location = living_room
		@chain_welded = nil
		@bucket_filled = nil
		@wiz_kisses = -1
		game_action(:weld, bucket, chain, attic, proc { if have? bucket then
@chain_welded = true; "The chain is now securely welded to the bucket"
else "you do not have a bucket" end })
		game_action(:dunk, bucket, well, garden, proc { if @chain_welded
then @bucket_filled = true; "The bucket is now full of water" else
"the water level is too low to reach" end })
		game_action(:splash, bucket, wizzard, living_room, proc {
			if !@bucket_filled then "the bucket has nothing in it"
			elsif have? frog then "the wizzard awakens and sees that you stole
his frog.  He is so upset that he banishes you to the netherworlds -
You lose! the end."
			else "the wizzard awakens from his slumber and greets you warmly. 
He hands you the magic chunky bacon.  You win!  the end."
			end})
		game_multi_action(:wake, {wizzard=>[proc{"If it were only that
easy."},living_room],frog=>[proc{"the frog is already awake"},nil]})
		game_multi_action(:kiss, {frog => [proc{"Sorry, no prince"},nil],
												whiskey_bottle => [proc{"You warmly greet your old friend"},nil],
												wizzard => [proc{results = ["You timidly kiss the
wizzard's cheek. Nothing happens","You give the wizzard a tiny peck on
the lips. His eylid twitches.","You plant a big wet kiss right on the
wizzard's mouth.  His beard feels scratchy."]
																		  results[@wiz_kisses+=1]||"Enough Already!"},living_room]})
    game_directions([north, south, west, east, upstairs, downstairs])
    #any new game def must return these 3 things.
    return map,objects,location
	end
	private :init_game
end


#Here is the game logic.  Any additional the output strings are here
(not in the game mechanics objects)
class Game
	include GameDef
	def initialize
    @map, @objects, @location = init_game
	end
	def look
		@map.describe_location(@location)+
		@map.describe_paths(@location, "There is a "," going ", " from here.\n")+
		@objects.describe_floor(@location, "You see an ", " on the floor.\n")
	end
	def walk direction
		if  new_room = @map.room_in_direction(direction, @location)
			@location=new_room; look
		elsif direction
			"You Can't Go That Way"
		end
	end
	def pickup object
		if @objects.move_object(object, @location, :body)
			"You are now carrying the #{object}"
		else
			"You cannot get that."
		end
	end
	alias :get :pickup
	def inventory
		s = "You are carrying: "
		@objects.objs_in(:body).each{|o| s += "#{o} "}
		s
	end
	def have? obj
		inventory.scan(obj)!=[]
	end
	def game_action command, subj, obj, place,block
		p = proc {|subject, object|
			if (!have? subj)
				"You don't have a #{subject}"
			elsif (@location == place and subject == subj and object == obj)
				block.call
			else
				"I can't #{command} like that"
			end
		}
		self.class.send(:define_method, command , &p)
	end
	def game_multi_action command, hash
		p = proc {|subject|
			action = hash[subject]
			if  ! (@objects.objs_in(@location).include?(subject) ||
have?(subject) || (action && action[1]==@location))
				"You don't see any #{subject} to #{command} here"
			elsif action
				action[0].call
			else
				"You can't #{command} that."
			end
		}
		self.class.send(:define_method, command , &p)
	end
  def game_directions dirs
    dirs.each{|d|
      #p = proc{ walk d}
      self.class.send(:define_method, d, proc{walk d})
      }
  end
	private :game_action, :game_multi_action, :game_directions
	def help
		($g.methods - Object.methods).join ' '
	end
end

# The $words array is filled with symbols that become valid tokens for the game.
#  Only symbols that are referenced _before_ the game object is
created are added.
# This allows us to detect invalid words after the game has started.
$words = []


def method_missing symbol, *args
	#p "MM:#{symbol} [#{args}]"
	
	#if it's a game method, send it.
	if $g and ($g.methods.include? symbol.to_s )
		puts "> #{symbol} #{args.join ' '}" if !$Interactive
		puts s = $g.send(symbol,*args)
	#if the game hasn't started, define it as a valid token.
	elsif !$g
		$words << symbol
		symbol.to_s
	#if the game has started, see if it is a token
	elsif $words.include? symbol
		symbol.to_s
	#otherwise, it is an unknown command.
	else
		puts "> #{symbol} #{args.join ' '}" if !$Interactive
		puts "Sorry, I don't understand #{symbol}"
	end
end

$g = Game.new
look

if __FILE__ == $0
help
north
wake wizzard
get frog
get whiskey_bottle
kiss frog
kiss wizzard
kiss wizzard
kiss wizzard
kiss wizzard
west
kiss frog
pickup chain
inventory
have? frog
welt chain, bucket
weld chain, bucket
walk east
pickup bucket
kiss whiskey_bottle
upstairs
weld bucket, chain
downstairs
splash bucket, wizzard
west
dunk bucket, well
east
inventory
splash bucket, wizzard
end