# Solution to Ruby Quiz #138 by Gavin Kistner
SEED = "LOOK AND SAY"
module Enumerable
def item_counts
inject( Hash.new(0) ){ |counts, item|
counts[ item ] += 1
counts
}
end
end
class String
def look_and_say
counts = upcase.scan( /[A-Z]/ ).item_counts
counts.keys.sort.map{ |letter|
"#{counts[letter].to_english.upcase} #{letter}"
}.join( ' ' )
end
end
# Code courtesy of Glenn Parker in Ruby Quiz #25
class Integer
Ones = %w[ zero one two three four five six seven eight nine ]
Teen = %w[ ten eleven twelve thirteen fourteen fifteen sixteen
seventeen eighteen nineteen ]
Tens = %w[ zero ten twenty thirty forty fifty sixty seventy eighty
ninety ]
Mega = %w[ none thousand million billion trillion quadrillion
quintillion sextillion septillion octillion ]
def to_english
places = to_s.split(//).collect {|s| s.to_i}.reverse
name = []
((places.length + 2) / 3).times do |p|
strings = Integer.trio(places[p * 3, 3])
name.push(Mega[p]) if strings.length > 0 and p > 0
name += strings
end
name.push(Ones[0]) unless name.length > 0
name.reverse.join(" ")
end
private
def Integer.trio(places)
strings = []
if places[1] == 1
strings.push(Teen[places[0]])
elsif places[1] and places[1] > 0
strings.push(places[0] == 0 ? Tens[places[1]] :
"#{Tens[places[1]]}-#{Ones[places[0]]}")
elsif places[0] > 0
strings.push(Ones[places[0]])
end
if places[2] and places[2] > 0
strings.push("hundred", Ones[places[2]])
end
strings
end
end
str = SEED
strs_seen = {}
0.upto( 9999 ){ |i|
puts "%4d. %s" % [ i, str ]
if last_seen_on = strs_seen[ str ]
print "Cycle from #{i-1} back to #{last_seen_on}"
puts " (#{i - last_seen_on} lines in cycle)"
break
else
strs_seen[ str ] = i
end
str = str.look_and_say
}