From: Paolo Negri Date: 2006-10-30T03:56:02+09:00 Subject: Re: [QUIZ] Fuzzy Time (#99) Hi all Here's is my script. It was fun working at it, just please no more time based quiz when there's the switching of daylight time saving :P The scripts defines two class one to manage how to move within a given range and one for actual FuzzyTime implementation. At the end of the email there's a really ugly extra file you may want to use to play with the FuzzyTime class. Paolo #!/bin/ruby class WanderingWalker SIGN = [1, -1] CHECK_LIMIT = %w(min max) DEFAULT_GENERATOR = lambda { |limit| rand(limit) * SIGN[rand(2)] } attr_reader :position, :limit, :target alias :level :position def initialize(limit, &block) @limit = limit @generator = block_given? ? block : DEFAULT_GENERATOR @position = 0 generate_target end def walk(steps) generate_target while @position == @target new_pos = [(@position + direction*steps), @target].send \ CHECK_LIMIT[SIGN.index(direction)] @position = new_pos end def distance @target - @position end def direction if distance == 0 : 1 else distance/distance.abs end end private def generate_target @target = @generator.call limit end end class FuzzyTime FORMATS = %w(%H:%M %I:%M) attr_reader :fuzziness #two params, both optional time and an hash #time: a time object used as the starting point #hash params #:hidden_digits - hidden_digits number, from right #:precision - maximum distance from real time in seconds def initialize(time=Time.new, opts={}) @internaltime = time @last_call = @printed_time = Time.new @precision = opts[:precision] || 300 @fuzziness = WanderingWalker.new(@precision) @format = FORMATS[0] @hidden_digits = opts[:hidden_digits] || 1 @sub_args = case @hidden_digits when 0 : [//, ''] when 1 : [/\d$/, '~'] when 2 : [/\d{2}$/, '~~'] when 3 : [/\d:\d{2}$/, '~:~~'] else raise "nothing to see!" end end def advance(secs) tic(secs) end def update tic Time.new - @last_call end def actual @internaltime end #switch 12 / 24h format def toggle_format @format = FORMATS[FORMATS.index(@format) == 0 ? 1 : 0] end def to_s @printed_time = [@printed_time, (@internaltime + fuzziness.level)].max @printed_time.strftime(@format).sub(*@sub_args) end private def tic(secs=1) @internaltime += secs @last_call = Time.new @fuzziness.walk secs end end Ad here's the super ugly runner script #!/bin/ruby require 'fuzzy_time' # accepts a FuzzyTime object as argument + an hash of options #possible options # :time_warp if true instead of working as a clock just prints out #in an instant all the clock output, if false, works as a clock # :step_size how often the clock will be updated in seconds # :print_actual prints the actual time # :print_fuzziness, prints the error size # :toggle switch at every step from 24 to 12 hour format # :duration how many step_size the clock should run class FuzzyExec def initialize(ft = FuzzyTime.new ,opts = {}) opt_def = {:duration => 100, :step_size => 60, :print_actual => true} @opts = opt_def.update opts @ft = ft end def show_fuzzy_clock @opts[:duration].times do @ft.toggle_format if @opts[:toggle] out = [] out << @ft.to_s out << @ft.actual.strftime('%H:%M') if @opts[:print_actual] out << @ft.fuzziness.level if @opts[:print_fuzziness] out << Time.new.strftime('%H:%M') if @opts[:print_current] puts out.join ' ' if @opts[:time_warp] @ft.advance(@opts[:step_size]) else sleep @opts[:step_size] @ft.update end end end end