Skip to content

Commit 9b839ee

Browse files
committed
2019 day 7 part 1
1 parent c7bfff5 commit 9b839ee

3 files changed

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