--envbJBWh7q8WU6mo
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Ahh, it feels good to do a ruby quiz again :-). I've ignored the other
solutions so far so someone else may have taken the same approach.
I took the approach that there are actual 3 times involved, so as to
hopefully avoid as little as possible the range limiting that Ara was
talking about. And it just made it easier to think about the problem.
Essentially, the time displayed to the user is all that must be
increase. So long as that is continually moving forward, the times
behind the scene don't really matter. That for me results in 3
different times.
- Actual time
- Fuzzed time : actual time fuzzed within the fuzzy range (+- 5 min default)
- Display time : Fuzzed time 'floored' to a granularity (10 min default)
As a result, my approach has the following algorithm:
- Calculate the actual time (update/advance)
- Calculate a fuzzy range for the new fuzzed time which has
- lower bound he maximum of the last display time or (actual - fuzz factor minutes)
- upper bound ctual + range
- randomly pick a new time in that range for the fuzzed time
- calculate the new display time off of the fuzzed time
As a result, the Fuzzed time can increase/decrease with respect to
itself, but will always be greater than the displayed time. But overall
the fuzzed time will continue to increase. And the display time is
always increasing.
I also threw in some Extra Credits: 24hour/12hour option, fuzz factor
(changing the +- range) and display granularity(one minute, ten minute
on hour).
Great Quiz!
enjoy,
-jeremy
--
Jeremy Hinegardner jeremy / hinegardner.org
--envbJBWh7q8WU6mo
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="fuzzytime.rb"
#----------------------------------------------------------------------
# Ruby Quiz #99 - Fuzzy Time
#
# Jeremy Hinegardner
#----------------------------------------------------------------------
class FuzzyTime
HOUR_24_FORMAT %H:%M"
HOUR_12_FORMAT %I:%M"
HOUR_FORMAT_OPTIONS ash.new(HOUR_24_FORMAT)
HOUR_FORMAT_OPTIONS[HOUR_12_FORMAT] OUR_12_FORMAT
HOUR_FORMAT_OPTIONS[:twelve_hour] OUR_12_FORMAT
HOUR_FORMAT_OPTIONS[HOUR_24_FORMAT] OUR_24_FORMAT
HOUR_FORMAT_OPTIONS[:twentyfour_hour] OUR_24_FORMAT
GRANULARITIES :one_minute, :ten_minute, :one_hour]
attr_accessor :time_format
attr_accessor :fuzz_factor
attr_accessor :display_granularity
attr_reader :actual
attr_reader :fuzzed
attr_reader :display
def initialize(time ime.now)
@actual ime.dup
@time_format OUR_24_FORMAT
@fuzz_factor * 60
@display_granularity ten_minute
# initialize a base history that is minimal to give maximum
# range for calculating a fuzzy time
@fuzzed actual - @fuzz_factor
@fuzz_history { :actual @actual, :fuzzed @fuzzed, :display calculate_display_time } ]
calculate_fuzz_time
end
def advance(seconds)
@actual + econds
calculate_fuzz_time
end
def update
@actual ime.now
calculate_fuzz_time
end
# return a string representation of the display time which is the time
# with up to the @disiplay_granularity replaced by '~'. when
# :one_minute is the granularity, nothing needs to be done
def to_s
s display.strftime(time_format)
case @display_granularity
when :ten_minute
s.chop!
s << "~"
when :one_hour
s.chop!
s.chop!
s << "~~"
end
return s
end
# allow the time format to be set to 24 hour or 12 hour time
def time_formatormat)
@time_format OUR_FORMAT_OPTIONS[format]
end
# all the fuzz factor to be set in a range of minutes, no limit
def fuzz_factor inutes)
@fuzz_factor 0 * minutes.abs
end
def fuzz_factor
(@fuzz_factor / 60).to_i
end
def display_granularity ranularity)
if GRANULARITIES.include?(granularity.to_sym)
@display_granularity ranularity
# need to recalculate this when the granularity is updated
calculate_display_time
else
raise ArgumentError, "display_granularity must be one of #{GRANULARITIES.join(",")}"
end
@display_granularity
end
private
#
# the display time is the fuzzy time converted to a "floor" time
# with a granularity base upon the @display_granularity. For
# example, if the fuzzed time is 13:43 then the display time would
# be:
#
# granularity display
#
# :one_minute 13:43
# :ten_minute 13:40
# :one_hour 13:00
#
def calculate_display_time
case @display_granularity
when :one_minute
min fuzzed.min
when :ten_minute
min @fuzzed.min / 10) * 10
when :one_hour
min
end
@display ime.mktime( @fuzzed.year, @fuzzed.month, @fuzzed.day, @fuzzed.hour, min, 0, 0)
end
#
# calculate the new fuzzy time.
#
# Since :
# 1) the displayed time must appear to be continually increasing
# 2) we must always be within the fuzz factor of the actual time
#
# Therefore:
# the lower bound of the fuzzy range is the maximum of the displayed
# time or the lower bond of the fuzz factor around the actual time.
#
def calculate_fuzz_time
last_display fuzz_history.last[:display]
min_fuzz_factor actual - @fuzz_factor
lower_bound ast_display > min_fuzz_factor ? last_display : min_fuzz_factor
upper_bound actual + @fuzz_factor
range pper_bound - lower_bound
@fuzzed ower_bound + rand(range + 1)
calculate_display_time
@fuzz_history << { :actual @actual, :fuzzed @fuzzed, :display @display }
end
end
--envbJBWh7q8WU6mo--