zerosleeps

Since 2010

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

My blog platform is dying

Did you notice that the code block in my last post doesn’t have syntax highlighting (here’s what the code block should look like)? Yeah, true to form I haven’t done anything about replacing or upgrading the generator used to create this site.

My Ruby 2.6.9 (de-supported for well over a year now) installation broke and I can’t build it on my arm64 Mac any more. The smallest possible upgrade I could make to get back into supported-Ruby-land was to version 2.7.7. Octopress was able to generate pages on that version, albeit with a tonne of deprecation warnings.

But Pygments 0.6 - which is used to add the code highlighting - refuses to play nicely with Ruby 2.7.7. That version of Pygments is a dependency of Jekyll 2, and I can’t upgrade Jekyll because Octopress 2 depends on Jekyll 2.

Installing Python 2 might help, but Homebrew doesn’t have a Python 2 formula any more. My code blocks use Python 3 syntax anyway.

This is absolutely on me - I’ve let this thing rot for so long that it doesn’t feel like there’s any way back now 😕

Advent of Code 2022 day 1

My Python solution for Advent of Code 2022 day 1. About 90% of my time was spent working out how nested list comprehension works - it’s always been one of my downfalls in these things!

import unittest
import utils


def part_one(raw_input):
    return max([sum(elf) for elf in parse_raw_input(raw_input)])


def part_two(raw_input):
    return sum(sorted([sum(elf) for elf in parse_raw_input(raw_input)])[-3:])


def parse_raw_input(raw_input):
    return [
        [int(calories) for calories in elf.splitlines()]
        for elf in [elves for elves in raw_input.split("\n\n")]
    ]


class TestExamples(unittest.TestCase):
    def setUp(self):
        self.raw_input = (
            "1000\n2000\n3000\n\n4000\n\n5000\n6000\n\n7000\n8000\n9000\n\n10000"
        )

    def test_part_one(self):
        self.assertEqual(part_one(self.raw_input), 24000)

    def test_part_two(self):
        self.assertEqual(part_two(self.raw_input), 45000)


if __name__ == "__main__":
    raw_input = utils.get_raw_input_as_str("day_01_input.txt")

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