Skip to content

Instantly share code, notes, and snippets.

@jimweirich
Created April 17, 2012 23:53
Show Gist options
  • Save jimweirich/2409983 to your computer and use it in GitHub Desktop.
Save jimweirich/2409983 to your computer and use it in GitHub Desktop.
Berlin Clock Kata

Berlin Clock

Original Reference

Create a representation of the Berlin Clock for a given time (hh::mm:ss).

The Berlin Uhr (Clock) is a rather strange way to show the time. On the top of the clock there is a yellow lamp that blinks on/off every two seconds. The time is calculated by adding rectangular lamps.

The top two rows of lamps are red. These indicate the hours of a day. In the top row there are 4 red lamps. Every lamp represents 5 hours. In the lower row of red lamps every lamp represents 1 hour. So if two lamps of the first row and three of the second row are switched on that indicates 5+5+3=13h or 1 pm.

The two rows of lamps at the bottom count the minutes. The first of these rows has 11 lamps, the second 4. In the first row every lamp represents 5 minutes. In this first row the 3rd, 6th and 9th lamp are red and indicate the first quarter, half and last quarter of an hour. The other lamps are yellow. In the last row with 4 lamps every lamp represents 1 minute.

The lamps are switched on from left to right.

Test Cases (Y = Yellow, R = Red, O = Off)

Input

    00:00:00

Result

    Y
    OOOO
    OOOO
    OOOOOOOOOOO
    OOOO

INPUT

    13:17:01

RESULT

    O
    RROO
    RRRO
    YYROOOOOOOO
    YYOO

INPUT

    23:59:59

RESULT

    O
    RRRR
    RRRO
    YYRYYRYYRYY
    YYYY

INPUT

    24:00:00

RESULT

    Y
    RRRR
    RRRR
    OOOOOOOOOOO
    OOOO

References

As far as we know Meike Mertsch was the first to use the example as a Code Kata.

One description of the Berlin Clock is here.

class AnsiFormatter
Y = "\e[33m*\e[0m"
R = "\e[31m*\e[0m"
DOT = "\e[0m.\e[0m"
def initialize
@formatter = RyFormatter.new
end
def format(counts)
string = @formatter.format(counts)
string.gsub(/R/, R).gsub(/Y/, Y).gsub(/\./, DOT)
end
end
require 'rspec/given'
require 'ansi_formatter'
describe AnsiFormatter do
def format(counts)
deansi(formatter.format(counts))
end
def deansi(string)
string.gsub(/\e\[31m\*/,'R').gsub(/\e\[33m\*/,'Y').gsub(/\e\[\d+m/,'')
end
Given(:formatter) { AnsiFormatter.new }
Then { format([0,0,0,0,0]).should == ".\n....\n....\n...........\n....\n" }
Then { format([1,4,3,11,4]).should == "Y\nRRRR\nRRR.\nYYRYYRYYRYY\nYYYY\n" }
end
#!/usr/bin/ruby -wKU
require 'time'
require 'optparse'
$: << "."
require 'berlin_clock'
require 'ansi_formatter'
def options
@options ||= {}
end
def clear
printf "\e[H\e[2J"
end
def show_cursor
printf("\e[?25h")
end
def hide_cursor
printf("\e[?25l")
end
def hiding_cursor
hide_cursor
yield
rescue Exception
show_cursor
raise
end
def show_time(time)
clock = BerlinClock.new(time, AnsiFormatter.new)
puts clock.to_s
puts time.strftime("%H:%M:%S") if options[:digital]
end
OptionParser.new do |opts|
opts.banner = "Usage: berlin [options]"
opts.on("-d", "--digital", "Show Digital Time") do |v|
options[:digital] = v
end
opts.on("-r", "--repeat", "Repeat every second") do |v|
options[:repeat] = v
end
opts.on_tail("-h", "--help", "Help message") do
puts opts
exit
end
end.parse!
if ARGV.empty?
time = Time.now
else
time = Time.parse(ARGV.shift)
end
if ! options[:repeat]
show_time(time)
else
begin
hiding_cursor do
loop do
clear
show_time(Time.now)
sleep(1)
end
end
rescue Interrupt
puts
puts "Clock cancelled"
end
end
require 'ry_formatter'
class BerlinClock
def initialize(time, formatter=nil)
@time = time
@formatter = formatter || RyFormatter.new
end
def counts
[
@time.sec % 2,
@time.hour.div(5),
@time.hour % 5,
@time.min.div(5),
@time.min % 5,
]
end
def to_s
@formatter.format(counts)
end
end
require 'rspec/given'
require 'time'
require 'berlin_clock'
describe BerlinClock do
Given(:time) { "10:13:14" }
Given(:clock) { BerlinClock.new(Time.parse(time)) }
Then { clock.to_s.should == ".\nRR..\n....\nYY.........\nYYY.\n" }
context "at midnight" do
Given(:time) { "00:00:00" }
Then { clock.counts.should == [0, 0, 0, 0, 0] }
end
context "at odd seconds" do
Given(:time) { "00:00:35" }
Then { clock.counts.should == [1, 0, 0, 0, 0] }
end
context "at all ones" do
Given(:time) { "11:11:11" }
Then { clock.counts.should == [1, 2, 1, 2, 1] }
end
context "at with minutes < 5" do
Given(:time) { "00:03:52" }
Then { clock.counts.should == [0, 0, 0, 0, 3] }
end
context "at minutes > 5" do
Given(:time) { "00:43:52" }
Then { clock.counts.should == [0, 0, 0, 8, 3] }
end
context "at hours < 5" do
Given(:time) { "04:43:52" }
Then { clock.counts.should == [0, 0, 4, 8, 3] }
end
context "at hours > 5" do
Given(:time) { "17:43:52" }
Then { clock.counts.should == [0, 3, 2, 8, 3] }
end
context "at just before midnight" do
Given(:time) { "23:59:59" }
Then { clock.counts.should == [1, 4, 3, 11, 4] }
end
end
class RyFormatter
def format(counts)
make_line(counts[0], "Y") +
make_line(counts[1], "RRRR") +
make_line(counts[2], "RRRR") +
make_line(counts[3], "YYRYYRYYRYY") +
make_line(counts[4], "YYYY")
end
private
def make_line(count, template)
template[0,count] + '.' * (template.size - count) + "\n"
end
end
require 'rspec/given'
require 'ry_formatter'
describe RyFormatter do
Given(:formatter) { RyFormatter.new }
Then { formatter.format([0,0,0,0,0]).should == ".\n....\n....\n...........\n....\n" }
Then { formatter.format([1,2,4,11,4]).should == "Y\nRR..\nRRRR\nYYRYYRYYRYY\nYYYY\n" }
Then { formatter.format([1,4,3,11,4]).should == "Y\nRRRR\nRRR.\nYYRYYRYYRYY\nYYYY\n" }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment