2019-12-05 - Sunny with a Chance of Asteroids
(original .ipynb)
Day 5 puzzle input is a list of integers which are instructions for a program which will run on the processor we started writing a few days before (mine is here). Part 1 involves adding a bit more functionality to the interpreter we wrote and finding what the output is if we send 1
as the input to the program. Part 2 involves adding still more functionality and finding what is output if we send 5
as input.
opcode_add = 1 opcode_mul = 2 opcode_read = 3 opcode_write = 4 opcode_jump_true = 5 opcode_jump_false = 6 opcode_lt = 7 opcode_eq = 8 opcode_terminate = 99 mode_position = 0 mode_immediate = 1 def uop_read(memory, value, mode): debug_log("\tuop_read") if mode == mode_position: debug_log("\tposition") return int(memory[value]) elif mode == mode_immediate: debug_log("\timmediate") return int(value) else: raise Exception("UNKNOWN MODE") def uop_cond_jump(memory, pc, modes, cond): param_mode = try_pop_mode(modes) param_raw = int(memory[pc + 1]) param = uop_read(memory, param_raw, param_mode) dest_mode = try_pop_mode(modes) dest_raw = int(memory[pc + 2]) dest = uop_read(memory, dest_raw, dest_mode) if cond(param): return dest return pc + 3 def uop_cmp(memory, pc, modes, cmp): debug_log("\tuop_cmp") param0_mode = try_pop_mode(modes) param0_raw = int(memory[pc + 1]) param0 = uop_read(memory, param0_raw, param0_mode) param1_mode = try_pop_mode(modes) param1_raw = int(memory[pc + 2]) param1 = uop_read(memory, param1_raw, param1_mode) dest = int(memory[pc + 3]) if cmp(param0, param1): memory[dest] = 1 else: memory[dest] = 0 return pc + 4 def op_add(memory, pc, modes, input_buffer, output_buffer): debug_log("op_add") arg0_mode = try_pop_mode(modes) arg1_mode = try_pop_mode(modes) dest_mode = try_pop_mode(modes) arg0_raw = int(memory[pc + 1]) arg1_raw = int(memory[pc + 2]) dest = int(memory[pc + 3]) arg0 = uop_read(memory, arg0_raw, arg0_mode) arg1 = uop_read(memory, arg1_raw, arg1_mode) debug_log(dest, "=", arg0, "+", arg1) memory[dest] = str(int(arg0) + int(arg1)) return pc + 4 def op_mul(memory, pc, modes, input_buffer, output_buffer): debug_log("op_mul") arg0_mode = try_pop_mode(modes) arg1_mode = try_pop_mode(modes) dest_mode = try_pop_mode(modes) arg0_raw = int(memory[pc + 1]) arg1_raw = int(memory[pc + 2]) dest = int(memory[pc + 3]) arg0 = uop_read(memory, arg0_raw, arg0_mode) arg1 = uop_read(memory, arg1_raw, arg1_mode) debug_log(dest, "=", arg0, "*", arg1) memory[dest] = str(int(arg0) * int(arg1)) return pc + 4 # Opcode 3 takes a single integer as input and # saves it to the position given by its only parameter. For example, the instruction 3,50 # would take an input value and store it at address 50. def op_read(memory, pc, modes, input_buffer, output_buffer): debug_log("op_read") dest = int(memory[pc + 1]) val = input_buffer.pop() debug_log("\tdest", dest) debug_log("\tval", val) memory[dest] = str(val) return pc + 2 # Opcode 4 outputs the value of its only parameter. For example, the instruction 4,50 # would output the value at address 50. def op_write(memory, pc, modes, input_buffer, output_buffer): debug_log("op_write") src_mode = try_pop_mode(modes) src_raw = int(memory[pc + 1]) src = uop_read(memory, src_raw, src_mode) output_buffer.append(src) return pc + 2 # Opcode 5 is jump-if-true: # if the first parameter is non-zero, it sets the instruction pointer to # the value from the second parameter. Otherwise, it does nothing. def op_jump_true(memory, pc, modes, input_buffer, output_buffer): debug_log("op_jump_true") return uop_cond_jump(memory, pc, modes, lambda x: x != 0) # Opcode 6 is jump-if-false: # if the first parameter is zero, it sets the instruction pointer to the # value from the second parameter. Otherwise, it does nothing. def op_jump_false(memory, pc, modes, input_buffer, output_buffer): debug_log("op_jump_false") return uop_cond_jump(memory, pc, modes, lambda x: x == 0) # Opcode 7 is less than: # if the first parameter is less than the second parameter, it stores 1 # in the position given by the third parameter. Otherwise, it stores 0. def op_lt(memory, pc, modes, input_buffer, output_buffer): debug_log("op_lt") return uop_cmp(memory, pc, modes, lambda x, y: x < y) # Opcode 8 is equals: # if the first parameter is equal to the second parameter, it stores 1 # in the position given by the third parameter. Otherwise, it stores 0. def op_eq(memory, pc, modes, input_buffer, output_buffer): debug_log("op_eq") return uop_cmp(memory, pc, modes, lambda x, y: x == y) def decode_op(instr): if len(instr) > 2: return int(instr[-2:]) return int(instr) def decode_modes(instr): if len(instr) > 2: return [ int(d) for d in instr[:-2]] return [] def try_pop_mode(modes): if len(modes) == 0: return 0 return modes.pop() def decode_instr(instr): instr = str(instr) opcode = decode_op(instr) modes = decode_modes(instr) debug_log(f"\t{modes}") if opcode == opcode_add: return (op_add, modes) elif opcode == opcode_mul: return (op_mul, modes) elif opcode == opcode_read: return (op_read, modes) elif opcode == opcode_write: return (op_write, modes) elif opcode == opcode_jump_true: return (op_jump_true, modes) elif opcode == opcode_jump_false: return (op_jump_false, modes) elif opcode == opcode_lt: return (op_lt, modes) elif opcode == opcode_eq: return (op_eq, modes) else: raise Exception(f"Invalid opcode {opcode}") def run_intcode(program, input_buffer, output_buffer): pc = 0 instr = program[pc] while int(instr) != opcode_terminate: debug_log(pc, instr) (op, modes) = decode_instr(instr) pc = op(program, pc, modes, input_buffer, output_buffer) instr = program[pc] debug_log(program) return program[0] def debug_log(*args): # print(*args) pass puzzle_input_str = open("puzzle_input/day5.txt", "r").read().split(",") puzzle_input = [int(s) for s in puzzle_input_str] def run_program(noun, verb, program, input_buffer, output_buffer): program_copy = [ x for x in program ] # program_copy[1] = noun # program_copy[2] = verb return run_intcode(program_copy, input_buffer, output_buffer) output = [] print(run_program(12, 2, puzzle_input, [1], output)) print(output)
3 [0, 0, 0, 0, 0, 0, 0, 0, 0, 9775037]
output = [] print(run_program(None, None, puzzle_input, [5], output)) print(output) # 3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99 - take input, if<8 output 999, if ==8 output 1000, if>8 output 1001 # 3,9,8,9,10,9,4,9,99,-1,8 - Using position mode, consider whether the input is equal to 8; output 1 (if it is) or 0 (if it is not). # 3,9,7,9,10,9,4,9,99,-1,8 - Using position mode, consider whether the input is less than 8; output 1 (if it is) or 0 (if it is not). # 3,3,1108,-1,8,3,4,3,99 - Using immediate mode, consider whether the input is equal to 8; output 1 (if it is) or 0 (if it is not). # 3,3,1107,-1,8,3,4,3,99 - Using immediate mode, consider whether the input is less than 8; output 1 (if it is) or 0 (if it is not).
314 [15586959]