diff --git a/2018/ruby/day_16.rb b/2018/ruby/day_16.rb
new file mode 100644
index 0000000..12974cb
--- /dev/null
+++ b/2018/ruby/day_16.rb
@@ -0,0 +1,74 @@
+require "minitest"
+require "minitest/pride"
+
+OPCODES = {
+ addr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] + registers[b] } },
+ addi: ->(a, b, c) { ->(registers) { registers[c] = registers[a] + b } },
+ mulr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] * registers[b] } },
+ muli: ->(a, b, c) { ->(registers) { registers[c] = registers[a] * b } },
+ banr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] & registers[b] } },
+ bani: ->(a, b, c) { ->(registers) { registers[c] = registers[a] & b } },
+ borr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] | registers[b] } },
+ bori: ->(a, b, c) { ->(registers) { registers[c] = registers[a] | b } },
+ setr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] } },
+ seti: ->(a, b, c) { ->(registers) { registers[c] = a } },
+ gtir: ->(a, b, c) { ->(registers) { registers[c] = (a > registers[b]) ? 1 : 0 } },
+ gtri: ->(a, b, c) { ->(registers) { registers[c] = (a > b) ? 1 : 0 } },
+ gtrr: ->(a, b, c) { ->(registers) { registers[c] = (registers[a] > registers[b]) ? 1 : 0 } },
+ eqir: ->(a, b, c) { ->(registers) { registers[c] = (a == registers[b]) ? 1 : 0 } },
+ eqri: ->(a, b, c) { ->(registers) { registers[c] = (a == b) ? 1 : 0 } },
+ eqrr: ->(a, b, c) { ->(registers) { registers[c] = (registers[a] == registers[b]) ? 1 : 0 } },
+}
+
+class TestOpcodes < Minitest::Test
+ def test_mulr
+ registers = [3, 2, 1, 1]
+
+ OPCODES[:mulr].call(2, 1, 2).call(registers)
+
+ assert_equal [3, 2, 2, 1], registers
+ end
+
+ def test_addi
+ registers = [3, 2, 1, 1]
+
+ OPCODES[:addi].call(2, 1, 2).call(registers)
+
+ assert_equal [3, 2, 2, 1], registers
+ end
+
+ def test_seti
+ registers = [3, 2, 1, 1]
+
+ OPCODES[:seti].call(2, 1, 2).call(registers)
+
+ assert_equal [3, 2, 2, 1], registers
+ end
+end
+
+def solve(input)
+ samples = input.scan(/
+ Before:\s*((?~\n))\n
+ ((?~\n))\n
+ After:\s*((?~\n))\n
+ /mx)
+ samples.count {|sample|
+ before = sample[0].scan(/\d+/).map(&:to_i)
+ opcode = sample[1].scan(/\d+/).map(&:to_i)
+ after = sample[2].scan(/\d+/).map(&:to_i)
+ _, *args = opcode
+
+ opcode_count = OPCODES.count {|_, instruction|
+ registers = before.dup
+ instruction[*args][registers]
+ registers == after
+ }
+ opcode_count >= 3
+ }
+end
+
+if __FILE__ == $0
+ require "minitest/autorun" and exit if ENV["TEST"]
+
+ puts solve(ARGF.read)
+end