# 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 }