Skip to content

Commit 29b46e6

Browse files
committed
Day 17 of AOC 2019
1 parent 842471b commit 29b46e6

File tree

5 files changed

+433
-2
lines changed

5 files changed

+433
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ This repository contains my solutions in Python3 for the [Advent of Code](https:
152152
| 24 ||| [🔗](./aoc_2020/day24) |
153153
| 25 ||| [🔗](./aoc_2020/day25) |
154154

155-
## \[2019] 30
155+
## \[2019] 32
156156

157157
| Day | Part 1 | Part 2 | Source link |
158158
| :-: | :----: | :----: | :-------------------------: |
@@ -172,7 +172,7 @@ This repository contains my solutions in Python3 for the [Advent of Code](https:
172172
| 14 ||| [🔗](./aoc_2019/day14) |
173173
| 15 ||| [🔗](./aoc_2019/day15) |
174174
| 16 ||| [🔗](./aoc_2019/day16) |
175-
| 17 | | | [🔗](./aoc_2019/day17) |
175+
| 17 | | | [🔗](./aoc_2019/day17) |
176176
| 18 ||| [🔗](./aoc_2019/day18) |
177177
| 19 ||| [🔗](./aoc_2019/day19) |
178178
| 20 ||| [🔗](./aoc_2019/day20) |

aoc_2019/day17/__init__.py

Whitespace-only changes.

aoc_2019/day17/intcode2.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import typing as t
2+
from dataclasses import dataclass
3+
from enum import Enum
4+
5+
type OpCode = t.Literal[99, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6+
7+
8+
class ParameterMode(Enum):
9+
POSITION = 0
10+
IMMEDIATE = 1
11+
RELATIVE = 2
12+
13+
14+
@dataclass
15+
class ProgramState:
16+
ip: int
17+
relative_base: int
18+
intcode: list[int]
19+
finished: bool
20+
21+
22+
INSTRUCTIONS_ARITY: dict[OpCode, int] = {
23+
99: 0, # Halt
24+
1: 3, # Add
25+
2: 3, # Mul
26+
3: 1, # Input
27+
4: 1, # Output
28+
5: 2, # Jump-if-true
29+
6: 2, # Jump-if-false
30+
7: 3, # Less than
31+
8: 3, # Equals
32+
9: 1, # Relative base
33+
}
34+
35+
36+
def parse_instruction(
37+
n: int,
38+
) -> tuple[OpCode, list[ParameterMode]]:
39+
opcode = n % 100
40+
n = n // 100
41+
42+
if opcode not in INSTRUCTIONS_ARITY:
43+
raise ValueError(f"[ERROR] Unknown opcode: {opcode}")
44+
45+
args: list[ParameterMode] = []
46+
47+
for _ in range(INSTRUCTIONS_ARITY[opcode]): # type: ignore
48+
args.append(ParameterMode(n % 10))
49+
n = n // 10
50+
51+
return (opcode, args) # type: ignore
52+
53+
54+
def allocate_memory(intcode: list[int], idx: int) -> None:
55+
if 0 <= idx < len(intcode):
56+
return
57+
size = (idx + 1) - len(intcode)
58+
59+
intcode.extend([0] * size)
60+
61+
62+
def read_value(
63+
intcode: list[int], idx: int, mode: ParameterMode, relative_base: int
64+
) -> int:
65+
allocate_memory(intcode, idx)
66+
address = intcode[idx]
67+
68+
if mode == ParameterMode.IMMEDIATE:
69+
return address
70+
elif mode == ParameterMode.POSITION:
71+
allocate_memory(intcode, address)
72+
return intcode[address]
73+
elif mode == ParameterMode.RELATIVE:
74+
allocate_memory(intcode, relative_base + address)
75+
return intcode[relative_base + address]
76+
77+
78+
def write_value(
79+
intcode: list[int], idx: int, value: int, mode: ParameterMode, relative_base: int
80+
) -> None:
81+
if mode == ParameterMode.IMMEDIATE:
82+
raise ValueError("Mode cannot be immediate for writing in memory")
83+
84+
allocate_memory(intcode, idx)
85+
address = intcode[idx]
86+
87+
if mode == ParameterMode.POSITION:
88+
allocate_memory(intcode, address)
89+
intcode[address] = value
90+
elif mode == ParameterMode.RELATIVE:
91+
allocate_memory(intcode, relative_base + address)
92+
intcode[relative_base + address] = value
93+
94+
95+
def execute_intcode(program: ProgramState, inputs: list[int]) -> list[int]:
96+
output: list[int] = []
97+
98+
while True:
99+
instruction = parse_instruction(program.intcode[program.ip])
100+
match instruction:
101+
case (99, []): # Halt
102+
break
103+
case (1, [mode1, mode2, mode3]): # Add
104+
write_value(
105+
program.intcode,
106+
program.ip + 3,
107+
read_value(
108+
program.intcode, program.ip + 1, mode1, program.relative_base
109+
)
110+
+ read_value(
111+
program.intcode, program.ip + 2, mode2, program.relative_base
112+
),
113+
mode3,
114+
program.relative_base,
115+
)
116+
program.ip += 4
117+
case (2, [mode1, mode2, mode3]): # Mul
118+
write_value(
119+
program.intcode,
120+
program.ip + 3,
121+
read_value(
122+
program.intcode, program.ip + 1, mode1, program.relative_base
123+
)
124+
* read_value(
125+
program.intcode, program.ip + 2, mode2, program.relative_base
126+
),
127+
mode3,
128+
program.relative_base,
129+
)
130+
program.ip += 4
131+
case (3, [mode1]): # Input
132+
if len(inputs) == 0:
133+
return output
134+
write_value(
135+
program.intcode,
136+
program.ip + 1,
137+
inputs.pop(0),
138+
mode1,
139+
program.relative_base,
140+
)
141+
program.ip += 2
142+
case (4, [mode1]): # Output
143+
output.append(
144+
read_value(
145+
program.intcode, program.ip + 1, mode1, program.relative_base
146+
)
147+
)
148+
program.ip += 2
149+
case (5, [mode1, mode2]): # Jump-if-true
150+
if (
151+
read_value(
152+
program.intcode, program.ip + 1, mode1, program.relative_base
153+
)
154+
!= 0
155+
):
156+
program.ip = read_value(
157+
program.intcode, program.ip + 2, mode2, program.relative_base
158+
)
159+
else:
160+
program.ip += 3
161+
case (6, [mode1, mode2]): # Jump-if-false
162+
if (
163+
read_value(
164+
program.intcode, program.ip + 1, mode1, program.relative_base
165+
)
166+
== 0
167+
):
168+
program.ip = read_value(
169+
program.intcode, program.ip + 2, mode2, program.relative_base
170+
)
171+
else:
172+
program.ip += 3
173+
case (7, [mode1, mode2, mode3]): # Less than
174+
if read_value(
175+
program.intcode, program.ip + 1, mode1, program.relative_base
176+
) < read_value(
177+
program.intcode, program.ip + 2, mode2, program.relative_base
178+
):
179+
write_value(
180+
program.intcode, program.ip + 3, 1, mode3, program.relative_base
181+
)
182+
else:
183+
write_value(
184+
program.intcode, program.ip + 3, 0, mode3, program.relative_base
185+
)
186+
program.ip += 4
187+
case (8, [mode1, mode2, mode3]): # Equals
188+
if read_value(
189+
program.intcode, program.ip + 1, mode1, program.relative_base
190+
) == read_value(
191+
program.intcode, program.ip + 2, mode2, program.relative_base
192+
):
193+
write_value(
194+
program.intcode, program.ip + 3, 1, mode3, program.relative_base
195+
)
196+
else:
197+
write_value(
198+
program.intcode, program.ip + 3, 0, mode3, program.relative_base
199+
)
200+
program.ip += 4
201+
case (9, [mode1]): # Relative
202+
program.relative_base += read_value(
203+
program.intcode, program.ip + 1, mode1, program.relative_base
204+
)
205+
program.ip += 2
206+
case _:
207+
raise ValueError(f"Unknown instruction: {instruction}")
208+
209+
program.finished = True
210+
return output

aoc_2019/day17/part1.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import sys
2+
import typing as t
3+
from pathlib import Path
4+
5+
sys.path.append(str(Path(__file__).resolve().parents[2]))
6+
7+
from aoc_2019.day17.intcode2 import ProgramState, execute_intcode
8+
9+
type Direction = t.Literal["^", "v", "<", ">"]
10+
11+
12+
class CoordsT(t.NamedTuple):
13+
x: int
14+
y: int
15+
16+
17+
DIRECTIONS: dict[Direction, CoordsT] = {
18+
"^": CoordsT(0, -1),
19+
"v": CoordsT(0, 1),
20+
"<": CoordsT(-1, 0),
21+
">": CoordsT(1, 0),
22+
}
23+
NEIGHBORS: dict[Direction, tuple[Direction, Direction]] = {
24+
"^": ("<", ">"),
25+
"v": ("<", ">"),
26+
"<": ("^", "v"),
27+
">": ("^", "v"),
28+
}
29+
ASCII_DIRECTIONS = [ord("^"), ord("v"), ord("<"), ord(">")]
30+
31+
intcode = [int(c) for c in Path(Path(__file__).parent, "input").read_text().split(",")]
32+
program = ProgramState(0, 0, intcode, False)
33+
output = execute_intcode(program, [])
34+
35+
scaffolds: set[CoordsT] = set()
36+
robot_pos = CoordsT(0, 0)
37+
direction: Direction = "^"
38+
39+
x = 0
40+
y = 0
41+
for code in output:
42+
if code == 35:
43+
scaffolds.add(CoordsT(x, y))
44+
elif code == 10:
45+
y += 1
46+
x = 0
47+
continue
48+
elif code in ASCII_DIRECTIONS:
49+
robot_pos = CoordsT(x, y)
50+
scaffolds.add(robot_pos)
51+
direction = chr(code) # type: ignore
52+
x += 1
53+
54+
visited: set[CoordsT] = {robot_pos}
55+
intersections: set[CoordsT] = set()
56+
57+
while True:
58+
vec = DIRECTIONS[direction]
59+
c = CoordsT(robot_pos.x + vec.x, robot_pos.y + vec.y)
60+
61+
if c in scaffolds:
62+
if c in visited:
63+
intersections.add(c)
64+
visited.add(c)
65+
robot_pos = c
66+
else:
67+
dir1, dir2 = NEIGHBORS[direction]
68+
nvec1, nvec2 = DIRECTIONS[dir1], DIRECTIONS[dir2]
69+
c1 = CoordsT(robot_pos.x + nvec1.x, robot_pos.y + nvec1.y)
70+
c2 = CoordsT(robot_pos.x + nvec2.x, robot_pos.y + nvec2.y)
71+
72+
if c1 in scaffolds:
73+
direction = dir1
74+
elif c2 in scaffolds:
75+
direction = dir2
76+
else:
77+
break
78+
79+
r = sum(i.x * i.y for i in intersections)
80+
81+
print(f"Result: {r}")

0 commit comments

Comments
 (0)