day07 finished
This commit is contained in:
parent
576bfd0bcd
commit
4589a7c591
6 changed files with 1311 additions and 1 deletions
0
advent/days/day07/__init__.py
Normal file
0
advent/days/day07/__init__.py
Normal file
1135
advent/days/day07/data/input.txt
Normal file
1135
advent/days/day07/data/input.txt
Normal file
File diff suppressed because it is too large
Load diff
23
advent/days/day07/data/test01.txt
Normal file
23
advent/days/day07/data/test01.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
$ 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
|
||||
110
advent/days/day07/solution.py
Normal file
110
advent/days/day07/solution.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from typing import Iterator
|
||||
|
||||
day_num = 7
|
||||
|
||||
|
||||
def part1(lines: Iterator[str]) -> int:
|
||||
return Directory.parse(lines).get_maxed_size(100_000)
|
||||
|
||||
|
||||
def part2(lines: Iterator[str]) -> int:
|
||||
directory = Directory.parse(lines)
|
||||
return directory.get_min_delete_size(70_000_000, 30_000_000)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Directory:
|
||||
name: str
|
||||
parent: Directory | None
|
||||
subdirs: list[Directory] = field(default_factory=list)
|
||||
files: list[tuple[str, int]] = field(default_factory=list)
|
||||
size: int | None = None
|
||||
|
||||
def cd_into(self, name: str) -> Directory:
|
||||
"""
|
||||
Returns the named sub directory or .. for parent
|
||||
May fail if unkown subdirectory - or already in root
|
||||
"""
|
||||
if name == "..":
|
||||
if self.parent is None:
|
||||
raise Exception('Already at root Directory')
|
||||
return self.parent
|
||||
|
||||
for sub in self.subdirs:
|
||||
if sub.name == name:
|
||||
return sub
|
||||
raise Exception(f"Could not find subdir {name}")
|
||||
|
||||
def add_directory(self, name: str):
|
||||
""" Adds the named directory."""
|
||||
self.subdirs.append(Directory(name, self))
|
||||
|
||||
def add_file(self, name: str, size: int):
|
||||
""" Adds the given file and size """
|
||||
self.files.append((name, size))
|
||||
|
||||
def get_size(self) -> int:
|
||||
""" returns the size of this directory including all subdirectories """
|
||||
if self.size is None:
|
||||
self.size = (sum(size for _, size in self.files)
|
||||
+ sum(sub.get_size() for sub in self.subdirs))
|
||||
return self.size
|
||||
|
||||
def get_all_directories(self) -> Iterator[Directory]:
|
||||
""" Returns an iterator of all subdirectories """
|
||||
for sub in self.subdirs:
|
||||
yield from sub.get_all_directories()
|
||||
yield self
|
||||
|
||||
def get_maxed_size(self, threshold: int) -> int:
|
||||
""" Returns the sum of all sizes of subdirectories, that are below the given threshold"""
|
||||
return sum(size for size in
|
||||
(dir.get_size() for dir in self.get_all_directories())
|
||||
if size <= threshold)
|
||||
|
||||
def get_min_delete_size(self, disk_size: int, space_needed: int) -> int:
|
||||
"""
|
||||
Returns the size of the smallest directory that must be removed to created the free space
|
||||
given as a parameter and the given disk size
|
||||
#"""
|
||||
unused = disk_size - self.get_size()
|
||||
minimum: int | None = None
|
||||
for dir in self.get_all_directories():
|
||||
size = dir.get_size()
|
||||
if unused + size >= space_needed and (minimum is None or minimum > size):
|
||||
minimum = size
|
||||
|
||||
if minimum is None:
|
||||
raise Exception("Could not find large enough directory to remove")
|
||||
|
||||
return minimum
|
||||
|
||||
@staticmethod
|
||||
def parse(lines: Iterator[str]) -> Directory:
|
||||
line = next(lines)
|
||||
if line != '$ cd /':
|
||||
raise Exception(f"Illegal first line: {line}")
|
||||
|
||||
root = Directory('/', None)
|
||||
current = root
|
||||
for line in lines:
|
||||
match line.split():
|
||||
case ['$', 'cd', name]:
|
||||
current = current.cd_into(name)
|
||||
|
||||
case ['$', 'ls']:
|
||||
pass
|
||||
|
||||
case ['dir', name]:
|
||||
current.add_directory(name)
|
||||
|
||||
case [size, name]:
|
||||
current.add_file(name, int(size))
|
||||
|
||||
case _:
|
||||
raise Exception(f"Could not parse line: {line}")
|
||||
|
||||
return root
|
||||
41
advent/days/day07/test_solution.py
Normal file
41
advent/days/day07/test_solution.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from advent.common import utils
|
||||
|
||||
from .solution import day_num, part1, part2, Directory
|
||||
|
||||
|
||||
def test_part1():
|
||||
data = utils.read_data(day_num, 'test01.txt')
|
||||
expected = 95437
|
||||
result = part1(data)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_part2():
|
||||
data = utils.read_data(day_num, 'test01.txt')
|
||||
expected = 24933642
|
||||
result = part2(data)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_size():
|
||||
data = utils.read_data(day_num, 'test01.txt')
|
||||
expected = 48381165
|
||||
directory = Directory.parse(data)
|
||||
result = directory.get_size()
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_maxed_size():
|
||||
data = utils.read_data(day_num, 'test01.txt')
|
||||
expected = 95437
|
||||
directory = Directory.parse(data)
|
||||
result = directory.get_maxed_size(100_000)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_find_to_delete():
|
||||
data = utils.read_data(day_num, 'test01.txt')
|
||||
expected = 24933642
|
||||
directory = Directory.parse(data)
|
||||
result = directory.get_min_delete_size(70_000_000, 30_000_000)
|
||||
assert result == expected
|
||||
Loading…
Add table
Add a link
Reference in a new issue