Skip to content

Commit 13e1326

Browse files
committed
Completed Day 5
1 parent ff8f2af commit 13e1326

File tree

3 files changed

+246
-6
lines changed

3 files changed

+246
-6
lines changed

aoc/year2022/day05.py

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#!env python
2+
"""
3+
--- Day 5: Supply Stacks ---
4+
The expedition can depart as soon as the final supplies have been unloaded
5+
from the ships. Supplies are stored in stacks of marked crates, but because
6+
the needed supplies are buried under many other crates, the crates need to
7+
be rearranged.
8+
9+
The ship has a giant cargo crane capable of moving crates between stacks.
10+
To ensure none of the crates get crushed or fall over, the crane operator
11+
will rearrange them in a series of carefully-planned steps. After the
12+
crates are rearranged, the desired crates will be at the top of each stack.
13+
14+
The Elves don't want to interrupt the crane operator during this delicate
15+
procedure, but they forgot to ask her which crate will end up where, and
16+
they want to be ready to unload them as soon as possible so they can
17+
embark.
18+
19+
They do, however, have a drawing of the starting stacks of crates and the
20+
rearrangement procedure (your puzzle input). For example:
21+
22+
[D]
23+
[N] [C]
24+
[Z] [M] [P]
25+
1 2 3
26+
27+
move 1 from 2 to 1
28+
move 3 from 1 to 3
29+
move 2 from 2 to 1
30+
move 1 from 1 to 2
31+
32+
In this example, there are three stacks of crates. Stack 1 contains two
33+
crates: crate Z is on the bottom, and crate N is on top. Stack 2 contains
34+
three crates; from bottom to top, they are crates M, C, and D. Finally,
35+
stack 3 contains a single crate, P.
36+
37+
Then, the rearrangement procedure is given. In each step of the procedure,
38+
a quantity of crates is moved from one stack to a different stack. In the
39+
first step of the above rearrangement procedure, one crate is moved from
40+
stack 2 to stack 1, resulting in this configuration:
41+
42+
[D]
43+
[N] [C]
44+
[Z] [M] [P]
45+
1 2 3
46+
47+
In the second step, three crates are moved from stack 1 to stack 3. Crates
48+
are moved one at a time, so the first crate to be moved (D) ends up below
49+
the second and third crates:
50+
51+
[Z]
52+
[N]
53+
[C] [D]
54+
[M] [P]
55+
1 2 3
56+
57+
Then, both crates are moved from stack 2 to stack 1. Again, because crates
58+
are moved one at a time, crate C ends up below crate M:
59+
60+
[Z]
61+
[N]
62+
[M] [D]
63+
[C] [P]
64+
1 2 3
65+
66+
Finally, one crate is moved from stack 1 to stack 2:
67+
68+
[Z]
69+
[N]
70+
[D]
71+
[C] [M] [P]
72+
1 2 3
73+
74+
The Elves just need to know which crate will end up on top of each stack;
75+
in this example, the top crates are C in stack 1, M in stack 2, and Z in
76+
stack 3, so you should combine these together and give the Elves the message CMZ.
77+
78+
After the rearrangement procedure completes, what crate ends up on top of
79+
each stack?
80+
81+
82+
--- Part Two ---
83+
As you watch the crane operator expertly rearrange the crates, you notice
84+
the process isn't following your prediction.
85+
86+
Some mud was covering the writing on the side of the crane, and you quickly
87+
wipe it away. The crane isn't a CrateMover 9000 - it's a CrateMover 9001.
88+
89+
The CrateMover 9001 is notable for many new and exciting features: air
90+
conditioning, leather seats, an extra cup holder, and the ability to pick up
91+
and move multiple crates at once.
92+
93+
Again considering the example above, the crates begin in the same
94+
configuration:
95+
96+
[D]
97+
[N] [C]
98+
[Z] [M] [P]
99+
1 2 3
100+
101+
Moving a single crate from stack 2 to stack 1 behaves the same as before:
102+
103+
[D]
104+
[N] [C]
105+
[Z] [M] [P]
106+
1 2 3
107+
108+
However, the action of moving three crates from stack 1 to stack 3 means
109+
that those three moved crates stay in the same order, resulting in this new
110+
configuration:
111+
112+
[D]
113+
[N]
114+
[C] [Z]
115+
[M] [P]
116+
1 2 3
117+
118+
Next, as both crates are moved from stack 2 to stack 1, they retain their
119+
order as well:
120+
121+
[D]
122+
[N]
123+
[C] [Z]
124+
[M] [P]
125+
1 2 3
126+
127+
Finally, a single crate is still moved from stack 1 to stack 2, but now
128+
it's crate C that gets moved:
129+
130+
[D]
131+
[N]
132+
[Z]
133+
[M] [C] [P]
134+
1 2 3
135+
136+
In this example, the CrateMover 9001 has put the crates in a totally
137+
different order: MCD.
138+
139+
Before the rearrangement process finishes, update your simulation so that
140+
the Elves know where they should stand to be ready to unload the final
141+
supplies. After the rearrangement procedure completes, what crate ends up
142+
on top of each stack?
143+
"""
144+
import sys
145+
from collections import deque
146+
from typing import Dict, Literal, Optional
147+
148+
import pytest
149+
from parse import Result, compile
150+
151+
152+
@pytest.fixture
153+
def example_data():
154+
return {
155+
"input": """
156+
[D]
157+
[N] [C]
158+
[Z] [M] [P]
159+
1 2 3
160+
161+
move 1 from 2 to 1
162+
move 3 from 1 to 3
163+
move 2 from 2 to 1
164+
move 1 from 1 to 2
165+
""",
166+
"a": "CMZ",
167+
"b": "MCD",
168+
}
169+
170+
171+
def test_solve_a(example_data):
172+
if example_data.get("a") is not None:
173+
assert solve(input=example_data["input"], part="a") == example_data["a"]
174+
175+
176+
def test_solve_b(example_data):
177+
if example_data.get("b") is not None:
178+
assert solve(input=example_data["input"], part="b") == example_data["b"]
179+
180+
181+
def solve(input: str, part: Literal["a", "b"]) -> Optional[str]:
182+
mode = "crates"
183+
unnumbered_stacks: Dict[int, deque[str]] = {}
184+
stacks: Dict[str, deque[str]] = {}
185+
parser = compile("move {:d} from {} to {}")
186+
for line in input.splitlines():
187+
if line == "" and len(stacks) and mode == "crates":
188+
mode = "instructions"
189+
continue
190+
if mode == "crates":
191+
for stack_number in range(0, len(line), 4):
192+
crate = line[stack_number : stack_number + 3]
193+
if crate == " ":
194+
# Empty crate
195+
continue
196+
if "[" in line:
197+
# This is a crate specification
198+
if unnumbered_stacks.get(stack_number, None) is None:
199+
unnumbered_stacks[stack_number] = deque()
200+
unnumbered_stacks[stack_number].appendleft(crate[1])
201+
else:
202+
# This is the list of crate "names"
203+
stacks[crate[1]] = unnumbered_stacks[stack_number]
204+
elif mode == "instructions":
205+
r = parser.parse(line)
206+
assert type(r) is Result
207+
size_before = len(stacks[str(r[2])])
208+
assert len(stacks[r[1]]) >= r[0]
209+
if part == "a":
210+
# Grab boxes and put them directly on the target
211+
for _ in range(r[0]):
212+
stacks[str(r[2])].append(stacks[str(r[1])].pop())
213+
else:
214+
# Grab boxes into a holding group, then
215+
# Put them in order onto the target
216+
grab: deque[str] = deque()
217+
for _ in range(r[0]):
218+
grab.appendleft(stacks[str(r[1])].pop())
219+
while len(grab):
220+
stacks[r[2]].append(grab.popleft())
221+
assert len(stacks[str(r[2])]) == size_before + r[0]
222+
223+
return "".join([x.pop() for x in stacks.values()])
224+
225+
226+
if __name__ == "__main__":
227+
sys.exit(pytest.main([__file__]))

poetry.lock

Lines changed: 18 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ numpy = ">=1.21.4"
3939
pandas = ">=1.3.4"
4040
parse = "*"
4141
rich = ">=10"
42+
more-itertools = "^9.0.0"
4243

4344
[tool.poetry.group.dev.dependencies]
4445
flake8 = "^6.0.0"

0 commit comments

Comments
 (0)