From: Ruby Quiz Date: 2005-02-26T02:53:40+09:00 Subject: [QUIZ] Phone Typing (#21) The three rules of Ruby Quiz: 1. Please do not post any solutions or spoiler discussion for this quiz until 48 hours have passed from the time on this message. 2. Support Ruby Quiz by submitting ideas as often as you can: http://www.rubyquiz.com/ 3. Enjoy! -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= by Hans Fugal I am amazed whenever I see or hear about the rising generation of keypad punchers. People that can carry on IM conversations with a 12-key phone pad without instantly going mad; it's mind-boggling. As adaptive as the rising generation is, the "Multitap" solution is far from efficient. For example, I learned from Wheel of Fortune that the most common letters are RSTLNE, only one of which (T) is the first tap on one of the keys. Your mission then, should you choose to accept it, is to develop a more efficient algorithm for key entry. User interaction is simply the 12 keys (0-9, * and #) with the letters printed on them as usual[1], and visual feedback of what they have typed so far. Simulating algorithms thought up by other people (e.g. LetterWise[2]) is fair game. [ Editor's Note: Hans sent the following code to get people started with a simple testing inteface for their algorithms: class TapEvent attr_reader :when, :digit def initialize(digit) @digit = digit @when = Time.now end def to_s @digit end end class PhonePad attr_reader :text, :cursor def set_text(text,cursor) @text = text @cursor = cursor end def register(&block) @observers.push block end def initialize @observers = [] @text = '' @cursor = 0 end def notify_observers(digit) if digit =~ /[0-9*#]/ @observers.each { |block| block.call(TapEvent.new(digit)) } end end end class CLIPhonePad < PhonePad def initialize super Thread.new do while not $stdin.eof? do $stdin.readline.split('').each { |c| notify_observers(c) } end end end def set_text(text,cursor) super(text,cursor) puts to_s end def run end def to_s @text.dup.insert(@cursor,'_') end end if $0 == __FILE__ p = CLIPhonePad.new p.register do |ev| puts "#{ev} at #{ev.when}" p.set_text(p.text+ev.digit, p.cursor+1) end Thread.list[0].join end Hans's code is well suited to GUI adaption, if you're so inclined. (Please post to Ruby Talk if you do build a GUI for this. That's not a spoiler.) Here's another version by me, that doesn't require you press return after the digits: begin require "Win32API" def read_char Win32API.new("crtdll", "_getch", [], "L").Call end rescue LoadError def read_char system "stty raw -echo" STDIN.getc ensure system "stty -raw echo" end end loop do char = read_char.chr case char when /^(?:\d|\*|#)$/ ### Replace the following line with your algorithm. ### puts "You entered #{char}." when /q/i break else puts "'#{char}' is not a key on the keypad." end end -JEG2 ] This is an area of serious research for some people[2]; don't fret it if your algorithm isn't perfect or ideal, and don't spend too long on it. ;-) Make your goal to be better than dumb, which is to say be better than "Multitap". 1. http://www.yorku.ca/mack/uist01-f1.jpg 2. http://www.yorku.ca/mack/uist01.html