zerosleeps

Since 2010

Advent of Code 2022 day 5

I’m jumping around a little bit, but here’s my Python solution for Advent of Code 2022 day 5. I had quite a few false starts trying to parse the input for this one - I knew I wanted to transpose the stacks, but trying to extract the stack IDs from their delimiters while managing potential white-space caused some head scratching. Some list popping and pushing from there.

import re
import unittest
import utils


def extract_stacks_from_raw_input(raw_input):
    return [line.rstrip() for line in raw_input if "[" in line]


def extract_steps_from_raw_input(raw_input):
    return [line.rstrip() for line in raw_input if "move" in line]


def parse_raw_stacks(raw_stacks: list[str]):
    # Some of this is simply to work around the fact that I've set my text editor
    # to strip trailing whitespace from all lines in a file on save
    stacks_width = max([len(line) for line in raw_stacks])
    transposed_raw_stacks = [
        [row.ljust(stacks_width)[i] for row in raw_stacks] for i in range(stacks_width)
    ]
    dirty_stacks = [
        stack
        for stack in transposed_raw_stacks
        if any([char.isupper() for char in stack])
    ]
    return [list(reversed([c for c in stack if c.isupper()])) for stack in dirty_stacks]


def extract_data_from_step(step):
    return [
        int(g)
        for g in re.match(r"^move (\d+) from (\d+) to (\d+)$", step).group(1, 2, 3)
    ]


def parse_raw_steps(raw_steps):
    return [extract_data_from_step(step) for step in raw_steps]


def part_one(raw_input):
    stacks = parse_raw_stacks(extract_stacks_from_raw_input(raw_input))
    steps = parse_raw_steps(extract_steps_from_raw_input(raw_input))
    for step in steps:
        for _ in range(step[0]):
            stacks[step[2] - 1] += stacks[step[1] - 1].pop()
    return "".join([stack[-1] for stack in stacks])


def part_two(raw_input):
    stacks = parse_raw_stacks(extract_stacks_from_raw_input(raw_input))
    steps = parse_raw_steps(extract_steps_from_raw_input(raw_input))
    for step in steps:
        stacks[step[2] - 1] += stacks[step[1] - 1][-step[0] :]
        del stacks[step[1] - 1][-step[0] :]
    return "".join([stack[-1] for stack in stacks])


class TestExamples(unittest.TestCase):
    def setUp(self):
        self.raw_input = [
            "    [D]    ",
            "[N] [C]    ",
            "[Z] [M] [P]",
            " 1   2   3 ",
            "",
            "move 1 from 2 to 1",
            "move 3 from 1 to 3",
            "move 2 from 2 to 1",
            "move 1 from 1 to 2",
        ]

    def test_part_one(self):
        self.assertEqual(part_one(self.raw_input), "CMZ")

    def test_part_two(self):
        self.assertEqual(part_two(self.raw_input), "MCD")


if __name__ == "__main__":
    raw_input = utils.get_raw_input("day_05_input.txt", strip=False)
    print(f"Part one: {part_one(raw_input)}")
    print(f"Part two: {part_two(raw_input)}")