mirror of
https://github.com/thib8956/advent-of-code.git
synced 2025-08-23 16:01:59 +00:00
133 lines
3.8 KiB
Python
133 lines
3.8 KiB
Python
#! /usr/bin/env python3
|
||
from collections import namedtuple
|
||
from enum import Enum
|
||
import logging
|
||
|
||
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG)
|
||
|
||
|
||
def get_nth_digit(n, number):
|
||
"Returns the nth digit of the input number"
|
||
return number // 10 ** n % 10
|
||
|
||
|
||
class Operation(Enum):
|
||
ADDITION = 1
|
||
MULTIPLICATION = 2
|
||
INPUT = 3
|
||
OUTPUT = 4
|
||
JMP_IF_TRUE = 5
|
||
JMP_IF_FALSE = 6
|
||
LESS_THAN = 7
|
||
EQUALS = 8
|
||
TERMINATION = 99
|
||
|
||
|
||
class Mode(Enum):
|
||
POSITION = 0
|
||
IMMEDIATE = 1
|
||
|
||
|
||
class Instruction:
|
||
def __init__(self, opcode):
|
||
self.opcode = opcode
|
||
# A B C D E
|
||
# 0 1 1 0 3
|
||
# A B C modes, DE opcode
|
||
self.operation = Operation(opcode % 100)
|
||
self.modes = [Mode(get_nth_digit(n, opcode)) for n in range(2, 5)]
|
||
self.handler_name = f"handle_{self.operation.name.lower()}"
|
||
self.handler = getattr(self, self.handler_name, self.handle_termination)
|
||
self.input = None
|
||
self.output = None
|
||
|
||
def __repr__(self):
|
||
return f"[{self.opcode}] Instruction({self.operation}, {self.modes})"
|
||
|
||
def handle(self, program, ip):
|
||
return self.handler(program, ip)
|
||
|
||
def handle_addition(self, program, ip):
|
||
first, second = self._get_parameters(program, ip)
|
||
logging.debug(f"ADD {first} {second}")
|
||
result = first + second
|
||
# the last mode should *always* be POSITION
|
||
program[program[ip + 3]] = result
|
||
ip += 4
|
||
return ip
|
||
|
||
def handle_multiplication(self, program, ip):
|
||
first, second = self._get_parameters(program, ip)
|
||
logging.debug(f"MUL {first} {second}")
|
||
result = first * second
|
||
# the last mode should *always* be POSITION
|
||
program[program[ip + 3]] = result
|
||
ip += 4
|
||
return ip
|
||
|
||
def handle_input(self, program, ip):
|
||
program[program[ip + 1]] = self.input
|
||
ip += 2
|
||
return ip
|
||
|
||
def handle_output(self, program, ip):
|
||
self.output = self._get_param(program, ip)
|
||
logging.debug(f"OUT {self.output}")
|
||
ip += 2
|
||
return ip
|
||
|
||
def handle_jmp_if_true(self, program, ip):
|
||
first, second = self._get_parameters(program, ip)
|
||
logging.debug(f"JMPT {first} {second}")
|
||
return second if first != 0 else ip + 3
|
||
|
||
def handle_jmp_if_false(self, program, ip):
|
||
first, second = self._get_parameters(program, ip)
|
||
logging.debug(f"JMPF {first} {second}")
|
||
return second if first == 0 else ip + 3
|
||
|
||
def handle_less_than(self, program, ip):
|
||
first, second = self._get_parameters(program, ip)
|
||
logging.debug(f"LT {first} {second}")
|
||
program[program[ip + 3]] = int(first < second)
|
||
ip += 4
|
||
return ip
|
||
|
||
def handle_equals(self, program, ip):
|
||
first, second = self._get_parameters(program, ip)
|
||
logging.debug(f"EQ {first} {second}")
|
||
program[program[ip + 3]] = int(first == second)
|
||
ip += 4
|
||
return ip
|
||
|
||
def handle_termination(self, program, ip):
|
||
logging.debug("HALT")
|
||
return ip
|
||
|
||
def _get_param(self, program, ip, i=0):
|
||
return (
|
||
program[ip + i + 1]
|
||
if self.modes[i] is Mode.IMMEDIATE
|
||
else program[program[ip + i + 1]]
|
||
)
|
||
|
||
def _get_parameters(self, program, ip):
|
||
first = self._get_param(program, ip, 0)
|
||
second = self._get_param(program, ip, 1)
|
||
return first, second
|
||
|
||
|
||
def interpret_intcode(program, stdin=[]):
|
||
ip = 0
|
||
out = []
|
||
while program[ip] != 99:
|
||
opcode = program[ip]
|
||
instruction = Instruction(opcode)
|
||
if instruction.operation == Operation.INPUT:
|
||
instruction.input=stdin.pop(0)
|
||
ip = instruction.handle(program, ip)
|
||
if instruction.output is not None:
|
||
out.append(instruction.output)
|
||
return out
|
||
|