-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
191 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,41 @@ | ||
from copy import deepcopy | ||
import sys | ||
from copy import deepcopy | ||
|
||
sys.path.append("utils") | ||
|
||
from intcode import get_program, run_program | ||
|
||
from intcode import IntCode | ||
|
||
def amplify(inputs: list[str], phases: list[int], input: int) -> int: | ||
program = get_program(inputs) | ||
|
||
def amplify(prog: list[int], phases: list[int], input: int) -> int: | ||
output = input | ||
for amp in range(len(phases)): | ||
mem = deepcopy(program) | ||
mem = deepcopy(prog) | ||
io_ip = [phases[amp], output] | ||
io_op = [] | ||
run_program(mem, io_ip, io_op) | ||
output = io_op[0] | ||
state = IntCode(mem, 0, io_ip).run() | ||
output = state.output | ||
return output | ||
|
||
|
||
def amplify_with_feedback(prog: list[int], phases: list[int], input: int) -> int: | ||
mem_arr = [deepcopy(prog) for _ in range(len(phases))] | ||
inputs_arr = [[phase] for phase in phases] | ||
ip_arr = [0] * len(phases) | ||
|
||
while True: | ||
halt = False | ||
for idx, (inputs, mem, ip) in enumerate(zip(inputs_arr, mem_arr, ip_arr)): | ||
inputs.append(input) | ||
|
||
state = IntCode(mem, ip, inputs).run() | ||
ip_arr[idx] = state.ip | ||
|
||
if state.halted: | ||
halt = True | ||
continue | ||
|
||
input = state.output | ||
|
||
if halt: | ||
break | ||
|
||
return input |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,25 @@ | ||
import sys | ||
from itertools import permutations | ||
|
||
from helpers import amplify | ||
sys.path.append("utils") | ||
|
||
from helpers import amplify, amplify_with_feedback | ||
from intcode import get_program | ||
|
||
|
||
def part1(inputs: list[str]) -> int: | ||
prog = get_program(inputs) | ||
|
||
max_output = 0 | ||
for phases in list(permutations(range(5), 5)): | ||
max_output = max(max_output, amplify(inputs, list(phases), 0)) | ||
max_output = max(max_output, amplify(prog, list(phases), 0)) | ||
return max_output | ||
|
||
|
||
def part2(inputs: list[str]) -> int: | ||
prog = get_program(inputs) | ||
|
||
max_output = 0 | ||
for phases in list(permutations(range(9), 5)): | ||
max_output = max(max_output, amplify(inputs, list(phases), 0)) | ||
for phases in list(permutations(range(5, 10), 5)): | ||
max_output = max(max_output, amplify_with_feedback(prog, list(phases), 0)) | ||
return max_output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,132 @@ | ||
from enum import Enum | ||
from typing import Tuple | ||
|
||
|
||
class OpCode(Enum): | ||
ADD = 1 | ||
MUL = 2 | ||
INPUT = 3 | ||
OUTPUT = 4 | ||
JUMP_IF_TRUE = 5 | ||
JUMP_IF_FALSE = 6 | ||
LESS_THAN = 7 | ||
EQUALS = 8 | ||
HALT = 99 | ||
|
||
def num_params(self): | ||
return { | ||
OpCode.ADD: 3, | ||
OpCode.MUL: 3, | ||
OpCode.INPUT: 1, | ||
OpCode.OUTPUT: 1, | ||
OpCode.JUMP_IF_TRUE: 2, | ||
OpCode.JUMP_IF_FALSE: 2, | ||
OpCode.LESS_THAN: 3, | ||
OpCode.EQUALS: 3, | ||
OpCode.HALT: 0, | ||
}[self] | ||
|
||
|
||
def get_program(inputs: list[str]) -> list[int]: | ||
return list(map(lambda x: int(x), inputs[0].split(","))) | ||
|
||
|
||
def parse_instruction(instr: int) -> Tuple[int, int, int, int]: | ||
instr = str(instr).zfill(5) | ||
op_code = int(instr[-2:]) | ||
m_3, m_2, m_1 = map(int, instr[:3]) | ||
return m_3, m_2, m_1, op_code | ||
|
||
|
||
def run_program( | ||
mem: list[int], io_ip: list[int] = [], io_op: list[int] = [] | ||
) -> list[int]: | ||
ip = 0 | ||
|
||
def get_val(addr: int, mode: int) -> int: | ||
return mem[addr] if mode else mem[mem[addr]] | ||
|
||
num_params = {1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 99: 0} | ||
|
||
while True: | ||
_, m_2, m_1, op_code = parse_instruction(mem[ip]) | ||
|
||
jump = False | ||
|
||
if op_code == 1: | ||
mem[mem[ip + 3]] = get_val(ip + 1, m_1) + get_val(ip + 2, m_2) | ||
elif op_code == 2: | ||
mem[mem[ip + 3]] = get_val(ip + 1, m_1) * get_val(ip + 2, m_2) | ||
elif op_code == 3: | ||
mem[mem[ip + 1]] = io_ip.pop(0) | ||
elif op_code == 4: | ||
io_op.append(get_val(ip + 1, m_1)) | ||
elif op_code == 5: | ||
if jump := get_val(ip + 1, m_1) > 0: | ||
ip = get_val(ip + 2, m_2) | ||
elif op_code == 6: | ||
if jump := get_val(ip + 1, m_1) == 0: | ||
ip = get_val(ip + 2, m_2) | ||
elif op_code == 7: | ||
mem[mem[ip + 3]] = 1 if get_val(ip + 1, m_1) < get_val(ip + 2, m_2) else 0 | ||
elif op_code == 8: | ||
mem[mem[ip + 3]] = 1 if get_val(ip + 1, m_1) == get_val(ip + 2, m_2) else 0 | ||
elif op_code == 99: | ||
break | ||
else: | ||
raise Exception(f"Unknown opcode: {op_code}") | ||
|
||
if not jump: | ||
ip += num_params[op_code] + 1 | ||
|
||
return mem | ||
return list(map(int, inputs[0].split(","))) | ||
|
||
|
||
class ProgramState: | ||
def __init__(self, ip: int, mem: list[int], output: int, halted: bool) -> None: | ||
self.ip = ip | ||
self.mem = mem | ||
self.output = output | ||
self.halted = halted | ||
|
||
|
||
class IntCode: | ||
def __init__(self, mem: list[int], ip: int, inputs: list[int] = []) -> None: | ||
self.mem = mem | ||
self.ip = ip | ||
self.inputs = inputs | ||
|
||
def run( | ||
self, | ||
) -> ProgramState: | ||
while True: | ||
op_code, m_1, m_2, _ = self.__get_next_instruction() | ||
|
||
if op_code == OpCode.ADD: | ||
self.__add(m_1, m_2) | ||
elif op_code == OpCode.MUL: | ||
self.__mul(m_1, m_2) | ||
elif op_code == OpCode.INPUT: | ||
self.__input() | ||
elif op_code == OpCode.OUTPUT: | ||
return self.__output(op_code, m_1) | ||
elif op_code == OpCode.JUMP_IF_TRUE: | ||
if self.__jump_if_true(m_1, m_2): | ||
continue | ||
elif op_code == OpCode.JUMP_IF_FALSE: | ||
if self.__jump_if_false(m_1, m_2): | ||
continue | ||
elif op_code == OpCode.LESS_THAN: | ||
self.__less_than(m_1, m_2) | ||
elif op_code == OpCode.EQUALS: | ||
self.__equals(m_1, m_2) | ||
elif op_code == OpCode.HALT: | ||
return self.__halt() | ||
else: | ||
raise Exception(f"Unknown opcode: {op_code}") | ||
|
||
self.ip += op_code.num_params() + 1 | ||
|
||
def __get_next_instruction(self) -> Tuple[OpCode, int, int, int]: | ||
instr = str(self.mem[self.ip]).zfill(5) | ||
op_code = OpCode(int(instr[-2:])) | ||
m_3, m_2, m_1 = map(int, instr[:3]) | ||
return op_code, m_1, m_2, m_3 | ||
|
||
def __get_val(self, addr: int, mode: int) -> int: | ||
return self.mem[addr] if mode else self.mem[self.mem[addr]] | ||
|
||
def __add(self, m_1: int, m_2: int) -> None: | ||
self.mem[self.mem[self.ip + 3]] = self.__get_val( | ||
self.ip + 1, m_1 | ||
) + self.__get_val(self.ip + 2, m_2) | ||
|
||
def __mul(self, m_1: int, m_2: int) -> None: | ||
self.mem[self.mem[self.ip + 3]] = self.__get_val( | ||
self.ip + 1, m_1 | ||
) * self.__get_val(self.ip + 2, m_2) | ||
|
||
def __input(self) -> None: | ||
self.mem[self.mem[self.ip + 1]] = self.inputs.pop(0) | ||
|
||
def __output(self, op_code: OpCode, m_1: int) -> int: | ||
io_op = self.__get_val(self.ip + 1, m_1) | ||
return ProgramState(self.ip + op_code.num_params() + 1, self.mem, io_op, False) | ||
|
||
def __jump_if_true(self, m_1: int, m_2: int) -> bool: | ||
if self.__get_val(self.ip + 1, m_1) > 0: | ||
self.ip = self.__get_val(self.ip + 2, m_2) | ||
return True | ||
return False | ||
|
||
def __jump_if_false(self, m_1: int, m_2: int) -> bool: | ||
if self.__get_val(self.ip + 1, m_1) == 0: | ||
self.ip = self.__get_val(self.ip + 2, m_2) | ||
return True | ||
return False | ||
|
||
def __less_than(self, m_1: int, m_2: int) -> None: | ||
self.mem[self.mem[self.ip + 3]] = ( | ||
1 | ||
if self.__get_val(self.ip + 1, m_1) < self.__get_val(self.ip + 2, m_2) | ||
else 0 | ||
) | ||
|
||
def __equals(self, m_1: int, m_2: int) -> None: | ||
self.mem[self.mem[self.ip + 3]] = ( | ||
1 | ||
if self.__get_val(self.ip + 1, m_1) == self.__get_val(self.ip + 2, m_2) | ||
else 0 | ||
) | ||
|
||
def __halt(self) -> None: | ||
return ProgramState(self.ip, self.mem, 0, True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
from intcode import get_program, parse_instruction | ||
from intcode import get_program | ||
|
||
|
||
class TestIntCode: | ||
def test_get_program(self): | ||
assert get_program(["1002, 4, 3, 4, 33"]) == [1002, 4, 3, 4, 33] | ||
|
||
def test_parse_instruction(self): | ||
assert parse_instruction(1002) == (0, 1, 0, 2) | ||
assert parse_instruction(1102) == (0, 1, 1, 2) | ||
assert parse_instruction(1182) == (0, 1, 1, 82) | ||
# def test_parse_instruction(self): | ||
# assert parse_instruction(1002) == (0, 1, 0, 2) | ||
# assert parse_instruction(1102) == (0, 1, 1, 2) | ||
# assert parse_instruction(1182) == (0, 1, 1, 82) |