day07 finished

This commit is contained in:
Ruediger Ludwig 2022-12-07 07:36:08 +01:00
parent 576bfd0bcd
commit 4589a7c591
6 changed files with 1311 additions and 1 deletions

View file

@ -2,12 +2,13 @@
Here are my solutions for [Advent of Code 2022](https://adventofcode.com/2022). Thanks to [Eric Wastl](http://was.tl) for the great puzzles and the great time I have solving them.
My goal is to solve each puzzle on the day it was posted. Also I want to create code that is correct (of course), but some I would also understand in about a year. So a readable solution is preferable to one that is only correct. This is why I use TDD, classes and comments where they technically might not be necessary.
My goal is to solve each puzzle on the day it was posted. Also I want to create code that is correct (of course), but which I still understand in about a year. So a readable solution is preferable to one that is only correct. This is why I use TDD, classes and comments where they technically might not be necessary. I often refine my solution after I handed it in.
I use python 3.11 without any libraries beyond the standard.
| Day | Time | Rank | Score | Time | Rank | Score |
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
| 7 | 00:34:59 | 2683 | 0 | 00:45:45 | 2943 | 0 |
| 6 | 00:14:52 | 9153 | 0 | 00:17:06 | 8413 | 0 |
| 5 | 01:01:27 | 11570 | 0 | 01:05:20 | 10701 | 0 |
| 4 | 00:12:09 | 5789 | 0 | 00:16:03 | 5187 | 0 |

View file

File diff suppressed because it is too large Load diff

View 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

View 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

View 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