Skip to content

Instantly share code, notes, and snippets.

@gisikw
Created July 17, 2010 19:22
Show Gist options
  • Save gisikw/479767 to your computer and use it in GitHub Desktop.
Save gisikw/479767 to your computer and use it in GitHub Desktop.
game = TextAdventureGame.new("My First Text Adventure")
hallway = Location.new("A dark and quiet hallway")
kitchen = Location.new("An abandoned kitchen")
hall = Location.new("A banquet hall...strangely deserted")
game.rooms << hallway
game.rooms << kitchen
game.rooms << hall
hallway.north = kitchen
kitchen.south = hallway
kitchen.north = hall
hall.south = kitchen
game = TextAdventureGame.new("My First Text Adventure") # Okay, so we have a game called "My First Text Adventure"
hallway = Location.new("A dark and quiet hallway") # There's a dark and quiet hallway
kitchen = Location.new("An abandoned kitchen") # There's an abandoned kitchen
hall = Location.new("A banquet hall...strangely deserted") # There's a banquet hall which is strangely deserted
game.rooms << hallway # The hallway is one of the rooms in the game
game.rooms << kitchen # The kitchen is one of the rooms in the game
game.rooms << hall # The hall is one of the rooms in the game
hallway.north = kitchen # The hallway exits north to the kitchen
kitchen.south = hallway # The kitchen exits south to the hallway
kitchen.north = hall # The kitchen exits north to the hall
hall.south = kitchen # The hall exits south to the kitchen
World.new("My First Text Adventure") do
location "hallway" do
description "A dark and quiet hallway"
north "kitchen"
end
location "kitchen" do
description "An abandoned kitchen"
north "hall"
south "hallway"
end
location "hall" do
description "A banquet hall...strangely deserted"
south "kitchen"
end
start "hallway"
end
World.new("My First Text Adventure") do # Okay, we have a text adventure game
# called "My First Text Adventure"
location "hallway" do # It contains a hallway
description "A dark and quiet hallway" # which is dark and quiet
north "kitchen" # and exits north to the kitchen
end
location "kitchen" do # It also contains a kitchen
description "An abandoned kitchen" # which is abandoned
north "hall" # which leads north to the hall
south "hallway" # or south back to the hallway
end
location "hall" do # It also contains a hall
description "A banquet hall...strangely deserted" # which is large and strangely deserted
south "kitchen" # that leads south back to the kitchen
end
start "hallway" # We begin in the hallway
end
world "My First Text Adventure"
location "hallway"
description "A dark and quiet hallway"
north "kitchen"
location "kitchen"
description "An abandoned kitchen"
north "hall"
south "hallway"
location "hall"
description "A banquet hall...strangely deserted"
south "kitchen"
start "hallway"
class DSLObject < BasicObject
def method_missing(name,*args)
return *args.flatten.unshift(name.to_s)
end
def const_missing(name)
eval(name.to_s_downcase)
end
end
# @dsl_object.instance_eval("Set course fer the Hoth system")
# => ["Set","course","fer","the","Hoth","system"]
class DSLObject
def method_missing(name,*args)
return name.to_s
end
end
# @dsl_object.green => "green"
class DSLObject
def color(name)
case name
when "green" : "#00FF00"
when "blue" : "#0000FF"
when "red" : "#FF0000"
end
end
def method_missing(name,*args)
return name.to_s
end
end
# @dsl_object.color green => NameError: undefined local variable or method `green' for #<Object:0x1001c8288>
class DSLObject
def color(*args)
case args[0]
when "green" then "#00FF00"
when "blue" then "#0000FF"
when "red" then "#FF0000"
end
end
def method_missing(name,*args)
return *args.flatten.unshift(name.to_s)
end
end
# @dsl_object.instance_eval("background color green") => ["background","#00FF00"]
class DSLObject
def color(*args)
case args[0]
when "green" then "#00FF00"
when "blue" then "#0000FF"
when "red" then "#FF0000"
end
end
def the(*args)
return *args
end
def method_missing(name,*args)
return *args.flatten.unshift(name.to_s)
end
end
# @dsl_object.instance_eval("the monitor background color green") => ["monitor","background","#00FF00"]
class DSLObject
def color(*args)
case args[0]
when "green" : "#00FF00"
when "blue" : "#0000FF"
when "red" : "#FF0000"
end
end
def the(*args)
return *args
end
def turn(*args)
ComputerHardware.send(args[0]).send("#{args[1]}=#{args[2]}")
end
def method_missing(name,*args)
return *args.flatten.unshift(name.to_s)
end
end
require 'world'
require 'location'
require 'player'
require 'runner'
class Game
def self.run(world)
runner = Runner.new(world)
puts "Welcome to the text adventure game!"
print "> "
until (input = gets.chomp) == "exit"
runner.__execute(input.downcase)
print "> "
end
puts "Thanks for playing"
end
end
@dsl_object.instance_eval do
# Everything in this block
# will treat @dsl_object
# as self
color green
end
# This is equivalent
@dsl_object.instance_eval("color green")
class Location
attr_accessor :name, :description, :exits
def initialize(name,&block)
@name = name
@exits = {}
instance_eval &block
end
def description(prose)
@description = prose
end
["north","south","east","west"].each do |direction|
class_eval <<-END
def #{direction}(location)
@exits["#{direction}"] = location
end
END
end
end
class Player
attr_accessor :location
end
class Runner < BasicObject
attr_accessor :__world, :__handled
def initialize(world)
@__world = world
end
def __handle!
@__handled = true
end
def __execute(string)
@__handled = false
instance_eval(string)
__puts "I don't understand" unless @__handled
end
def __puts(message)
$stdout.puts(message)
end
def method_missing(name,*args)
return *args.flatten.unshift(name.to_s)
end
def look(*args)
__puts @__world.player.location.instance_eval(:@description)
__handle!
end
end

