mirror of
https://github.com/thib8956/advent-of-code.git
synced 2025-08-24 08:21:57 +00:00
chore: create new project structure and aoc.py runner script
This commit is contained in:
177
adventofcode/2019/day5/day5.py
Normal file
177
adventofcode/2019/day5/day5.py
Normal file
@@ -0,0 +1,177 @@
|
||||
#! /usr/bin/env python3
|
||||
from collections import namedtuple
|
||||
from enum import Enum
|
||||
import logging
|
||||
|
||||
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.WARN)
|
||||
|
||||
|
||||
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.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 = 0
|
||||
self.output = 0
|
||||
|
||||
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):
|
||||
program[program[ip + 1]] = self.input
|
||||
ip += 2
|
||||
return ip
|
||||
|
||||
def handle_output(self, program, ip):
|
||||
param = (
|
||||
program[ip + 1]
|
||||
if self.modes[0] is Mode.IMMEDIATE
|
||||
else program[program[ip + 1]]
|
||||
)
|
||||
|
||||
self.output = param
|
||||
print("OUT ", param)
|
||||
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=[]):
|
||||
ip = 0
|
||||
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)
|
||||
return instruction.output
|
||||
|
||||
|
||||
def tests():
|
||||
inputs = (
|
||||
[1, 0, 0, 0, 99], # ADD 1 + 1 to location 0
|
||||
[2, 3, 0, 3, 99], # MUL 2 * 3 to location 3
|
||||
[2, 4, 4, 5, 99, 0],
|
||||
[1, 1, 1, 4, 99, 5, 6, 0, 99],
|
||||
[1101, 1, 1, 0, 99], # ADD 1 + 1 to location 0 (direct access)
|
||||
)
|
||||
expected_outputs = (
|
||||
[2, 0, 0, 0, 99], # 1 + 1 = 2
|
||||
[2, 3, 0, 6, 99], # 3 * 2 = 6
|
||||
[2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801
|
||||
[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):
|
||||
result = inp[:]
|
||||
interpret_intcode(result)
|
||||
assert (
|
||||
result == expected_outputs[i]
|
||||
), f"Expected output for {inp} is {expected_outputs[i]}, but found {result} instead."
|
||||
|
||||
# factorial test
|
||||
fac = [3,29,1007,29,2,28,1005,28,24,2,27,29,27,1001,29,-1,29,1101,0,0,28,1006,28,2,4,27,99,1,0,0]
|
||||
res = interpret_intcode(fac, [4])
|
||||
assert res == 24, f"Expected 24 but got {res}"
|
||||
|
||||
print("\nAll tests passed.\n")
|
||||
|
||||
|
||||
def run_input_program(inp):
|
||||
print("Start of input program.")
|
||||
memory = [int(x) for x in inp.readline().strip().split(",")]
|
||||
part1 = interpret_intcode(memory[::], [1])
|
||||
print("Part 1: ", part1)
|
||||
part2 = interpret_intcode(memory[::], [5])
|
||||
print("Part 2: ", part2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tests()
|
||||
import fileinput
|
||||
run_input_program(fileinput.input())
|
||||
|
192
adventofcode/2019/day5/day52.py
Normal file
192
adventofcode/2019/day5/day52.py
Normal file
@@ -0,0 +1,192 @@
|
||||
import logging
|
||||
|
||||
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG)
|
||||
|
||||
|
||||
class Instruction:
|
||||
code, argument_num = 0, 0
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
pass
|
||||
|
||||
def new_pc(self, intcode):
|
||||
return intcode.pc + self.argument_num + 1
|
||||
|
||||
|
||||
class PlusInstruction(Instruction):
|
||||
code, argument_num = 1, 3
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
logging.debug(f"ADD {arguments[0].value} {arguments[1].value}")
|
||||
intcode.set(arguments[2].address, arguments[0].value + arguments[1].value)
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class MultiplyInstruction(Instruction):
|
||||
code, argument_num = 2, 3
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
logging.debug(f"MUL {arguments[0].value} {arguments[1].value}")
|
||||
intcode.set(arguments[2].address, arguments[0].value * arguments[1].value)
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class InputInstruction(Instruction):
|
||||
code, argument_num = 3, 1
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
intcode.set(arguments[0].address, intcode.get_input())
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class OutputInstruction(Instruction):
|
||||
code, argument_num = 4, 1
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
intcode.output = arguments[0].value
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class JumpIfTrueInstruction(Instruction):
|
||||
code, argument_num = 5, 2
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
logging.debug(f"JMPT {arguments[0].value} {arguments[1].value}")
|
||||
return arguments[1].value if arguments[0].value != 0 else self.new_pc(intcode)
|
||||
|
||||
|
||||
class JumpIfFalseInstruction(Instruction):
|
||||
code, argument_num = 6, 2
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
logging.debug(f"JMPF {arguments[0].value} {arguments[1].value}")
|
||||
return arguments[1].value if arguments[0].value == 0 else self.new_pc(intcode)
|
||||
|
||||
|
||||
class LessThanInstruction(Instruction):
|
||||
code, argument_num = 7, 3
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
logging.debug(f"LT {arguments[0].value} {arguments[1].value}")
|
||||
intcode.set(arguments[2].address, int(arguments[0].value < arguments[1].value))
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class EqualsInstruction(Instruction):
|
||||
code, argument_num = 8, 3
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
intcode.set(arguments[2].address, int(arguments[0].value == arguments[1].value))
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class RelativeBaseOffsetInstruction(Instruction):
|
||||
code, argument_num = 9, 1
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
intcode.relative_base += arguments[0].value
|
||||
return self.new_pc(intcode)
|
||||
|
||||
|
||||
class HaltInstruction(Instruction):
|
||||
code, argument_num = 99, 0
|
||||
|
||||
def execute(self, intcode, arguments):
|
||||
intcode.halted = True
|
||||
return None
|
||||
|
||||
|
||||
class Argument:
|
||||
def __init__(self, value, address):
|
||||
self.value = value
|
||||
self.address = address
|
||||
|
||||
|
||||
class IntCode:
|
||||
def __init__(self, program, inputs=[], input_func=None):
|
||||
self.program = program[:]
|
||||
self.inputs = inputs[::-1]
|
||||
self.input_func = input_func
|
||||
self.relative_base = 0
|
||||
self.memory = {}
|
||||
self.output = None
|
||||
self.halted = False
|
||||
self.pc = 0
|
||||
|
||||
def _get_instruction(self, instruction_code):
|
||||
return next(
|
||||
cls for cls in Instruction.__subclasses__() if cls.code == instruction_code
|
||||
)
|
||||
|
||||
def _parse_arguments(self, argument_num):
|
||||
modes = str(self.program[self.pc]).zfill(5)[:3][::-1]
|
||||
arguments = []
|
||||
for i in range(argument_num):
|
||||
value = self.program[self.pc + i + 1] + (
|
||||
self.relative_base if modes[i] == "2" else 0
|
||||
)
|
||||
if modes[i] == "1":
|
||||
arguments.append(Argument(value, value))
|
||||
else:
|
||||
arguments.append(
|
||||
Argument(
|
||||
self.program[value]
|
||||
if value < len(self.program)
|
||||
else self.memory.get(value, 0),
|
||||
value,
|
||||
)
|
||||
)
|
||||
return arguments
|
||||
|
||||
def run(self):
|
||||
self.output = None
|
||||
while not self.halted and self.output is None:
|
||||
instruction = self._get_instruction(self.program[self.pc] % 100)
|
||||
arguments = self._parse_arguments(instruction.argument_num)
|
||||
self.pc = instruction().execute(self, arguments)
|
||||
return self.output
|
||||
|
||||
def execute(self):
|
||||
last_output = None
|
||||
while not self.halted:
|
||||
output = self.run()
|
||||
if not self.halted:
|
||||
last_output = output
|
||||
return last_output
|
||||
|
||||
def clone(self):
|
||||
cloned = IntCode(self.program)
|
||||
cloned.inputs = self.inputs[:]
|
||||
cloned.input_func = self.input_func
|
||||
cloned.relative_base = self.relative_base
|
||||
cloned.memory = {key: value for key, value in self.memory.items()}
|
||||
cloned.output = self.output
|
||||
cloned.halted = self.halted
|
||||
cloned.pc = self.pc
|
||||
return cloned
|
||||
|
||||
def get(self, address):
|
||||
return (
|
||||
self.program[address]
|
||||
if address < len(self.program)
|
||||
else self.memory.get(value + self.relative_base, 0)
|
||||
)
|
||||
|
||||
def set(self, address, value):
|
||||
target = self.program if address < len(self.program) else self.memory
|
||||
target[address] = value
|
||||
|
||||
def input(self, value):
|
||||
self.inputs = [value] + self.inputs
|
||||
|
||||
def get_input(self):
|
||||
return self.inputs.pop() if self.inputs else self.input_func()
|
||||
|
||||
|
||||
import os
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
code = list(map(int, open(os.path.join(SCRIPT_DIR, "input.txt")).read().split(",")))
|
||||
|
||||
part2 = IntCode(code, [5]).execute()
|
||||
print(part2)
|
Reference in New Issue
Block a user