From 720c9924da0a78fa980a991e880405992edf26e7 Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Wed, 7 Dec 2022 18:16:31 +0100 Subject: [PATCH] 07 --- 2022/07/README.md | 11 ++++ 2022/07/python/main.py | 115 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 2022/07/README.md create mode 100644 2022/07/python/main.py diff --git a/2022/07/README.md b/2022/07/README.md new file mode 100644 index 0000000..0880c1c --- /dev/null +++ b/2022/07/README.md @@ -0,0 +1,11 @@ +# 07 + +Loved this one too!! Writing a littler shell "emulator" was def. fun! Also caching directory sizes for performance :) + +
+ Solutions +
    +
  1. 1306611
  2. +
  3. 13210366
  4. +
+
diff --git a/2022/07/python/main.py b/2022/07/python/main.py new file mode 100644 index 0000000..6277166 --- /dev/null +++ b/2022/07/python/main.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +from dataclasses import dataclass +from os.path import dirname, join +from typing import Callable, ClassVar, Literal, Union + +# Day 07 + +# Common + + +def read_input(filename): + data = join(dirname(__file__), '..', filename) + with open(data) as f: + return f.read().strip() + + +test = read_input('test.txt') +data = read_input('input.txt') + + +@dataclass +class File: + size: int + + +@dataclass +class Directory: + name: str + contents: dict[str, Union['Directory', File]] + parent: Union[None, 'Directory'] = None + _size: int | None = None + + def size(self) -> int: + if self._size is None: + self._size = sum([ + entry.size if isinstance(entry, File) else entry.size() + for entry in self.contents.values() + ]) + return self._size + + def walk(self, fn: Callable[[Union[File, 'Directory']], None]): + for entry in self.contents.values(): + fn(entry) + if isinstance(entry, Directory): + entry.walk(fn) + + +@dataclass +class Command: + command: str + output: list[str] + + +@dataclass +class Shell: + commands: list[Command] + root: Directory + cwd: Directory + + def execute(self): + for command in self.commands: + cmd, *args = command.command.split() + match cmd: + case 'ls': + for entry in command.output: + size, name = entry.split() + self.cwd.contents[name] = Directory(name, {}, self.cwd) if size == 'dir' else File(int(size)) + case 'cd': + match args[0]: + case '/': + self.cwd = self.root + case '..': + self.cwd = self.cwd.parent + case _: + self.cwd = self.cwd.contents[args[0]] + + def get_all_sizes(self) -> list[int]: + sizes: list[int] = [] + + def fn(entry: Directory | File): + if isinstance(entry, Directory): + sizes.append(entry.size()) + self.root.walk(fn) + return sizes + + def flag_1(self): + directories = self.get_all_sizes() + return sum([size for size in directories if size <= 100_000]) + + def flag_2(self, total=70_000_000, needed=30_000_000): + to_remove = needed - total + self.root.size() + big_enough = sorted([ + size + for size in self.get_all_sizes() + if size > to_remove + ]) + return big_enough[0] + + @staticmethod + def parse(data: str): + commands = [command.strip().split('\n') for command in data.split('$ ')[1:]] + root = Directory('/', {}) + return Shell([Command(command[0], command[1:]) for command in commands], root, root) + + +shell = Shell.parse(test) +shell.execute() +print(shell.flag_1()) +print(shell.flag_2()) + +shell = Shell.parse(data) +shell.execute() +print(shell.flag_1()) +print(shell.flag_2())