zerosleeps

Since 2010

Advent of Code 2022 day 7

A little bit of OOP, a little bit of recursion. Had fun with this one.

import unittest
from utils import get_raw_input


class Directory:
    def __init__(self, parent=None):
        self.parent = parent
        self.directories = {}
        self.files = {}

    def subdirectory(self, name):
        return self.directories.setdefault(name, Directory(parent=self))

    def add_file(self, size, name):
        return self.files.setdefault(name, int(size))

    @property
    def size(self):
        return sum(self.files.values()) + sum(
            [dir.size for dir in self.directories.values()]
        )

    @property
    def subdirectories(self):
        return list(self.directories.values()) + [
            sd
            for d in [
                directory.subdirectories for directory in self.directories.values()
            ]
            for sd in d
        ]


def build_file_system(raw_input):
    root_directory = Directory()
    current_directory = root_directory

    for line in raw_input:
        if line == "$ cd /":
            current_directory = root_directory
        elif line == "$ cd ..":
            current_directory = current_directory.parent
        elif line.startswith("$ cd"):
            current_directory = current_directory.subdirectory(line.split()[-1])
        elif line.startswith("dir"):
            current_directory.subdirectory(line.split()[-1])
        elif line[0].isdigit():
            current_directory.add_file(*line.split())

    return root_directory


def part_one(file_system):
    return sum(
        [
            directory.size
            for directory in file_system.subdirectories
            if directory.size <= 100000
        ]
    )


def part_two(file_system):
    min_space_to_find = 30000000 - (70000000 - file_system.size)
    candidate_directory_sizes = [
        directory.size
        for directory in file_system.subdirectories
        if directory.size >= min_space_to_find
    ]
    return min(candidate_directory_sizes)


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


class TestExamples(unittest.TestCase):
    def setUp(self):
        self.input = [
            "$ cd /",
            "$ ls",
            "dir a",
            "14848514 b.txt",
            "8504156 c.dat",
            "dir d",
            "$ cd a",
            "$ ls",
            "dir e",
            "29116 f",
            "2557 g",
            "62596 h.lst",
            "$ cd e",
            "$ ls",
            "584 i",
            "$ cd ..",
            "$ cd ..",
            "$ cd d",
            "$ ls",
            "4060174 j",
            "8033020 d.log",
            "5626152 d.ext",
            "7214296 k",
        ]
        self.file_system = build_file_system(self.input)

    def test_size_of_e(self):
        self.assertEqual(self.file_system.directories["a"].directories["e"].size, 584)

    def test_size_of_a(self):
        self.assertEqual(self.file_system.directories["a"].size, 94853)

    def test_size_of_d(self):
        self.assertEqual(self.file_system.directories["d"].size, 24933642)

    def test_size_of_root_directory(self):
        self.assertEqual(self.file_system.size, 48381165)

    def test_part_one_example(self):
        self.assertEqual(part_one(self.file_system), 95437)

    def test_part_two_example(self):
        self.assertEqual(part_two(self.file_system), 24933642)