From 85503e88e1ce76d99d9be40492f77021364e63bd Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Wed, 7 Dec 2022 11:44:48 +0100 Subject: Day 7 --- day7/__init__.py | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 day7/__init__.py (limited to 'day7/__init__.py') diff --git a/day7/__init__.py b/day7/__init__.py new file mode 100644 index 0000000..279201a --- /dev/null +++ b/day7/__init__.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +from abc import ABC +from dataclasses import dataclass, field +from enum import Enum +from typing import Set, List, Optional, Tuple, Dict, Iterator, Any + +from aoc import BaseAssignment + + +class Action(Enum): + ls = "ls" + cd = "cd" + + +@dataclass(kw_only=True) +class Command: + action: Tuple[Action, List[str]] + output: Optional[List[str]] + + @classmethod + def from_input(cls, input: str) -> "Command": + action, *args = input[2:].split(" ") + + return Command(action=(Action(action), args), output=[]) + + +@dataclass(kw_only=True) +class Inode(ABC): + name: str + parent: "Folder" = None + size: int = NotImplemented + + +@dataclass(kw_only=True) +class Folder(Inode): + children: Dict[str, Inode] = field(default_factory=dict) + + @property + def size(self) -> int: + return sum([inode.size for inode in self.children.values()]) + + @size.setter + def size(self, value: int): + pass + + def __iter__(self): + yield self + + for inode in self.children.values(): + if isinstance(inode, Folder): + for item in iter(inode): + yield item + else: + yield inode + + +@dataclass(kw_only=True) +class File(Inode): + pass + + +@dataclass +class FileSystem: + current: Folder = None + root: Folder = field(default_factory=lambda: Folder(name="/")) + + capacity = 70000000 + + @classmethod + def from_commands(cls, commands: List[Command]) -> "FileSystem": + file_system = cls() + + for command in commands: + file_system.parse_command(command) + + return file_system + + def __post_init__(self): + self.current = self.root + + def parse_command(self, command: Command): + match command.action: + case [Action.ls, _]: + self.create_inodes(command.output or []) + case [Action.cd, arguments]: + self.change_directory(*arguments) + + def create_inodes(self, inodes: List[str]): + for item in inodes: + size, name = item.split(" ") + + inode = ( + Folder(name=name, parent=self.current) + if size == "dir" + else File(name=name, parent=self.current, size=int(size)) + ) + + if name in self.current.children: + raise Exception(f"File/Folder already exists {name}") + + self.current.children[name] = inode + + def change_directory(self, name: str): + match name: + case "..": + new_dir = self.current.parent + case "/": + new_dir = self.root + case name: + new_dir = self.current.children.get(name) + + if new_dir is None or isinstance(new_dir, File): + raise Exception(f"No such folder {name}") + + self.current = new_dir + + def __iter__(self): + return iter(self.root) + + @property + def free_space(self): + return self.capacity - self.root.size + + +class Assignment(BaseAssignment, ABC): + @staticmethod + def parse_terminal_output(output: Iterator[str]) -> List[Command]: + commands = [] + command = None + + for item in output: + if item.startswith("$"): + if command is not None: + commands.append(command) + + command = Command.from_input(item) + else: + command.output.append(item) + + if command is not None: + commands.append(command) + + return commands + + +class AssignmentOne(Assignment): + example_result = 95437 + + def run(self, input: Iterator) -> Any: + commands = self.parse_terminal_output(input) + file_system = FileSystem.from_commands(commands) + + items = [ + inode.size + for inode in file_system + if isinstance(inode, Folder) and inode.name != "/" and inode.size <= 100000 + ] + + return sum(items) + + +class AssignmentTwo(Assignment): + example_result = 24933642 + + def run(self, input: Iterator) -> Any: + commands = self.parse_terminal_output(input) + file_system = FileSystem.from_commands(commands) + + update_size = 30000000 + space_to_free = update_size - file_system.free_space + + eligible_for_deletion = sorted( + [ + inode + for inode in file_system + if isinstance(inode, Folder) and inode.size >= space_to_free + ], + key=lambda node: node.size, + ) + + return eligible_for_deletion[0].size -- cgit v1.2.3