-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday07.py
executable file
·144 lines (106 loc) · 3.11 KB
/
day07.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from __future__ import annotations
from functools import cache
DISK_SIZE = 70000000
SPACE_NEEDED = 30000000
class Node:
name: str
size: int
children: dict[str, Node]
def __init__(self, name: str, size: int):
self.name = name
self.size = size
self.children = {}
def add_file(self, path: list[str], name: str, size: int) -> None:
if len(path) == 0:
self.children[name] = File(name, size)
else:
self.children[path[0]].add_file(path[1:], name, size)
def add_dir(self, path: list[str], name: str) -> None:
if len(path) == 0:
self.children[name] = Dir(name)
else:
self.children[path[0]].add_dir(path[1:], name)
class File(Node):
def __init__(self, name: str, size: int):
super().__init__(name, size)
@property
def total_size(self) -> int:
return self.size
class Dir(Node):
def __init__(self, name: str):
super().__init__(name, 0)
@property
@cache
def total_size(self) -> int:
return sum(child.total_size for child in self.children.values())
class Root(Dir):
def __init__(self, name: str = "/"):
super().__init__(name)
self.add_dir([], name)
def parse(input: str) -> Root:
sections = filter(lambda s: s, input.split("$"))
location = []
root = Root()
for section in sections:
lines = section.strip().splitlines()
tokens = lines[0].split()
arg = None if len(tokens) == 1 else tokens[1]
match (tokens[0], arg):
case ("cd", ".."): location.pop()
case ("cd", _): location.append(arg)
case ("ls", _):
for output in [line.split() for line in lines[1:]]:
if output[0] == "dir":
root.add_dir(location, output[1])
else:
root.add_file(location, output[1], int(output[0]))
return root
def get_dir_sizes(node: Node) -> int:
dir_sizes = sum(get_dir_sizes(child) for child in node.children.values())
if issubclass(type(node), Dir) and node.total_size <= 100000:
dir_sizes += node.total_size
return dir_sizes
def find_dir_to_delete(node: Node, deficit: int) -> int:
answer = DISK_SIZE
for child in node.children.values():
if child.total_size >= deficit:
size = find_dir_to_delete(child, deficit)
if size >= deficit and size < answer:
answer = size
if node.total_size >= deficit and node.total_size < answer and issubclass(type(node), Dir):
answer = node.total_size
return answer
def part1(input: str) -> int:
return get_dir_sizes(parse(input))
def part2(input: str) -> int:
root = parse(input)
return find_dir_to_delete(root, root.total_size + SPACE_NEEDED - DISK_SIZE)
TEST_INPUT = """$ 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"""
PART1_TESTS = [
(TEST_INPUT, 95437),
]
PART2_TESTS = [
(TEST_INPUT, 24933642),
]