mirror of
https://github.com/thib8956/advent-of-code.git
synced 2025-08-23 16:01:59 +00:00
2019 day 7 part 1
This commit is contained in:
53
2019/day7/day7.py
Normal file
53
2019/day7/day7.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# TODO replace PYTHONPATH hack with a proper solution, like making intcode an
|
||||||
|
# installed module https://stackoverflow.com/a/50194143
|
||||||
|
sys.path.append(str(Path(__file__).absolute().parent.parent / "intcode"))
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
from intcode import interpret_intcode
|
||||||
|
|
||||||
|
|
||||||
|
def main(inp):
|
||||||
|
mem = list(map(int, inp.readline().rstrip().split(",")))
|
||||||
|
max_ret = 0
|
||||||
|
for seq in itertools.permutations([0, 1, 2, 3, 4], 5):
|
||||||
|
ret = amplifiers(mem, list(seq))
|
||||||
|
#print(ret)
|
||||||
|
max_ret = max(ret[0], max_ret)
|
||||||
|
print(max_ret)
|
||||||
|
|
||||||
|
|
||||||
|
def amplifiers(program, sequence):
|
||||||
|
ret = interpret_intcode(program[::], [sequence.pop(0), 0])
|
||||||
|
ret = interpret_intcode(program[::], [sequence.pop(0), ret.pop()])
|
||||||
|
ret = interpret_intcode(program[::], [sequence.pop(0), ret.pop()])
|
||||||
|
ret = interpret_intcode(program[::], [sequence.pop(0), ret.pop()])
|
||||||
|
ret = interpret_intcode(program[::], [sequence.pop(0), ret.pop()])
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def tests():
|
||||||
|
program = [3, 15, 3, 16, 1002, 16, 10, 16, 1, 16, 15, 15, 4, 15, 99, 0, 0]
|
||||||
|
sequence = [4, 3, 2, 1, 0]
|
||||||
|
res = amplifiers(program, sequence)
|
||||||
|
assert res == [43210]
|
||||||
|
|
||||||
|
program = [3,23,3,24,1002,24,10,24,1002,23,-1,23, 101,5,23,23,1,24,23,23,4,23,99,0,0]
|
||||||
|
sequence = [0,1,2,3,4]
|
||||||
|
res = amplifiers(program, sequence)
|
||||||
|
assert res == [54321]
|
||||||
|
|
||||||
|
|
||||||
|
program = [3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33, 1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0]
|
||||||
|
sequence = [1,0,4,3,2]
|
||||||
|
res = amplifiers(program, sequence)
|
||||||
|
assert res == [65210]
|
||||||
|
|
||||||
|
print("All tests passed")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import fileinput
|
||||||
|
main(fileinput.input())
|
||||||
|
tests()
|
||||||
|
|
132
2019/intcode/intcode.py
Normal file
132
2019/intcode/intcode.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#! /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
|
||||||
|
|
83
2019/intcode/tests.py
Normal file
83
2019/intcode/tests.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Run with pytest
|
||||||
|
import logging
|
||||||
|
from intcode import *
|
||||||
|
|
||||||
|
|
||||||
|
def run_test(program, expected_mem=None, stdin=[], expected_out=None):
|
||||||
|
mem = program[::]
|
||||||
|
out = interpret_intcode(mem, stdin=stdin)
|
||||||
|
|
||||||
|
if expected_mem is not None:
|
||||||
|
assert expected_mem == mem
|
||||||
|
if expected_out is not None:
|
||||||
|
assert expected_out == out
|
||||||
|
|
||||||
|
|
||||||
|
def test_day2():
|
||||||
|
tests = [
|
||||||
|
[[1,0,0,0,99], [2,0,0,0,99]], # ADD 1 + 1 to location 0
|
||||||
|
[[2,3,0,3,99], [2,3,0,6,99]], # MUL 2 * 3 to location 3
|
||||||
|
[[2,4,4,5,99,0], [2,4,4,5,99,9801]], # MUL 99 * 99 to location 5 (9801)
|
||||||
|
[[1,1,1,4,99,5,6,0,99], [30,1,1,4,2,5,6,0,99]],
|
||||||
|
]
|
||||||
|
for program, expected in tests:
|
||||||
|
run_test(program, expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_day5_basic():
|
||||||
|
# Using position mode, consider whether the input is equal to 8; output 1
|
||||||
|
# (if it is) or 0 (if it is not).
|
||||||
|
prog = [3,9,8,9,10,9,4,9,99,-1,8]
|
||||||
|
run_test(prog, stdin=[0], expected_out=[0])
|
||||||
|
run_test(prog, stdin=[8], expected_out=[1])
|
||||||
|
|
||||||
|
# Using position mode, consider whether the input is less than 8; output 1
|
||||||
|
# (if it is) or 0 (if it is not).
|
||||||
|
prog = [3,9,7,9,10,9,4,9,99,-1,8]
|
||||||
|
run_test(prog, stdin=[7], expected_out=[1])
|
||||||
|
run_test(prog, stdin=[9], expected_out=[0])
|
||||||
|
|
||||||
|
# Using immediate mode, consider whether the input is equal to 8; output 1
|
||||||
|
# (if it is) or 0 (if it is not).
|
||||||
|
prog = [3,3,1108,-1,8,3,4,3,99]
|
||||||
|
run_test(prog, stdin=[8], expected_out=[1])
|
||||||
|
run_test(prog, stdin=[9], expected_out=[0])
|
||||||
|
|
||||||
|
# Using immediate mode, consider whether the input is less than 8; output 1
|
||||||
|
# (if it is) or 0 (if it is not).
|
||||||
|
prog = [3,3,1107,-1,8,3,4,3,99]
|
||||||
|
run_test(prog, stdin=[7], expected_out=[1])
|
||||||
|
run_test(prog, stdin=[9], expected_out=[0])
|
||||||
|
|
||||||
|
# Here are some jump tests that take an input, then output 0 if the input
|
||||||
|
# was zero or 1 if the input was non-zero:
|
||||||
|
prog = [3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9] # using position mode
|
||||||
|
run_test(prog, stdin=[0], expected_out=[0])
|
||||||
|
run_test(prog, stdin=[4], expected_out=[1])
|
||||||
|
|
||||||
|
prog = [3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9] # using immediate mode
|
||||||
|
run_test(prog, stdin=[0], expected_out=[0])
|
||||||
|
run_test(prog, stdin=[4], expected_out=[1])
|
||||||
|
|
||||||
|
|
||||||
|
def test_day5_larger():
|
||||||
|
"""
|
||||||
|
The above example program uses an input instruction to ask for a single
|
||||||
|
number. The program will then output 999 if the input value is below 8,
|
||||||
|
output 1000 if the input value is equal to 8, or output 1001 if the input
|
||||||
|
value is greater than 8.
|
||||||
|
"""
|
||||||
|
prog = [3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
|
||||||
|
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,
|
||||||
|
999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99]
|
||||||
|
run_test(prog, stdin=[7], expected_out=[999])
|
||||||
|
run_test(prog, stdin=[8], expected_out=[1000])
|
||||||
|
run_test(prog, stdin=[9], expected_out=[1001])
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
# 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}"
|
||||||
|
|
Reference in New Issue
Block a user