Skip to content

Commit 3514b73

Browse files
committed
2019 day 7 part 1
1 parent c7bfff5 commit 3514b73

3 files changed

Lines changed: 164 additions & 0 deletions

File tree

2019/python/data/day07/input.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3,8,1001,8,10,8,105,1,0,0,21,38,55,64,81,106,187,268,349,430,99999,3,9,101,2,9,9,1002,9,2,9,101,5,9,9,4,9,99,3,9,102,2,9,9,101,3,9,9,1002,9,4,9,4,9,99,3,9,102,2,9,9,4,9,99,3,9,1002,9,5,9,1001,9,4,9,102,4,9,9,4,9,99,3,9,102,2,9,9,1001,9,5,9,102,3,9,9,1001,9,4,9,102,5,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,99,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,99

2019/python/src/aoc2019/day07.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import dataclasses
2+
import itertools
3+
from pathlib import Path
4+
from typing import Self
5+
6+
7+
def part1(input: Path) -> int:
8+
code = [int(n) for n in input.read_text().split(",")]
9+
largest_signal = 0
10+
for phases in itertools.permutations(range(5)):
11+
amps = [IntcodeVM(code.copy(), [phase]) for phase in phases]
12+
amps[0].push_input(0)
13+
amps[1].push_input(amps[0].run_until_halted().outputs[-1])
14+
amps[2].push_input(amps[1].run_until_halted().outputs[-1])
15+
amps[3].push_input(amps[2].run_until_halted().outputs[-1])
16+
amps[4].push_input(amps[3].run_until_halted().outputs[-1])
17+
if (signal := amps[4].run_until_halted().outputs[-1]) > largest_signal:
18+
largest_signal = signal
19+
return largest_signal
20+
21+
22+
# def part2(input: Path) -> int:
23+
# ...
24+
25+
26+
@dataclasses.dataclass(frozen=True)
27+
class InputRequired: ...
28+
29+
30+
@dataclasses.dataclass(frozen=True)
31+
class ProgramHalted: ...
32+
33+
34+
class InputRequiredError(Exception): ...
35+
36+
37+
class IntcodeVM:
38+
def __init__(self, code: list[int], inputs: list[int]) -> None:
39+
self._code = code.copy()
40+
self._inputs = inputs.copy()
41+
self._outputs: list[int] = []
42+
self._ip: int = 0
43+
44+
def push_input(self, value: int) -> None:
45+
self._inputs.append(value)
46+
47+
@property
48+
def outputs(self) -> tuple[int, ...]:
49+
return tuple(self._outputs)
50+
51+
def run_until_next_output(self) -> int | InputRequired | ProgramHalted:
52+
while True:
53+
opcode, parameter_modes = self._parse_opcode(self._code[self._ip])
54+
match opcode:
55+
case 1:
56+
self._set(
57+
self._ip + 3,
58+
parameter_modes[2],
59+
self._get(self._ip + 1, parameter_modes[0])
60+
+ self._get(self._ip + 2, parameter_modes[1]),
61+
)
62+
self._ip += 4
63+
case 2:
64+
self._set(
65+
self._ip + 3,
66+
parameter_modes[2],
67+
self._get(self._ip + 1, parameter_modes[0])
68+
* self._get(self._ip + 2, parameter_modes[1]),
69+
)
70+
self._ip += 4
71+
case 3:
72+
if not self._inputs:
73+
return InputRequired()
74+
self._set(self._ip + 1, parameter_modes[0], self._inputs.pop(0))
75+
self._ip += 2
76+
case 4:
77+
output = self._get(self._ip + 1, parameter_modes[0])
78+
self._outputs.append(output)
79+
self._ip += 2
80+
return output
81+
case 5:
82+
jump = self._get(self._ip + 1, parameter_modes[0]) != 0
83+
if jump:
84+
self._ip = self._get(self._ip + 2, parameter_modes[1])
85+
else:
86+
self._ip += 3
87+
case 6:
88+
jump = self._get(self._ip + 1, parameter_modes[0]) == 0
89+
if jump:
90+
self._ip = self._get(self._ip + 2, parameter_modes[1])
91+
else:
92+
self._ip += 3
93+
case 7:
94+
self._set(
95+
self._ip + 3,
96+
parameter_modes[2],
97+
int(
98+
self._get(self._ip + 1, parameter_modes[0])
99+
< self._get(self._ip + 2, parameter_modes[1])
100+
),
101+
)
102+
self._ip += 4
103+
case 8:
104+
self._set(
105+
self._ip + 3,
106+
parameter_modes[2],
107+
int(
108+
self._get(self._ip + 1, parameter_modes[0])
109+
== self._get(self._ip + 2, parameter_modes[1])
110+
),
111+
)
112+
self._ip += 4
113+
case 99:
114+
return ProgramHalted()
115+
case _:
116+
raise ValueError(f"Invalid opcode {opcode}")
117+
118+
def run_until_halted(self) -> Self:
119+
while True:
120+
output = self.run_until_next_output()
121+
match output:
122+
case InputRequired():
123+
raise InputRequiredError
124+
case ProgramHalted():
125+
return self
126+
case int():
127+
...
128+
129+
@staticmethod
130+
def _parse_opcode(opcode: int) -> tuple[int, tuple[int, ...]]:
131+
s = str(opcode).zfill(5)
132+
return int(s[3:]), (int(s[2]), int(s[1]), int(s[0]))
133+
134+
def _get(self, parameter_ip: int, parameter_mode: int) -> int:
135+
match parameter_mode:
136+
case 0:
137+
return self._code[self._code[parameter_ip]]
138+
case 1:
139+
return self._code[parameter_ip]
140+
case _:
141+
raise ValueError(f"Invalid parameter mode {parameter_mode}")
142+
143+
def _set(self, parameter_ip: int, parameter_mode: int, value: int) -> None:
144+
match parameter_mode:
145+
case 0:
146+
self._code[self._code[parameter_ip]] = value
147+
case 1:
148+
self._code[parameter_ip] = value
149+
case _:
150+
raise ValueError(f"Invalid parameter mode {parameter_mode}")

2019/python/tests/test_day07.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from pathlib import Path
2+
3+
from aoc2019 import day07
4+
5+
DATA_DIR = Path(__file__).parent.parent / "data/day07"
6+
7+
8+
def test_part1():
9+
assert day07.part1(DATA_DIR / "input.txt") == 117312
10+
11+
12+
# def test_part2():
13+
# assert day07.part2(DATA_DIR / "input.txt") == 1336480

0 commit comments

Comments
 (0)