Skip to content

Instantly share code, notes, and snippets.

@katafrakt
Created December 18, 2017 21:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save katafrakt/b06c570517e11c646e6db16529e72dd4 to your computer and use it in GitHub Desktop.
Save katafrakt/b06c570517e11c646e6db16529e72dd4 to your computer and use it in GitHub Desktop.
Advent of Code 2017, day 18, referential Ruby implementation
class Program < Hash
Recv = Class.new(StandardError)
attr_reader :last_sound
def set(reg, val)
self[reg] = value(val)
end
def add(reg, val)
self[reg] += value(val)
end
def mul(reg, val)
self[reg] *= value(val)
end
def mod(reg, val)
self[reg] %= value(val)
end
def rcv(reg)
raise Recv if !value(reg).zero?
end
def snd(val)
@last_sound = value(val)
end
def jgz(reg, val)
value(reg).positive? ? value(val) : 1
end
private
def value(val)
val =~ /[a-z]+/ ? self[val].to_i : val.to_i
end
end
class Program2 < Program
attr_reader :position, :send_count, :instructions
def initialize(instructions)
@queue = []
@position = @send_count = 0
@instructions = instructions
end
def rcv(reg)
@waiting = @queue.empty?
@waiting ? @position -= 1 : self[reg] = @queue.shift
end
def snd(val)
@send_count += 1
value(val)
end
def enqueue(num)
@queue.push(num)
end
def waiting?
@waiting && @queue.empty?
end
def perform
return nil if terminated?
instruction = instructions[position]
ret = public_send(*instruction)
instruction.first == 'jgz' ? @position += ret : @position += 1
instruction.first == 'snd' ? ret : nil
end
def terminated?
position < 0 || position >= instructions.length
end
end
instructions = File.readlines("input").map(&:split)
position = 0
program = Program.new
program.default = 0
loop do
instruction = instructions[position]
ret = program.public_send(*instruction)
instruction.first == 'jgz' ? position += ret : position += 1
rescue Program::Recv
puts "part 1: #{program.last_sound}"
break
end
prog_0 = Program2.new(instructions)
prog_1 = Program2.new(instructions)
[prog_0, prog_1].each { |p| p.default = 0 }
prog_0["p"] = 0
prog_1["p"] = 1
loop do
results = [prog_0, prog_1].map(&:perform)
results[0] && prog_1.enqueue(results[0])
results[1] && prog_0.enqueue(results[1])
break if (prog_0.terminated? || prog_0.waiting?) && (prog_1.terminated? || prog_1.waiting?)
end
puts "part 2: #{prog_1.send_count}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment