day07 finished
This commit is contained in:
parent
576bfd0bcd
commit
4589a7c591
6 changed files with 1311 additions and 1 deletions
|
|
@ -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.
|
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.
|
I use python 3.11 without any libraries beyond the standard.
|
||||||
|
|
||||||
| Day | Time | Rank | Score | Time | Rank | Score |
|
| 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 |
|
| 6 | 00:14:52 | 9153 | 0 | 00:17:06 | 8413 | 0 |
|
||||||
| 5 | 01:01:27 | 11570 | 0 | 01:05:20 | 10701 | 0 |
|
| 5 | 01:01:27 | 11570 | 0 | 01:05:20 | 10701 | 0 |
|
||||||
| 4 | 00:12:09 | 5789 | 0 | 00:16:03 | 5187 | 0 |
|
| 4 | 00:12:09 | 5789 | 0 | 00:16:03 | 5187 | 0 |
|
||||||
|
|
|
||||||
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