R
Ruby Quiz
__________________
< class Cow < Duck >
------------------
\ _____
\ \\_-~~ ~~-_
\ /~ ~\
\ _| _ |
\ ___) ~~) ~~~--_ |
\ _-~ ~-_ ___ \ |/
\ / _-~ ~-_ /
\ | / \ |
\ | O) | | |
| | | |
| | (O | |
\ | | |\
(~-_ _-\ / _--_ / \
\__~~~ ~-_ _-~ / ~\
/ ~---~~-_ ~~~ _-~ /| |
_-~ / \ ~~--~ | | |
_-~ | / |
,-~~-_ __--~~ |-~ /
| \ | _-~
\ |--~~
\ | |
~-_ _ | |
~-_ ~~---__ _--~~\ |
~~--__ / |
~~---___ __--~~| |
~~~~~ | |
| |
That's using Ryan Leavengood's solution to display the much celebrated line from
Dave Burt's solution. It's just so Ruby!
It looks like people had plenty of fun solving this quiz, which is great. We're
all about The Fun Factor(tm) here at Ruby Quiz.
Let's get to a solution:
______________________
< Dave Burt's Solution >
----------------------
\
\ .-"""-. _.---..-;
:.) ;"" \/
__..--'\ ;-"""-. ;._
`-.___.^.___.'-.____J__/-._J
Dave is always teaching me great Ruby tricks in his solutions and this week's
trick of choice is right at the top of the program:
class String
def width
inject(0) {|w, line| [w, line.chomp.size].max }
end
def height
to_a.size
end
def top
to_a.first
end
def middle
to_a.values_at(1..-1)
end
def bottom
to_a.last
end
end
# ...
I just know I would have written width() as:
map { |line| line.chomp.size }.max
Dave's is more efficient though. It only ever keeps the highest number, instead
of building an Array of all the lengths. Just one more reason inject() is the
one iterator to rule them all. Thanks for trick #562, Dave!
Obviously we're just dealing with some helpers added to String above, to make
later work easier. Just remember that String.each() traverses lines by default
and inject() and to_a() are defined in terms of each() and there shouldn't be
any surprises here.
# ...
class Duck
def self.say(speech="quack?", *args)
balloon(speech) + body(*args)
end
def self.balloon(speech)
" _#{ '_' * speech.width }_\n" +
if speech.chomp =~ /\n/
"/ %-#{ speech.width }s \\\n" % speech.top.chomp +
speech.middle.map do |line|
"| %-#{ speech.width }s |\n" % line.chomp
end.join +
"\\ %-#{ speech.width }s /\n" % speech.bottom.chomp
else
"< #{ speech.chomp } >\n"
end +
" -#{ '-' * speech.width }-\n"
end
def self.body(thoughts='\\', eyes='cc', tongue=' ')
" #{thoughts}
#{thoughts}
_ ___
/ \\ / \\
\\. |: #{eyes}|
(.|:,---,
(.|: \\( |
(. y-'
\\ _ / #{tongue}
m m
"
end
end
# ...
The say() method should be easy enough to digest. Build a balloon() and a
body(), slap them together, and return them. Beyond that, body() is almost
right out of the quiz. It just builds a String, interpolating the
substitutions.
That leaves balloon(). It's really just building a String too, with a little
more logic thrown in. The if branch handles multiline comments, so the speech
bubble can be rounded at the edges. Otherwise the else branch is used. The
rest is just border drawing.
# ...
class Cow < Duck
def self.body(thoughts='\\', eyes='oo', tongue=' ')
" #{thoughts} ^__^
#{thoughts} (#{eyes})\\_______
(__)\\ )\\/\\
#{tongue} ||----w |
|| ||
"
end
end
class DuckOnWater < Duck
def self.body(thoughts='\\', eyes='º', tongue='>')
" #{thoughts}
` #{tongue[0, 1]}(#{eyes[0, 1]})____,
(` =~~/
~^~^~^~^~`---'^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~
"
end
end
# ...
From there, other animals can just inherit and define a new body() method. Now
we all truly understand Duck Typing! Everything is really just a Duck...
# ...
if $0 == __FILE__
if ARGV.include?("--help")
puts "usage: #$0 animal thoughts eyes tongue <speech\n"
puts "animals: Duck Cow DuckOnWater\n"
puts "e.g.: #$0 DuckOnWater o x ) </etc/fortune\n"
else
animal = Object.const_get(ARGV.shift) rescue Duck
puts animal.say(STDIN.read, *ARGV)
end
end
The main code is trivial. Show usage (if branch), or locate the proper class
with const_get() and call say() (else branch). That's all it really takes to
get the animals talking.
_________________________________________________________________
< Ryan Leavengood's Solution: Where even Chunky Bacon can talk... >
-----------------------------------------------------------------
\
\ __ _.._
.-'__`-._.'.--.'.__.,
/--' '-._.' '-._./
/__.--._.--._.'``-.__/
'._.-'-._.-._.-''-..'
I won't show the whole thing here, but do look it over. It had a nice
collection of animals embedded in the DATA section of the code. They were
controlled through a Zoo class, where even a random animal could be chosen to
escape():
# ...
# Our lovely Zoo, full of many wacky animals
class Zoo
include Singleton
attr_reader :animals
def initialize
@animals = {}
current_key = nil
DATA.each do |line|
if line.chomp =~ /^'(\w*)':$/
current_key = $1
@animals[current_key] = []
elsif current_key
@animals[current_key] << line
end
end
end
# A random animal has escaped!
def escape
choices = @animals.keys
@animals[choices[rand(choices.length)]]
end
end
# ...
Ryan also emulated fortune for Windows, with only a few lines of code:
require 'open-uri'
# ...
# Since I wrote this on Windows I don't have fortune...but the
# internet does!
def get_fortune
open('http://www.coe.neu.edu/cgi-bin/fortune') do |page|
page.read.scan(/<pre>(.*)<\/pre>/m)[0][0].gsub("\t",' ')
end
end
# ...
Fun to read and educational at the same time. You can't beat that folks!
________________
< The Other Guys >
----------------
\
\
_ ___
/ \ / \
\. |: ^-|
(.|:,---,
(.|: \( |
(. y-'
\ _ /
m m
Jacob Quinn Shenker golfed the solution, needing less than 400 strokes. Check
it out.
JB Eriksson supported thought bubbles and sent in a duck on water template.
More good stuff.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Wrap Up: This went just swimmingly!)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
o
` >(o)____,
(` =~~/
^~^~^~^~^~`---'^~^~^~^~^~
I have to offer major thanks to everyone who made is possible for me to fill a
quiz summary with animal drawings! How cool is that?
Tomorrow's quiz takes us from animals to appliances as we build our own DRYer...
< class Cow < Duck >
------------------
\ _____
\ \\_-~~ ~~-_
\ /~ ~\
\ _| _ |
\ ___) ~~) ~~~--_ |
\ _-~ ~-_ ___ \ |/
\ / _-~ ~-_ /
\ | / \ |
\ | O) | | |
| | | |
| | (O | |
\ | | |\
(~-_ _-\ / _--_ / \
\__~~~ ~-_ _-~ / ~\
/ ~---~~-_ ~~~ _-~ /| |
_-~ / \ ~~--~ | | |
_-~ | / |
,-~~-_ __--~~ |-~ /
| \ | _-~
\ |--~~
\ | |
~-_ _ | |
~-_ ~~---__ _--~~\ |
~~--__ / |
~~---___ __--~~| |
~~~~~ | |
| |
That's using Ryan Leavengood's solution to display the much celebrated line from
Dave Burt's solution. It's just so Ruby!
It looks like people had plenty of fun solving this quiz, which is great. We're
all about The Fun Factor(tm) here at Ruby Quiz.
Let's get to a solution:
______________________
< Dave Burt's Solution >
----------------------
\
\ .-"""-. _.---..-;
:.) ;"" \/
__..--'\ ;-"""-. ;._
`-.___.^.___.'-.____J__/-._J
Dave is always teaching me great Ruby tricks in his solutions and this week's
trick of choice is right at the top of the program:
class String
def width
inject(0) {|w, line| [w, line.chomp.size].max }
end
def height
to_a.size
end
def top
to_a.first
end
def middle
to_a.values_at(1..-1)
end
def bottom
to_a.last
end
end
# ...
I just know I would have written width() as:
map { |line| line.chomp.size }.max
Dave's is more efficient though. It only ever keeps the highest number, instead
of building an Array of all the lengths. Just one more reason inject() is the
one iterator to rule them all. Thanks for trick #562, Dave!
Obviously we're just dealing with some helpers added to String above, to make
later work easier. Just remember that String.each() traverses lines by default
and inject() and to_a() are defined in terms of each() and there shouldn't be
any surprises here.
# ...
class Duck
def self.say(speech="quack?", *args)
balloon(speech) + body(*args)
end
def self.balloon(speech)
" _#{ '_' * speech.width }_\n" +
if speech.chomp =~ /\n/
"/ %-#{ speech.width }s \\\n" % speech.top.chomp +
speech.middle.map do |line|
"| %-#{ speech.width }s |\n" % line.chomp
end.join +
"\\ %-#{ speech.width }s /\n" % speech.bottom.chomp
else
"< #{ speech.chomp } >\n"
end +
" -#{ '-' * speech.width }-\n"
end
def self.body(thoughts='\\', eyes='cc', tongue=' ')
" #{thoughts}
#{thoughts}
_ ___
/ \\ / \\
\\. |: #{eyes}|
(.|:,---,
(.|: \\( |
(. y-'
\\ _ / #{tongue}
m m
"
end
end
# ...
The say() method should be easy enough to digest. Build a balloon() and a
body(), slap them together, and return them. Beyond that, body() is almost
right out of the quiz. It just builds a String, interpolating the
substitutions.
That leaves balloon(). It's really just building a String too, with a little
more logic thrown in. The if branch handles multiline comments, so the speech
bubble can be rounded at the edges. Otherwise the else branch is used. The
rest is just border drawing.
# ...
class Cow < Duck
def self.body(thoughts='\\', eyes='oo', tongue=' ')
" #{thoughts} ^__^
#{thoughts} (#{eyes})\\_______
(__)\\ )\\/\\
#{tongue} ||----w |
|| ||
"
end
end
class DuckOnWater < Duck
def self.body(thoughts='\\', eyes='º', tongue='>')
" #{thoughts}
` #{tongue[0, 1]}(#{eyes[0, 1]})____,
(` =~~/
~^~^~^~^~`---'^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~
"
end
end
# ...
From there, other animals can just inherit and define a new body() method. Now
we all truly understand Duck Typing! Everything is really just a Duck...
# ...
if $0 == __FILE__
if ARGV.include?("--help")
puts "usage: #$0 animal thoughts eyes tongue <speech\n"
puts "animals: Duck Cow DuckOnWater\n"
puts "e.g.: #$0 DuckOnWater o x ) </etc/fortune\n"
else
animal = Object.const_get(ARGV.shift) rescue Duck
puts animal.say(STDIN.read, *ARGV)
end
end
The main code is trivial. Show usage (if branch), or locate the proper class
with const_get() and call say() (else branch). That's all it really takes to
get the animals talking.
_________________________________________________________________
< Ryan Leavengood's Solution: Where even Chunky Bacon can talk... >
-----------------------------------------------------------------
\
\ __ _.._
.-'__`-._.'.--.'.__.,
/--' '-._.' '-._./
/__.--._.--._.'``-.__/
'._.-'-._.-._.-''-..'
I won't show the whole thing here, but do look it over. It had a nice
collection of animals embedded in the DATA section of the code. They were
controlled through a Zoo class, where even a random animal could be chosen to
escape():
# ...
# Our lovely Zoo, full of many wacky animals
class Zoo
include Singleton
attr_reader :animals
def initialize
@animals = {}
current_key = nil
DATA.each do |line|
if line.chomp =~ /^'(\w*)':$/
current_key = $1
@animals[current_key] = []
elsif current_key
@animals[current_key] << line
end
end
end
# A random animal has escaped!
def escape
choices = @animals.keys
@animals[choices[rand(choices.length)]]
end
end
# ...
Ryan also emulated fortune for Windows, with only a few lines of code:
require 'open-uri'
# ...
# Since I wrote this on Windows I don't have fortune...but the
# internet does!
def get_fortune
open('http://www.coe.neu.edu/cgi-bin/fortune') do |page|
page.read.scan(/<pre>(.*)<\/pre>/m)[0][0].gsub("\t",' ')
end
end
# ...
Fun to read and educational at the same time. You can't beat that folks!
________________
< The Other Guys >
----------------
\
\
_ ___
/ \ / \
\. |: ^-|
(.|:,---,
(.|: \( |
(. y-'
\ _ /
m m
Jacob Quinn Shenker golfed the solution, needing less than 400 strokes. Check
it out.
JB Eriksson supported thought bubbles and sent in a duck on water template.
More good stuff.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Wrap Up: This went just swimmingly!)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
o
` >(o)____,
(` =~~/
^~^~^~^~^~`---'^~^~^~^~^~
I have to offer major thanks to everyone who made is possible for me to fill a
quiz summary with animal drawings! How cool is that?
Tomorrow's quiz takes us from animals to appliances as we build our own DRYer...