From: Ruby Quiz Date: 2005-10-27T23:01:46+09:00 Subject: [SUMMARY] Ducksay (#52) __________________ < 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 ----------------------------------------------------------------- \ \ __ _.._ .-'__`-._.'.--.'.__., /--' '-._.' '-._./ /__.--._.--._.'``-.__/ '._.-'-._.-._.-''-..' 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>/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...