require_relative 'util' # --- Day 12: Leonardo's Monorail --- # You finally reach the top floor of this building: a garden with a # slanted glass ceiling. Looks like there are no more stars to be had. # While sitting on a nearby bench amidst some tiger lilies, you manage # to decrypt some of the files you extracted from the servers # downstairs. # According to these documents, Easter Bunny HQ isn't just this # building - it's a collection of buildings in the nearby # area. They're all connected by a local monorail, and there's another # building not far from here! Unfortunately, being night, the monorail # is currently not operating. # You remotely connect to the monorail control systems and discover # that the boot sequence expects a password. The password-checking # logic (your puzzle input) is easy to extract, but the code it uses # is strange: it's assembunny code designed for the new computer you # just assembled. You'll have to execute the code and get the # password. # The assembunny code you've extracted operates on four registers (a, # b, c, and d) that start at 0 and can hold any integer. However, it # seems to make use of only a few instructions: # - `cpy x y` copies x (either an integer or the value of a register) # into register y. # - `inc x` increases the value of register x by one. # - `dec x` decreases the value of register x by one. # - `jnz x y` jumps to an instruction y away (positive means forward; # negative means backward), but only if x is not zero. # The jnz instruction moves relative to itself: an offset of -1 would # continue at the previous instruction, while an offset of 2 would # skip over the next instruction. # For example: # cpy 41 a # inc a # inc a # dec a # jnz a 2 # dec a # The above code would set register a to 41, increase its value by 2, # decrease its value by 1, and then skip the last dec a (because a is # not zero, so the jnz a 2 skips it), leaving register a at 42. When # you move past the last instruction, the program halts. # After executing the assembunny code in your puzzle input, what value # is left in register a? input = File.open('12.txt') { |f| f.readlines.map(&:chomp) } test_input = "cpy 41 a\ninc a\ninc a\n dec a\njnz a 2\ndec a".split("\n") class Computer attr_accessor :registers def initialize(input) @registers = { a: 0, b: 0, c: 0, d: 0 } @program = input.map { |line| parse(line) } @pc = 0 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] + 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 step op, *args = fetch send(op, *args) end def done? @pc < 0 || !@program[@pc] end def run step until done? end end def easy(input) computer = Computer.new(input) computer.run computer.registers[:a] end assert(easy(test_input) == 42) puts "easy(input): #{easy(input)}" # --- Part Two --- # As you head down the fire escape to the monorail, you notice it # didn't start; register c needs to be initialized to the position of # the ignition key. # If you instead initialize register c to be 1, what value is now left # in register a? def hard(input) computer = Computer.new(input) computer.registers[:c] = 1 computer.run computer.registers[:a] end puts "hard(input): #{hard(input)}"