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)}")

Advent of Code 2022 day 6

More test code than problem-solving code today. Nothing wrong with that.

I’ll come back to day 5 - coming up with a nice way of parsing the input is taking me a while.

from unittest import TestCase
from utils import get_raw_input_as_str


def distinct_run_index(input, marker_size):
    for i in range(len(input) - marker_size - 1):
        if len(set(input[i : i + marker_size])) == marker_size:
            return i + marker_size


class TestExamples(TestCase):
    def setUp(self):
        self.example_one_input = "mjqjpqmgbljsphdztnvjfqwrcgsmlb"
        self.example_two_input = "bvwbjplbgvbhsrlpgdmjqwftvncz"
        self.example_three_input = "nppdvjthqldpwncqszvftbrmjlhg"
        self.example_four_input = "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg"
        self.example_five_input = "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"

    def test_part_one_example_one(self):
        self.assertEqual(distinct_run_index(self.example_one_input, 4), 7)

    def test_part_one_example_two(self):
        self.assertEqual(distinct_run_index(self.example_two_input, 4), 5)

    def test_part_one_example_three(self):
        self.assertEqual(distinct_run_index(self.example_three_input, 4), 6)

    def test_part_one_example_four(self):
        self.assertEqual(distinct_run_index(self.example_four_input, 4), 10)

    def test_part_one_example_five(self):
        self.assertEqual(distinct_run_index(self.example_five_input, 4), 11)

    def test_part_two_example_one(self):
        self.assertEqual(distinct_run_index(self.example_one_input, 14), 19)

    def test_part_two_example_two(self):
        self.assertEqual(distinct_run_index(self.example_two_input, 14), 23)

    def test_part_two_example_three(self):
        self.assertEqual(distinct_run_index(self.example_three_input, 14), 23)

    def test_part_two_example_four(self):
        self.assertEqual(distinct_run_index(self.example_four_input, 14), 29)

    def test_part_two_example_five(self):
        self.assertEqual(distinct_run_index(self.example_five_input, 14), 26)


if __name__ == "__main__":
    input = get_raw_input_as_str("day_06_input.txt")
    print(f"Part one: {distinct_run_index(input, 4)}")
    print(f"Part two: {distinct_run_index(input, 14)}")

Advent of Code 2022 day 4

Python solution for Advent of Code 2022 day 4. Used ranges and sets to do the heavy lifting, which felt like too much work for part 1 but it made part 2 easy as pie.

from unittest import TestCase
from utils import get_raw_input


def build_section_pairs(raw_input):
    return [
        tuple(
            set(range(int(elf.split("-")[0]), int(elf.split("-")[1]) + 1))
            for elf in pair.split(",")
        )
        for pair in raw_input
    ]


def superset_pair(pair):
    if pair[0] >= pair[1] or pair[1] >= pair[0]:
        return True
    return False


def overlap_pair(pair):
    return len(pair[0] & pair[1])


def part_one(raw_input):
    return len(list(filter(superset_pair, build_section_pairs(raw_input))))


def part_two(raw_input):
    return len(list(filter(overlap_pair, build_section_pairs(raw_input))))


class TestExamples(TestCase):
    def setUp(self):
        self.assignments = [
            "2-4,6-8",
            "2-3,4-5",
            "5-7,7-9",
            "2-8,3-7",
            "6-6,4-6",
            "2-6,4-8",
        ]

    def test_part_one(self):
        self.assertEqual(part_one(self.assignments), 2)

    def test_part_two(self):
        self.assertEqual(part_two(self.assignments), 4)


if __name__ == "__main__":
    raw_input = get_raw_input("day_04_input.txt")
    print(f"Part one: {part_one(raw_input)}")
    print(f"Part two: {part_two(raw_input)}")

Advent of Code 2022 day 3

Python solution for Advent of Code 2022 day 3. Pretty happy with this one, with perhaps an exception for the repetition in line 15.

import string
import unittest
import utils


def priority_from_rucksack(rucksack):
    split_point = int(len(rucksack) / 2)
    common_item = (set(rucksack[0:split_point]) & set(rucksack[split_point:])).pop()
    return string.ascii_letters.index(common_item) + 1


