diff options
| author | 2022-12-07 11:44:48 +0100 | |
|---|---|---|
| committer | 2022-12-07 11:44:48 +0100 | |
| commit | 85503e88e1ce76d99d9be40492f77021364e63bd (patch) | |
| tree | 1f6213e49a184c05e0620b80bcdb47b154ef297f /day7 | |
| parent | e0355500ae852319b07f212919fe2090d7746332 (diff) | |
| download | 2022-85503e88e1ce76d99d9be40492f77021364e63bd.tar.gz 2022-85503e88e1ce76d99d9be40492f77021364e63bd.tar.bz2 2022-85503e88e1ce76d99d9be40492f77021364e63bd.zip | |
Day 7
Diffstat (limited to 'day7')
| -rw-r--r-- | day7/__init__.py | 181 | ||||
| -rw-r--r-- | day7/example.txt | 23 | ||||
| -rw-r--r-- | day7/input.txt | 1079 |
3 files changed, 1283 insertions, 0 deletions
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 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from abc import ABC | ||
| 3 | from dataclasses import dataclass, field | ||
| 4 | from enum import Enum | ||
| 5 | from typing import Set, List, Optional, Tuple, Dict, Iterator, Any | ||
| 6 | |||
| 7 | from aoc import BaseAssignment | ||
| 8 | |||
| 9 | |||
| 10 | class Action(Enum): | ||
| 11 | ls = "ls" | ||
| 12 | cd = "cd" | ||
| 13 | |||
| 14 | |||
| 15 | @dataclass(kw_only=True) | ||
| 16 | class Command: | ||
| 17 | action: Tuple[Action, List[str]] | ||
| 18 | output: Optional[List[str]] | ||
| 19 | |||
| 20 | @classmethod | ||
| 21 | def from_input(cls, input: str) -> "Command": | ||
| 22 | action, *args = input[2:].split(" ") | ||
| 23 | |||
| 24 | return Command(action=(Action(action), args), output=[]) | ||
| 25 | |||
| 26 | |||
| 27 | @dataclass(kw_only=True) | ||
| 28 | class Inode(ABC): | ||
| 29 | name: str | ||
| 30 | parent: "Folder" = None | ||
| 31 | size: int = NotImplemented | ||
| 32 | |||
| 33 | |||
| 34 | @dataclass(kw_only=True) | ||
| 35 | class Folder(Inode): | ||
| 36 | children: Dict[str, Inode] = field(default_factory=dict) | ||
| 37 | |||
| 38 | @property | ||
| 39 | def size(self) -> int: | ||
| 40 | return sum([inode.size for inode in self.children.values()]) | ||
| 41 | |||
| 42 | @size.setter | ||
| 43 | def size(self, value: int): | ||
| 44 | pass | ||
| 45 | |||
| 46 | def __iter__(self): | ||
| 47 | yield self | ||
| 48 | |||
| 49 | for inode in self.children.values(): | ||
| 50 | if isinstance(inode, Folder): | ||
| 51 | for item in iter(inode): | ||
| 52 | yield item | ||
| 53 | else: | ||
| 54 | yield inode | ||
| 55 | |||
| 56 | |||
| 57 | @dataclass(kw_only=True) | ||
| 58 | class File(Inode): | ||
| 59 | pass | ||
| 60 | |||
| 61 | |||
| 62 | @dataclass | ||
| 63 | class FileSystem: | ||
| 64 | current: Folder = None | ||
| 65 | root: Folder = field(default_factory=lambda: Folder(name="/")) | ||
| 66 | |||
| 67 | capacity = 70000000 | ||
| 68 | |||
| 69 | @classmethod | ||
| 70 | def from_commands(cls, commands: List[Command]) -> "FileSystem": | ||
| 71 | file_system = cls() | ||
| 72 | |||
| 73 | for command in commands: | ||
| 74 | file_system.parse_command(command) | ||
| 75 | |||
| 76 | return file_system | ||
| 77 | |||
| 78 | def __post_init__(self): | ||
| 79 | self.current = self.root | ||
| 80 | |||
| 81 | def parse_command(self, command: Command): | ||
| 82 | match command.action: | ||
| 83 | case [Action.ls, _]: | ||
| 84 | self.create_inodes(command.output or []) | ||
| 85 | case [Action.cd, arguments]: | ||
| 86 | self.change_directory(*arguments) | ||
| 87 | |||
| 88 | def create_inodes(self, inodes: List[str]): | ||
| 89 | for item in inodes: | ||
| 90 | size, name = item.split(" ") | ||
| 91 | |||
| 92 | inode = ( | ||
| 93 | Folder(name=name, parent=self.current) | ||
| 94 | if size == "dir" | ||
| 95 | else File(name=name, parent=self.current, size=int(size)) | ||
| 96 | ) | ||
| 97 | |||
| 98 | if name in self.current.children: | ||
| 99 | raise Exception(f"File/Folder already exists {name}") | ||
| 100 | |||
| 101 | self.current.children[name] = inode | ||
| 102 | |||
| 103 | def change_directory(self, name: str): | ||
| 104 | match name: | ||
| 105 | case "..": | ||
| 106 | new_dir = self.current.parent | ||
| 107 | case "/": | ||
| 108 | new_dir = self.root | ||
| 109 | case name: | ||
| 110 | new_dir = self.current.children.get(name) | ||
| 111 | |||
| 112 | if new_dir is None or isinstance(new_dir, File): | ||
| 113 | raise Exception(f"No such folder {name}") | ||
| 114 | |||
| 115 | self.current = new_dir | ||
| 116 | |||
| 117 | def __iter__(self): | ||
| 118 | return iter(self.root) | ||
| 119 | |||
| 120 | @property | ||
| 121 | def free_space(self): | ||
| 122 | return self.capacity - self.root.size | ||
| 123 | |||
| 124 | |||
| 125 | class Assignment(BaseAssignment, ABC): | ||
| 126 | @staticmethod | ||
| 127 | def parse_terminal_output(output: Iterator[str]) -> List[Command]: | ||
| 128 | commands = [] | ||
| 129 | command = None | ||
| 130 | |||
| 131 | for item in output: | ||
| 132 | if item.startswith("$"): | ||
| 133 | if command is not None: | ||
| 134 | commands.append(command) | ||
| 135 | |||
| 136 | command = Command.from_input(item) | ||
| 137 | else: | ||
| 138 | command.output.append(item) | ||
| 139 | |||
| 140 | if command is not None: | ||
| 141 | commands.append(command) | ||
| 142 | |||
| 143 | return commands | ||
| 144 | |||
| 145 | |||
| 146 | class AssignmentOne(Assignment): | ||
| 147 | example_result = 95437 | ||
| 148 | |||
| 149 | def run(self, input: Iterator) -> Any: | ||
| 150 | commands = self.parse_terminal_output(input) | ||
| 151 | file_system = FileSystem.from_commands(commands) | ||
| 152 | |||
| 153 | items = [ | ||
| 154 | inode.size | ||
| 155 | for inode in file_system | ||
| 156 | if isinstance(inode, Folder) and inode.name != "/" and inode.size <= 100000 | ||
| 157 | ] | ||
| 158 | |||
| 159 | return sum(items) | ||
| 160 | |||
| 161 | |||
| 162 | class AssignmentTwo(Assignment): | ||
| 163 | example_result = 24933642 | ||
| 164 | |||
| 165 | def run(self, input: Iterator) -> Any: | ||
| 166 | commands = self.parse_terminal_output(input) | ||
| 167 | file_system = FileSystem.from_commands(commands) | ||
| 168 | |||
| 169 | update_size = 30000000 | ||
| 170 | space_to_free = update_size - file_system.free_space | ||
| 171 | |||
| 172 | eligible_for_deletion = sorted( | ||
| 173 | [ | ||
| 174 | inode | ||
| 175 | for inode in file_system | ||
| 176 | if isinstance(inode, Folder) and inode.size >= space_to_free | ||
| 177 | ], | ||
| 178 | key=lambda node: node.size, | ||
| 179 | ) | ||
| 180 | |||
| 181 | return eligible_for_deletion[0].size | ||
diff --git a/day7/example.txt b/day7/example.txt new file mode 100644 index 0000000..09a921e --- /dev/null +++ b/day7/example.txt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | $ cd / | ||
| 2 | $ ls | ||
| 3 | dir a | ||
| 4 | 14848514 b.txt | ||
| 5 | 8504156 c.dat | ||
| 6 | dir d | ||
| 7 | $ cd a | ||
| 8 | $ ls | ||
| 9 | dir e | ||
| 10 | 29116 f | ||
| 11 | 2557 g | ||
| 12 | 62596 h.lst | ||
| 13 | $ cd e | ||
| 14 | $ ls | ||
| 15 | 584 i | ||
| 16 | $ cd .. | ||
| 17 | $ cd .. | ||
| 18 | $ cd d | ||
| 19 | $ ls | ||
| 20 | 4060174 j | ||
| 21 | 8033020 d.log | ||
| 22 | 5626152 d.ext | ||
| 23 | 7214296 k | ||
diff --git a/day7/input.txt b/day7/input.txt new file mode 100644 index 0000000..8f22e2f --- /dev/null +++ b/day7/input.txt | |||
| @@ -0,0 +1,1079 @@ | |||
| 1 | $ cd / | ||
| 2 | $ ls | ||
| 3 | dir dpbwg | ||
| 4 | dir dvwfscw | ||
| 5 | dir hccpl | ||
| 6 | dir jsgbg | ||
| 7 | dir lhjmzsl | ||
| 8 | 63532 mwvbpw.mmg | ||
| 9 | 239480 npj | ||
| 10 | dir pngs | ||
| 11 | dir qhs | ||
| 12 | 303649 shvgmwn.vhv | ||
| 13 | 236905 sjrrgd.phh | ||
| 14 | dir sntcp | ||
| 15 | dir sqs | ||
| 16 | $ cd dpbwg | ||
| 17 | $ ls | ||
| 18 | dir dgh | ||
| 19 | 100731 dpbwg | ||
| 20 | dir rpwnv | ||
| 21 | $ cd dgh | ||
| 22 | $ ls | ||
| 23 | 197049 lhjmzsl.hzj | ||
| 24 | $ cd .. | ||
| 25 | $ cd rpwnv | ||
| 26 | $ ls | ||
| 27 | 10702 qsgv.fmf | ||
| 28 | $ cd .. | ||
| 29 | $ cd .. | ||
| 30 | $ cd dvwfscw | ||
| 31 | $ ls | ||
| 32 | dir bvg | ||
| 33 | dir fbfjs | ||
| 34 | 115450 gjftb.mgd | ||
| 35 | dir gsmnprgz | ||
| 36 | dir hdwdcvv | ||
