Oh boy… parsing is not my favorite pasttime.
This time part 2 was a simpler version of part 1. That’s new.
Python soluton
def read_file(file: TextIO):
"""Read lines from the `file`."""
lines = (line.rstrip() for line in file)
stacks = [[] for _ in range(9)]
for line in lines:
if not line:
break
for i in range(0, len(line), 4):
chunk = line[i:i + 4]
if re.match(r'\[[A-Z]\]', chunk):
stacks[i // 4].append(chunk[1])
for stack in stacks:
stack.reverse()
moves = []
for line in lines:
mat = re.match(r'move (\d+) from (\d) to (\d)', line)
assert mat
count, src, dst = (int(s) for s in mat.groups())
moves.append((count, src, dst))
return [stacks, moves]
def part1(file: TextIO) -> str:
"""Solve the first part of the puzzle."""
stacks, moves = read_file(file)
for count, src_idx1, dst_idx1 in moves:
assert src_idx1 != dst_idx1
src = stacks[src_idx1 - 1]
dst = stacks[dst_idx1 - 1]
dst.extend(src[-count:][::-1])
del src[-count:]
return ''.join(stack[-1] for stack in stacks if stack)
def test_part1() -> None:
"""Use the example from the instructions as a test for the first half."""
file = io.StringIO(test_data)
assert part1(file) == 'CMZ'
def part2(file: TextIO) -> str:
"""Solve the second part of the puzzle."""
stacks, moves = read_file(file)
for count, src, dst in moves:
assert src != dst
src = stacks[src - 1]
dst = stacks[dst - 1]
crates = src[-count:]
dst.extend(crates)
del src[-count:]
return ''.join(stack[-1] for stack in stacks if stack)
def test_part2() -> None:
"""Use the example from the instructions as a test for the second half."""
file = io.StringIO(test_data)
assert part2(file) == 'MCD'
I briefly considered regex … then opted to not bother. I was surprised how similar parts 1 and 2 were; I was expecting to need to change more than just that.
Python Solution
def build_stacks(self, setup: list[str]) -> list[list[str]]:
"""Build the initial stacks from input."""
stack_count = len(setup[-1].split())
width = stack_count * 4
stack = [[] for _ in range(stack_count)]
for line in setup[-2::-1]:
line = line.ljust(width)
for i in range(stack_count):
crate = line[1 + i * 4]
if crate != " ":
stack[i].append(crate)
return stack
def part1(self, parsed_input: InputType) -> str:
"""Return crates after moving them with a CrateMover 9000."""
setup_block, moves = parsed_input
stack = self.build_stacks(setup_block)
for move_count, src, dst in moves:
for _ in range(move_count):
stack[dst - 1].append(stack[src - 1].pop())
return "".join(s.pop() for s in stack)
def part2(self, parsed_input: InputType) -> str:
"""Return crates after moving them with a CrateMover 9001."""
setup_block, moves = parsed_input
stack = self.build_stacks(setup_block)
for move_count, src, dst in moves:
stack[dst - 1].extend(stack[src - 1][-move_count:])
del stack[src - 1][-move_count:]
return "".join(s.pop() for s in stack)
def input_parser(self, puzzle_input: str) -> InputType:
"""Parse the input data."""
setup, moves = puzzle_input.split("\n\n")
return (
setup.splitlines(),
[
tuple(int(i) for i in line.split() if i.isdigit())
for line in moves.splitlines()
]
)
I also feel the real challenge on this one was the parsing of the stacks
I considered using regexes for the stack parsing part, but I realized I probably was trying to make my solution parse a generic version of the input format with no need. The regex was becoming too big and too complicated and it was literally the meme “when you try to solve a problem with regexes, now you have 2 problems”. The input only had <10 stacks, so stack numbers only took 1 digit to represent and items only had 1 letter, so all items were in a predictable position. Ultimately I decided to take advantage of that and just looked for specific indexes directly in the matrix of characters.
If I have there is time, there are some simplifications I want to try, but will leave those for later.
Right? I was also expecting a big twist on this one. Still enjoyed the problem, and I imagine that depending on the solution, part 2 can still require significant changes for some people, although I imagine for most it won’t.
Note, strings in Go (and Python and other languages) are immutable.
In languages were strings are immutable, string concatenations are relatively expensive and should generally not be done in loops. Arrays/lists are much more efficient.
The suspense is driving me crazy. When are they going to drop the insidious problems? Last year this time, it felt like we were already to problems where part 2 would take forever if using brute force.