Skip to content

Commit 7b3e09c

Browse files
committed
2019 day 7 part 1
1 parent c7bfff5 commit 7b3e09c

3 files changed

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