advent-of-code/2019/day07-intcode.py

137 lines
4.2 KiB
Python

import itertools
from typing import List, Union
NUMBER_OF_PARAMS_MAP = {
1: 3,
2: 3,
3: 1,
4: 1,
5: 2,
6: 2,
7: 3,
8: 3,
}
LAST_IS_RESULT_MAP = {
1: True,
2: True,
3: True,
4: False,
5: False,
6: False,
7: True,
8: True,
}
class Computer:
@staticmethod
def get_value(program, args, param_modes, index):
if param_modes[index] == 0:
return program[args[index]]
return args[index]
@staticmethod
def parse_args(program, raw_args, param_modes, last_is_result=False):
args = []
limit = -1 if last_is_result else None
for i, arg in enumerate(raw_args[:limit]):
args.append(Computer.get_value(program, raw_args, param_modes, i))
if last_is_result:
args.append(raw_args[-1])
return args
def __init__(self, initial_program: List[int], inputs: List[int] = None):
self.program = initial_program.copy() # type: List[int]
self.inputs = inputs.copy() # type: List[int]
def compute(self, inputs: List[int] = None) -> Union[int, None]:
if inputs is None:
inputs = []
self.inputs.extend(inputs)
pointer = 0
while pointer < len(self.program):
pointer_moved = False
instruction = str(self.program[pointer])
code = int(instruction[-2:])
if code == 99:
raise
number_of_params = NUMBER_OF_PARAMS_MAP[code]
offset = number_of_params + 1
param_modes = instruction[:-2]
param_modes = param_modes.zfill(number_of_params)
param_modes = list(map(int, reversed(param_modes)))
raw_params = []
for i in range(1, offset):
raw_params.append(self.program[pointer + i])
last_is_result = LAST_IS_RESULT_MAP[code]
params = self.parse_args(
self.program, raw_params, param_modes, last_is_result
)
if code == 1:
# Addition
self.program[params[2]] = params[0] + params[1]
elif code == 2:
# Multiplication
self.program[params[2]] = params[0] * params[1]
elif code == 3:
# Input
try:
input_value = int(self.inputs.pop(0))
except IndexError:
input_value = int(input(f"Input for instruction {pointer}\n> "))
self.program[params[0]] = input_value
elif code == 4:
# Output
return params[0]
elif code == 5:
# Jump if true
if params[0] != 0:
pointer = params[1]
pointer_moved = True
elif code == 6:
# Jump if false
if params[0] == 0:
pointer = params[1]
pointer_moved = True
elif code == 7:
# Less than
if params[0] < params[1]:
self.program[params[2]] = 1
else:
self.program[params[2]] = 0
elif code == 8:
# Equals
if params[0] == params[1]:
self.program[params[2]] = 1
else:
self.program[params[2]] = 0
else:
raise ValueError(f"Something bad happened, code={code}")
if not pointer_moved:
pointer += offset
def main():
with open("inputs/day07") as input_file:
original_program = list(map(int, input_file.read().split(",")))
values = set()
for phase in itertools.permutations("01234"):
amp1 = Computer(original_program, [int(phase[0])]).compute([0])
amp2 = Computer(original_program, [int(phase[1])]).compute([amp1])
amp3 = Computer(original_program, [int(phase[2])]).compute([amp2])
amp4 = Computer(original_program, [int(phase[3])]).compute([amp3])
amp5 = Computer(original_program, [int(phase[4])]).compute([amp4])
values.add(amp5)
print(max(values))
if __name__ == "__main__":
main()