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:
3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion.
···
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#!/usr/bin/env ruby -w
#
# Stephen Waits <steve@waits.net>
#
class AlarmKeypad
# init a keypad, with length of security code, and the code's stop digits
def initialize(code_length = 4, stop_digits = [1,2,3])
# remember the length of the security code
@code_length = code_length
# and which digits cause a code to be checked
@stop_digits = stop_digits
# and reset our data structures to 0
clear
end
# reset keypad to initial state
def clear
# an array of each code and how many times it's been entered
@codes = Array.new(10**@code_length,0)
# last N+1 keypad button presses
@key_history = []
# total number of keypad button presses
@key_presses = 0
end
# press a single digit
def press(digit)
# add digit to key history
@key_history.shift while @key_history.size > @code_length
@key_history << digit
@key_presses += 1
# see if we just tested a code
if @stop_digits.include?(@key_history.last) and
@key_history.length > @code_length
@codes[@key_history[0,@code_length].join.to_i] += 1
end
end
# find out if every code had been tested
def fully_tested?
not @codes.include?(0)
end
# find out if an individual code has been tested
# NOTE: an actual keypad obviously doesn't offer this functionality;
# but, it's useful and convenient (and might save duplication)
def tested?(code)
@codes[code] > 0
end
# output a summary
def summarize
tested = @codes.select { |c| c > 0 }.size
tested_multiple = @codes.select { |c| c > 1 }.size
puts "Search space exhausted." if fully_tested?
puts "Tested #{tested} of #{@codes.size} codes " +
"in #{@key_presses} keystrokes."
puts "#{tested_multiple} codes were tested more than once."
end
end
if $0 == __FILE__
# a random button presser, 3 digit codes
a = AlarmKeypad.new(3)
a.press( rand(10) ) until a.fully_tested?
a.summarize
# sequential code entry, 4 digit codes
a = AlarmKeypad.new(4)
("0000".."9999").each do |c|
next if a.tested?(c.to_i)
c.split(//).each { |d| a.press(d.to_i) }
a.press(rand(3)+1)
end
a.summarize
# sequential code entry, 5 digit codes, only [1] as a stop digit
a = AlarmKeypad.new(5,[1])
("00000".."99999").each do |c|
next if a.tested?(c.to_i)
c.split(//).each { |d| a.press(d.to_i) }
a.press(1)
end
a.summarize
end