require_relative 'util' # --- Day 25: Clock Signal --- # You open the door and find yourself on the roof. The city sprawls # away from you for miles and miles. # There's not much time now - it's already Christmas, but you're # nowhere near the North Pole, much too far to deliver these stars to # the sleigh in time. # However, maybe the huge antenna up here can offer a solution. After # all, the sleigh doesn't need the stars, exactly; it needs the timing # data they provide, and you happen to have a massive signal generator # right here. # You connect the stars you have to your prototype computer, connect # that to the antenna, and begin the transmission. # Nothing happens. # You call the service number printed on the side of the antenna and # quickly explain the situation. "I'm not sure what kind of equipment # you have connected over there," he says, "but you need a clock # signal." You try to explain that this is a signal for a clock. # "No, no, a clock signal - timing information so the antenna computer # knows how to read the data you're sending it. An endless, # alternating pattern of 0, 1, 0, 1, 0, 1, 0, 1, 0, 1...." He trails # off. # You ask if the antenna can handle a clock signal at the frequency # you would need to use for the data from the stars. "There's no way # it can! The only antenna we've installed capable of that is on top # of a top-secret Easter Bunny installation, and you're definitely # not-" You hang up the phone. # You've extracted the antenna's clock signal generation assembunny # code (your puzzle input); it looks mostly compatible with code you # worked on just recently. # This antenna code, being a signal generator, uses one extra # instruction: # - out x transmits x (either an integer or the value of a register) # as the next value for the clock signal. # The code takes a value (via register a) that describes the signal to # generate, but you're not sure how it's used. You'll have to find the # input to produce the right signal through experimentation. # What is the lowest positive integer that can be used to initialize # register a and cause the code to output a clock signal of 0, 1, 0, # 1... repeating forever? input = File.open('25.txt') { |f| f.readlines.map(&:chomp) } class Computer attr_accessor :registers attr_reader :out_queue def initialize(input) @registers = { a: 0, b: 0, c: 0, d: 0 } @program = input.map { |line| parse(line) } @pc = 0 @out_queue = [] end def parse_arg(arg) if arg[/^[-+]?\d+$/] arg.to_i else arg.to_sym end end def parse(line) op, *ops = line.split [op.to_sym] + ops.map { |o| parse_arg(o) } end def lookup(reg_or_arg) if reg_or_arg.is_a?(Symbol) @registers[reg_or_arg] else reg_or_arg end end def fetch assert(@pc < @program.length) result = @program[@pc] @pc += 1 result end def cpy(src, dest) @registers[dest] = lookup(src) end def inc(reg) @registers[reg] += 1 end def dec(reg) @registers[reg] -= 1 end def jnz(arg1, arg2) # HACK: account for fetch always incrementing @pc @pc += lookup(arg2) - 1 if lookup(arg1) != 0 end def nop; end def incmul(reg, arg1, arg2) @registers[reg] += lookup(arg1) * lookup(arg2) end def out(arg) @out_queue << lookup(arg) end def step op, *args = fetch send(op, *args) end def done? @pc < 0 || !@program[@pc] end def run step until done? end def at_hotspot?(target) (target + 5 < @program.length && # 5 lines of context @program[target][0] == :inc && @program[target + 1][0] == :dec && @program[target + 2][0] == :jnz && @program[target + 2][2] == -2 && @program[target + 3][0] == :dec && @program[target + 4][0] == :jnz && @program[target + 4][2] == -5) end def patch_hotspot(target) acc = @program[target][1] arg1 = @program[target + 1][1] arg2 = @program[target + 3][1] @program[target] = [:incmul, acc, arg1, arg2] @program[target + 1] = [:cpy, 0, arg1] @program[target + 2] = [:cpy, 0, arg2] @program[target + 3] = [:nop] @program[target + 4] = [:nop] end def optimize @program.length.times do |i| patch_hotspot(i) if at_hotspot?(i) end end end def clock_signal?(ary) bit = ary.shift return false unless [0, 1].include?(bit) ary.each do |b| return false unless b ^ 1 == bit bit ^= 1 end true end def easy(input) 0.step do |i| puts "testing a = #{i}" computer = Computer.new(input) computer.registers[:a] = i computer.optimize 100_000.times { computer.step } out = computer.out_queue return i if out.length > 20 && clock_signal?(out) end end puts "easy(input): #{easy(input)}"