diff --git a/2016/ruby/day_02.rb b/2016/ruby/day_02.rb
new file mode 100644
index 0000000..83c664f
--- /dev/null
+++ b/2016/ruby/day_02.rb
@@ -0,0 +1,76 @@
+class Keypad
+ MAP = (1..9).each_slice(3).to_a
+
+ attr_reader :x, :y
+
+ def initialize
+ @x, @y = 1, 1
+ end
+
+ def button
+ MAP[y][x]
+ end
+
+ def <<(dir)
+ delta = case dir
+ when ?U
+ [0, 1]
+ when ?D
+ [0, -1]
+ when ?L
+ [-1, 0]
+ when ?R
+ [1, 0]
+ else
+ raise "unexpected direction: '#{dir}'"
+ end
+ @x += delta[0]
+ @y -= delta[1]
+
+ @x = [[2, x].min, 0].max
+ @y = [[2, y].min, 0].max
+ end
+end
+
+if __FILE__ == $0
+ keypad = Keypad.new
+ code = DATA.read.lines.map {|line|
+ line.strip.chars.each do |char|
+ keypad << char
+ end
+ keypad.button
+ }
+ puts code.join
+end
+
+require 'minitest'
+# require 'minitest/autorun'
+
+class TestDay2 < Minitest::Test
+ def test_day2
+ keypad = Keypad.new
+ assert_equal 5, keypad.button
+
+ 'ULL'.chars.each do |char|
+ keypad << char
+ end
+ assert_equal 1, keypad.button
+
+ 'RRDDD'.chars.each do |char|
+ keypad << char
+ end
+ assert_equal 9, keypad.button
+
+ 'LURDL'.chars.each do |char|
+ keypad << char
+ end
+ assert_equal 8, keypad.button
+
+ 'UUUUD'.chars.each do |char|
+ keypad << char
+ end
+ assert_equal 5, keypad.button
+ end
+end
+
+__END__