Ruby DSLs - Building a Text Adventure Game!

Eau Claire Ruby User Group

August 5, 2010

About the Speaker

Intridea

Kevin W. Gisi

Intridea

What's on the Agenda?

Outline

Language

Language

What is a DSL?

Domain-Specific Language

Cucumber is a DSL

Cucumber

Real Languages?

Parse Tree example

  • Rule-based
  • Pattern-matching
  • Very dull to read

DSL Evaluation (in Ruby)

Example: turn the monitor background color green

Ruby execution: turn (the (monitor (background (color (green) ) ) ) )

DSL Evaluation

turn the monitor background color green

=code dsl_part_1.rb

DSL Evaluation

turn the monitor background color "green"

=code dsl_part_2.rb

Problem!

How do we execute the code inside this object?

@dsl_object.color green

"green" is evaluated within the current scope, so unless it's a variable, it doesn't exist

Object Oriented Programming

OO

Bad Object Orientation

Bad OO

Better Object Orientation

Context OO

Static Dispatch

Static

Dynamic Dispatch

Dynamic

Context-Switching

Context-Switching

Ruby has Dynamic Dispatch!

Mail

Ruby uses message passing

@dsl_object.send("color green")

Oh yeah, some guy named Irb says "color green"

Wrong Again!

Question

Hmmm, do I have a method named "color green"? Nope.

Hmmm, do my ancestors have "color green"? Nope.

Ooh, I know! self.method_missing("color green")

@dsl_object.send("color green") => "color green"

Changing Scope

Two ways:

=code instance_eval.rb

DSL Evaluation

turn the monitor background "#00FF00"

=code dsl_part_3.rb

DSL Evaluation

turn the monitor ["background","#00FF00"]

No additional code needed here!

DSL Evaluation

turn the ["monitor","background","#00FF00"]

=code dsl_part_4.rb

DSL Evaluation

turn ["monitor","background","#00FF00"]

=code dsl_part_5.rb

Ambiguous DSL

Example: turn the beat around

=> turn ["beat","around"]

Where's the third argument?

Brittle DSL

Example: Set course for the Hoth system

Any issues?

(Hint: You CAN type this into IRB without error)

Issues

Set course for the Hoth system

  • Set, Hoth => Constant undefined
  • for => reserved word
  • system => already defined (inherited from Object)

Blank Slate

We can fix most of these!

=code blank_slate.rb

Text Adventure Games

Zork

DSLs Meet Games

Two DSLs:

  • Represent the environment

  • Interpret user input

Enterpriseyness: Documentation

Entity Relationship Diagram

Back to Language

Language

Environments are Hard!

=code bad_text_adventure.rb

English Translation

=code bad_text_adventure_english.rb

Text Adventure with Blocks!

=code better_text_adventure.rb

English Translation

=code better_text_adventure_english.rb

Even Cooler (Pythony)

=code better_text_adventure_indented.rb

Interpretation

Interpretation

World

=code world.rb

Location

=code location.rb

Player

=code player.rb

Handling User Input

  • Preprocess the input

  • Provide a context to execute

  • Manipulate the environment

Preprocess the input

=code game.rb

Provide a context to execute

and manipulate the environment

=code runner.rb

Before We Demo

Thanks!

require 'game'
class World
attr_accessor :name, :locations, :player
def initialize(name,&block)
@name = name
@locations = {}
@player = Player.new
instance_eval &block
end
def location(name,&block)
@locations[name] = Location.new(name,&block)
end
def start(location)
@player.location = @locations[location]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment