Files
advent-of-code/2019/intcode/intcode.py
2025-08-01 00:02:28 +02:00

133 lines
3.8 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /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