module ClockFlip
ClockFlip — upside-down seven-segment words from digital clock times. Public: format_time, display_hour, display_digit_string, upside_down_word, upside_down_candidates, valid_flip_word?, flip_word?, local_parts Depends: (none) Tests: test/lib/clock_flip_test.rb
Constants
- FLIP_MAP
- FLIP_OPTIONS
-
Per-digit letter options (first = canonical primary / tailed clock reading). g is only for upside-down 6; upside-down 1 is l or i.
- LETTER_PATTERN
Public Instance Methods
Source
# File lib/clock/flip.rb, line 110 def candidate_preference_index(digits, word) index = 0 multiplier = 1 digits.chars.reverse_each.with_index do |digit, position| options = FLIP_OPTIONS[digit] || [] letter = word[-(position + 1)] option_index = options.index(letter) || options.length index += option_index * multiplier multiplier *= [options.length, 1].max + 1 end index end
Source
# File lib/clock/flip.rb, line 47 def display_digit_string(hour, minute) h = display_hour(hour) m = minute.to_i raise ArgumentError, "invalid minute: #{m}" unless (0..59).cover?(m) digits = if h >= 10 format("%02d%02d", h, m) else format("%03d", (h * 100) + m) end unless digits.length.between?(3, 4) raise ArgumentError, "clock digit string must be 3–4 digits, got #{digits.inspect}" end digits end
Upright digit string as shown on a 12-hour digital clock (no colon, no seconds): 3 digits when hour < 10 (e.g. 5:10 -> “510”), 4 when hour >= 10 (e.g. 12:05 -> “1205”).
Source
# File lib/clock/flip.rb, line 31 def display_hour(hour) h = hour.to_i raise ArgumentError, "invalid hour: #{h}" unless (0..23).cover?(h) return 12 if h.zero? || h == 12 h > 12 ? h - 12 : h end
24-hour wall clock -> 12-hour face (12 for midnight and noon).
Source
# File lib/clock/flip.rb, line 96 def flip_word?(word) word.to_s.match?(LETTER_PATTERN) && !word.to_s.empty? end
Source
# File lib/clock/flip.rb, line 40 def format_time(hour, minute) format("%d:%02d", display_hour(hour), minute.to_i) end
Source
# File lib/clock/flip.rb, line 100 def local_parts(now:, utc_offset:) local = now.getlocal(utc_offset) [local.hour, local.min, local] end
Source
# File lib/clock/flip.rb, line 105 def map_digits_to_word(digits, map) digits.chars.filter_map { |digit| map[digit] }.join end
Source
# File lib/clock/flip.rb, line 76 def upside_down_candidates(hour, minute) digits = upside_down_digits(hour, minute) option_lists = digits.chars.map { |digit| FLIP_OPTIONS[digit] || [] } return [] if option_lists.any?(&:empty?) words = option_lists.reduce([""]) do |acc, options| acc.flat_map { |prefix| options.map { |letter| "#{prefix}#{letter}" } } end ranked = words.uniq.sort_by { |word| [candidate_preference_index(digits, word), word] } primary = upside_down_word(hour, minute) ranked = [primary] + (ranked - [primary]) if primary && !primary.empty? ranked end
Source
# File lib/clock/flip.rb, line 66 def upside_down_digits(hour, minute) display_digit_string(hour, minute).reverse end
Reversed display digits for upside-down left-to-right reading.
Source
# File lib/clock/flip.rb, line 72 def upside_down_word(hour, minute) map_digits_to_word(upside_down_digits(hour, minute), FLIP_MAP) end
Read digits left-to-right on the upside-down display: reverse the upright digit string, then map each segment (7 at the front becomes l at the end).
Source
# File lib/clock/flip.rb, line 91 def valid_flip_word?(hour, minute, word) token = word.to_s.strip.downcase upside_down_candidates(hour, minute).include?(token) end