def group_priorities(rucksacks):
    for i in range(0, len(rucksacks), 3):
        group = rucksacks[i : i + 3]
        badge = (set(group[0]) & set(group[1]) & set(group[2])).pop()
        yield string.ascii_letters.index(badge) + 1


def part_one(rucksacks):
    return sum([priority_from_rucksack(rucksack) for rucksack in rucksacks])


def part_two(rucksacks):
    return sum([group_priority for group_priority in group_priorities(rucksacks)])


if __name__ == "__main__":
    rucksacks = utils.get_raw_input("day_03_input.txt")

    print(f"Part one: {part_one(rucksacks)}")
    print(f"Part two: {part_two(rucksacks)}")


class TestExamples(unittest.TestCase):
    def setUp(self):
        self.rucksacks = [
            "vJrwpWtwJgWrhcsFMMfFFhFp",
            "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL",
            "PmmdzqPrVvPwwTWBwg",
            "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn",
            "ttgJtRGJQctTZtZT",
            "CrZsJsPPZsGzwwsLwLmpwMDw",
        ]

    def test_part_one(self):
        self.assertEqual(part_one(self.rucksacks), 157)

    def test_part_two(self):
        self.assertEqual(part_two(self.rucksacks), 70)

Advent of Code 2022 day 2

Yucky Python solution for Advent of Code 2022 day 2. I started off creating classes for shapes and “rounds” but that all fell apart for part 2, so I just ended up with a pile of data maps.

import unittest
import utils

SHAPES = {
    "rock": {"base_score": 1, "loses_to": "paper", "wins_to": "scissors"},
    "paper": {"base_score": 2, "loses_to": "scissors", "wins_to": "rock"},
    "scissors": {"base_score": 3, "loses_to": "rock", "wins_to": "paper"},
}

SHAPE_MAPPINGS = {
    "A": "rock",
    "B": "paper",
    "C": "scissors",
    "X": "rock",
    "Y": "paper",
    "Z": "scissors",
}

OUTCOMES = {"win": 6, "lose": 0, "draw": 3}

OUTCOME_MAPPINGS = {"X": "lose", "Y": "draw", "Z": "win"}


def outcome(round):
    if SHAPE_MAPPINGS[round[0]] == SHAPE_MAPPINGS[round[1]]:
        return OUTCOMES["draw"]
    elif SHAPES[SHAPE_MAPPINGS[round[0]]]["loses_to"] == SHAPE_MAPPINGS[round[1]]:
        return OUTCOMES["win"]
    else:
        return OUTCOMES["lose"]


def reverse_outcome(round):
    if OUTCOME_MAPPINGS[round[1]] == "win":
        return SHAPES[SHAPES[SHAPE_MAPPINGS[round[0]]]["loses_to"]]["base_score"]
    elif OUTCOME_MAPPINGS[round[1]] == "lose":
        return SHAPES[SHAPES[SHAPE_MAPPINGS[round[0]]]["wins_to"]]["base_score"]
    else:
        return SHAPES[SHAPE_MAPPINGS[round[0]]]["base_score"]


def part_one(rounds):
    return sum(
        [
            outcome(round) + SHAPES[SHAPE_MAPPINGS[round[1]]]["base_score"]
            for round in rounds
        ]
    )


def part_two(rounds):
    return sum(
        [
            reverse_outcome(round) + OUTCOMES[OUTCOME_MAPPINGS[round[1]]]
            for round in rounds
        ]
    )


def parse_raw_input(raw_input):
    return [(round[0], round[-1]) for round in raw_input]


class TestExamples(unittest.TestCase):
    def setUp(self):
        self.rounds = parse_raw_input(["A Y", "B X", "C Z"])

    def test_part_one(self):
        self.assertEqual(part_one(self.rounds), 15)

    def test_part_two(self):
        self.assertEqual(part_two(self.rounds), 12)


if __name__ == "__main__":
    rounds = parse_raw_input(utils.get_raw_input("day_02_input.txt"))
    print(f"Part one: {part_one(rounds)}")
    print(f"Part two: {part_two(rounds)}")