wip wip wip
This commit is contained in:
parent
b904e20b10
commit
d441db73e5
206
day5/day5.py
206
day5/day5.py
@ -1,89 +1,122 @@
|
|||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from enum import Enum
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
def decode_opcode(inp):
|
def get_nth_digit(n, number):
|
||||||
opcode = inp % 100 # 2-digit opcode
|
"Returns the nth digit of the input number"
|
||||||
a = (inp // 10000) % 10 # mode of 1st param
|
return number // n ** 10 % 10
|
||||||
b = (inp // 1000) % 10 # mode of 2nd param
|
|
||||||
c = (inp // 100) % 10 # mode of 3rd param
|
|
||||||
# ABC DE
|
|
||||||
# ^^^ ^^
|
|
||||||
# MODE OPCODE
|
|
||||||
return a, b, c, opcode
|
|
||||||
|
|
||||||
|
|
||||||
def decode_parameters(input_prog, ip, modes):
|
class Operation(Enum):
|
||||||
"""
|
ADDITION = 1
|
||||||
Returns the adress of the parameters according to the input modes.
|
MULTIPLICATION = 2
|
||||||
if mode[n] == 1: direct access, else indirect access
|
INPUT = 3
|
||||||
"""
|
OUTPUT = 4
|
||||||
param1 = input_prog[ip + 1] if modes[2] == 0 else (ip + 1)
|
JMP_IF_TRUE = 5
|
||||||
param2 = input_prog[ip + 2] if modes[1] == 0 else (ip + 2)
|
JMP_IF_FALSE = 6
|
||||||
param3 = input_prog[ip + 3] if modes[0] == 0 else (ip + 3)
|
LESS_THAN = 7
|
||||||
return param1, param2, param3
|
EQUALS = 8
|
||||||
|
TERMINATION = 99
|
||||||
|
|
||||||
|
|
||||||
|
class Mode(Enum):
|
||||||
|
POSITION = 0
|
||||||
|
IMMEDIATE = 1
|
||||||
|
|
||||||
def interpret_intcode(input_prog, stdin=None):
|
|
||||||
# Instruction pointer: index of the next element to be executed
|
class Instruction:
|
||||||
|
def __init__(self, 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)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"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):
|
||||||
|
ip += 2
|
||||||
|
program[program[ip + 1]] = int(input("Enter ID > "))
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_output(self, program, ip):
|
||||||
|
print("OUT ", program[program[ip + 1]])
|
||||||
|
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):
|
||||||
|
print("HALT")
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def _get_parameters(self, program, ip):
|
||||||
|
first = (
|
||||||
|
program[ip + 1]
|
||||||
|
if self.modes[0] is Mode.IMMEDIATE
|
||||||
|
else program[program[ip + 1]]
|
||||||
|
)
|
||||||
|
second = (
|
||||||
|
program[ip + 2]
|
||||||
|
if self.modes[1] is Mode.IMMEDIATE
|
||||||
|
else program[program[ip + 2]]
|
||||||
|
)
|
||||||
|
return first, second
|
||||||
|
|
||||||
|
|
||||||
|
def interpret_intcode(program, stdin=None):
|
||||||
ip = 0
|
ip = 0
|
||||||
while ip < len(input_prog):
|
while program[ip] != 99:
|
||||||
instruction = input_prog[ip]
|
opcode = program[ip]
|
||||||
*modes, opcode = decode_opcode(instruction)
|
instruction = Instruction(opcode)
|
||||||
|
ip = instruction.handle(program, ip)
|
||||||
if opcode == 99:
|
|
||||||
break
|
|
||||||
|
|
||||||
parameters = decode_parameters(input_prog, ip, modes)
|
|
||||||
|
|
||||||
if opcode == 1: # ADD
|
|
||||||
print("ADD", " ".join(str(input_prog[p]) for p in parameters))
|
|
||||||
input_prog[parameters[2]] = input_prog[parameters[0]] + input_prog[parameters[1]]
|
|
||||||
ip += 4
|
|
||||||
|
|
||||||
elif opcode == 2: # MUL
|
|
||||||
print("MUL", " ".join(str(input_prog[p]) for p in parameters))
|
|
||||||
input_prog[parameters[2]] = input_prog[parameters[0]] * input_prog[parameters[1]]
|
|
||||||
ip += 4
|
|
||||||
|
|
||||||
elif opcode == 3: # IN
|
|
||||||
input_prog[parameters[1]] = stdin
|
|
||||||
ip += 2
|
|
||||||
|
|
||||||
elif opcode == 4: # OUT
|
|
||||||
if input_prog[ip + 1] == 99:
|
|
||||||
print("End of execution")
|
|
||||||
break
|
|
||||||
|
|
||||||
print("OUT ", input_prog[parameters[1]])
|
|
||||||
ip += 2
|
|
||||||
|
|
||||||
elif opcode == 5: # JMP if true
|
|
||||||
if input_prog[parameters[0]] != 0:
|
|
||||||
print("JT", " ".join(str(input_prog[p]) for p in parameters))
|
|
||||||
ip = input_prog[parameters[1]]
|
|
||||||
else:
|
|
||||||
ip += 3
|
|
||||||
|
|
||||||
elif opcode == 6: # JMP if false
|
|
||||||
if input_prog[parameters[0]] == 0:
|
|
||||||
print("JF", " ".join(str(input_prog[p]) for p in parameters))
|
|
||||||
ip = input_prog[parameters[1]]
|
|
||||||
else:
|
|
||||||
ip += 3
|
|
||||||
|
|
||||||
elif opcode == 7: # LT
|
|
||||||
print("LT", " ".join(str(input_prog[p]) for p in parameters))
|
|
||||||
res = int(input_prog[parameters[0]] < input_prog[parameters[1]])
|
|
||||||
input_prog[parameters[2]] = res
|
|
||||||
ip += 4
|
|
||||||
|
|
||||||
elif opcode == 8: # EQ
|
|
||||||
print("EQ", " ".join(str(input_prog[p]) for p in parameters))
|
|
||||||
res = int(input_prog[parameters[1]] == input_prog[parameters[2]])
|
|
||||||
input_prog[parameters[2]] = res
|
|
||||||
ip += 4
|
|
||||||
|
|
||||||
|
|
||||||
def tests():
|
def tests():
|
||||||
@ -92,12 +125,14 @@ def tests():
|
|||||||
[2, 3, 0, 3, 99], # MUL 2 * 3 to location 3
|
[2, 3, 0, 3, 99], # MUL 2 * 3 to location 3
|
||||||
[2, 4, 4, 5, 99, 0],
|
[2, 4, 4, 5, 99, 0],
|
||||||
[1, 1, 1, 4, 99, 5, 6, 0, 99],
|
[1, 1, 1, 4, 99, 5, 6, 0, 99],
|
||||||
|
[1101, 1, 1, 0, 99], # ADD 1 + 1 to location 0 (direct access)
|
||||||
)
|
)
|
||||||
expected_outputs = (
|
expected_outputs = (
|
||||||
[2, 0, 0, 0, 99], # 1 + 1 = 2
|
[2, 0, 0, 0, 99], # 1 + 1 = 2
|
||||||
[2, 3, 0, 6, 99], # 3 * 2 = 6
|
[2, 3, 0, 6, 99], # 3 * 2 = 6
|
||||||
[2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801
|
[2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801
|
||||||
[30, 1, 1, 4, 2, 5, 6, 0, 99],
|
[30, 1, 1, 4, 2, 5, 6, 0, 99],
|
||||||
|
[2, 1, 1, 0, 99], # 1 + 1 = 2 (direct access)
|
||||||
)
|
)
|
||||||
for i, inp in enumerate(inputs):
|
for i, inp in enumerate(inputs):
|
||||||
result = inp[:]
|
result = inp[:]
|
||||||
@ -108,16 +143,21 @@ def tests():
|
|||||||
print("\nAll tests passed.\n")
|
print("\nAll tests passed.\n")
|
||||||
|
|
||||||
|
|
||||||
|
def run_input_program(filename):
|
||||||
|
import os
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
with open(os.path.join(SCRIPT_DIR, filename)) as inp:
|
||||||
|
print("Start of input program.")
|
||||||
|
memory = [int(x) for x in inp.readline().strip().split(",")]
|
||||||
|
interpret_intcode(memory, 5)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
tests()
|
# tests()
|
||||||
# a = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9]
|
# a = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9]
|
||||||
# b = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]
|
# b = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]
|
||||||
|
|
||||||
# interpret_intcode(b, 0)
|
# interpret_intcode(b, 0)
|
||||||
# interpret_intcode(a, 0)
|
# interpret_intcode(a, 0)
|
||||||
|
run_input_program("input.txt")
|
||||||
with open("input.txt") as inp:
|
|
||||||
print("Start of input program.")
|
|
||||||
memory = [int(x) for x in inp.readline().strip().split(",")]
|
|
||||||
interpret_intcode(memory, 5)
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user