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]