110 lines
2.6 KiB
Python
110 lines
2.6 KiB
Python
from typing import *
|
|
from dataclasses import dataclass
|
|
from aocpy import BaseChallenge
|
|
|
|
|
|
class Path:
|
|
parts: List[str]
|
|
|
|
def __init__(self):
|
|
self.parts = []
|
|
|
|
def cd(self, arg: str):
|
|
if arg == "..":
|
|
_ = self.parts.pop()
|
|
elif arg != ".":
|
|
self.parts.append(arg)
|
|
|
|
def cwd(self) -> str:
|
|
x = "/".join(self.parts)
|
|
return x if len(x) != 0 else "/"
|
|
|
|
def abs_in_cwd(self, path: str) -> str:
|
|
cwd = self.cwd()
|
|
if cwd == "/":
|
|
return "/" + path
|
|
return cwd + "/" + path
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Directories:
|
|
directory_names: List[str]
|
|
files: Dict[str, int]
|
|
|
|
def __init__(self):
|
|
self.directory_names = []
|
|
self.files = {}
|
|
|
|
def add_dir(self, path: str):
|
|
if path not in self.directory_names:
|
|
self.directory_names.append(path)
|
|
|
|
def add_file(self, path: str, size: int):
|
|
self.files[path] = size
|
|
|
|
|
|
def parse(instr: str) -> Directories:
|
|
dirs = Directories()
|
|
fp = Path()
|
|
|
|
i = 0
|
|
lines = instr.strip().splitlines()
|
|
while i < len(lines):
|
|
line = lines[i]
|
|
|
|
if line.startswith("$ cd"):
|
|
fp.cd(line.removeprefix("$ cd "))
|
|
dirs.add_dir(fp.cwd())
|
|
|
|
elif line.startswith("$ ls"):
|
|
while i < len(lines) - 1 and not lines[i + 1].startswith("$"):
|
|
i += 1
|
|
line = lines[i]
|
|
|
|
sp = line.split(" ")
|
|
if sp[0] == "dir":
|
|
dirs.add_dir(fp.abs_in_cwd(sp[1]))
|
|
else:
|
|
dirs.add_file(fp.abs_in_cwd(sp[1]), int(sp[0]))
|
|
|
|
i += 1
|
|
|
|
return dirs
|
|
|
|
|
|
def calculate_directory_size(dirs: Directories, target_dir_name: str) -> int:
|
|
total = 0
|
|
for fname in dirs.files:
|
|
if fname.startswith(target_dir_name + "/"):
|
|
total += dirs.files[fname]
|
|
return total
|
|
|
|
|
|
class Challenge(BaseChallenge):
|
|
@staticmethod
|
|
def one(instr: str) -> int:
|
|
dirs = parse(instr)
|
|
|
|
total = 0
|
|
|
|
for dir_name in dirs.directory_names:
|
|
sz = calculate_directory_size(dirs, dir_name)
|
|
if sz <= 100000:
|
|
total += sz
|
|
|
|
return total
|
|
|
|
@staticmethod
|
|
def two(instr: str) -> int:
|
|
dirs = parse(instr)
|
|
|
|
used = 70000000 - calculate_directory_size(dirs, "")
|
|
amount_to_delete = 30000000 - used
|
|
|
|
opts = []
|
|
for dir_name in dirs.directory_names:
|
|
sz = calculate_directory_size(dirs, dir_name)
|
|
if sz >= amount_to_delete:
|
|
opts.append(sz)
|
|
|
|
return min(opts)
|