summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--day7/__init__.py181
-rw-r--r--day7/example.txt23
-rw-r--r--day7/input.txt1079
3 files changed, 1283 insertions, 0 deletions
diff --git a/day7/__init__.py b/day7/__init__.py
new file mode 100644
index 0000000..279201a
--- /dev/null
+++ b/day7/__init__.py
@@ -0,0 +1,181 @@
1# -*- coding: utf-8 -*-
2from abc import ABC
3from dataclasses import dataclass, field
4from enum import Enum
5from typing import Set, List, Optional, Tuple, Dict, Iterator, Any
6
7from aoc import BaseAssignment
8
9
10class Action(Enum):
11 ls = "ls"
12 cd = "cd"
13
14
15@dataclass(kw_only=True)
16class Command:
17 action: Tuple[Action, List[str]]
18 output: Optional[List[str]]
19
20 @classmethod
21 def from_input(cls, input: str) -> "Command":
22 action, *args = input[2:].split(" ")
23
24 return Command(action=(Action(action), args), output=[])
25
26
27@dataclass(kw_only=True)
28class Inode(ABC):
29 name: str
30 parent: "Folder" = None
31 size: int = NotImplemented
32
33
34@dataclass(kw_only=True)
35class Folder(Inode):
36 children: Dict[str, Inode] = field(default_factory=dict)
37
38 @property
39 def size(self) -> int:
40 return sum([inode.size for inode in self.children.values()])
41
42 @size.setter
43 def size(self, value: int):
44 pass
45
46 def __iter__(self):
47 yield self
48
49 for inode in self.children.values():
50 if isinstance(inode, Folder):
51 for item in iter(inode):
52 yield item
53 else:
54 yield inode
55
56
57@dataclass(kw_only=True)
58class File(Inode):
59 pass
60
61
62@dataclass
63class FileSystem:
64 current: Folder = None
65 root: Folder = field(default_factory=lambda: Folder(name="/"))
66
67 capacity = 70000000
68
69 @classmethod
70 def from_commands(cls, commands: List[Command]) -> "FileSystem":
71 file_system = cls()
72
73 for command in commands:
74 file_system.parse_command(command)
75
76 return file_system
77
78 def __post_init__(self):
79 self.current = self.root
80
81 def parse_command(self, command: Command):
82 match command.action:
83 case [Action.ls, _]:
84 self.create_inodes(command.output or [])
85 case [Action.cd, arguments]:
86 self.change_directory(*arguments)
87
88 def create_inodes(self, inodes: List[str]):
89 for item in inodes:
90 size, name = item.split(" ")
91
92 inode = (
93 Folder(name=name, parent=self.current)
94 if size == "dir"
95 else File(name=name, parent=self.current, size=int(size))
96 )
97
98 if name in self.current.children:
99 raise Exception(f"File/Folder already exists {name}")
100
101 self.current.children[name] = inode
102
103 def change_directory(self, name: str):
104 match name:
105 case "..":
106 new_dir = self.current.parent
107 case "/":
108 new_dir = self.root
109 case name:
110 new_dir = self.current.children.get(name)
111
112 if new_dir is None or isinstance(new_dir, File):
113 raise Exception(f"No such folder {name}")
114
115 self.current = new_dir
116
117 def __iter__(self):
118 return iter(self.root)
119
120 @property
121 def free_space(self):
122 return self.capacity - self.root.size
123
124
125class Assignment(BaseAssignment, ABC):
126 @staticmethod
127 def parse_terminal_output(output: Iterator[str]) -> List[Command]:
128 commands = []
129 command = None
130
131 for item in output:
132 if item.startswith("$"):
133 if command is not None:
134 commands.append(command)
135
136 command = Command.from_input(item)
137 else:
138 command.output.append(item)
139
140 if command is not None:
141 commands.append(command)
142
143 return commands
144
145
146class AssignmentOne(Assignment):
147 example_result = 95437
148
149 def run(self, input: Iterator) -> Any:
150 commands = self.parse_terminal_output(input)
151 file_system = FileSystem.from_commands(commands)
152
153 items = [
154 inode.size
155 for inode in file_system
156 if isinstance(inode, Folder) and inode.name != "/" and inode.size <= 100000
157 ]
158
159 return sum(items)
160
161
162class AssignmentTwo(Assignment):
163 example_result = 24933642
164
165 def run(self, input: Iterator) -> Any:
166 commands = self.parse_terminal_output(input)
167 file_system = FileSystem.from_commands(commands)
168
169 update_size = 30000000
170 space_to_free = update_size - file_system.free_space
171
172 eligible_for_deletion = sorted(
173 [
174 inode
175 for inode in file_system
176 if isinstance(inode, Folder) and inode.size >= space_to_free
177 ],
178 key=lambda node: node.size,
179 )
180
181 return eligible_for_deletion[0].size
diff --git a/day7/example.txt b/day7/example.txt
new file mode 100644
index 0000000..09a921e
--- /dev/null
+++ b/day7/example.txt
@@ -0,0 +1,23 @@
1$ cd /
2$ ls
3dir a
414848514 b.txt
58504156 c.dat
6dir d
7$ cd a
8$ ls
9dir e
1029116 f
112557 g
1262596 h.lst
13$ cd e
14$ ls
15584 i
16$ cd ..
17$ cd ..
18$ cd d
19$ ls
204060174 j
218033020 d.log
225626152 d.ext
237214296 k
diff --git a/day7/input.txt b/day7/input.txt
new file mode 100644
index 0000000..8f22e2f
--- /dev/null
+++ b/day7/input.txt
@@ -0,0 +1,1079 @@
1$ cd /
2$ ls
3dir dpbwg
4dir dvwfscw
5dir hccpl
6dir jsgbg
7dir lhjmzsl
863532 mwvbpw.mmg
9239480 npj
10dir pngs
11dir qhs
12303649 shvgmwn.vhv
13236905 sjrrgd.phh
14dir sntcp
15dir sqs
16$ cd dpbwg
17$ ls
18dir dgh
19100731 dpbwg
20dir rpwnv
21$ cd dgh
22$ ls
23197049 lhjmzsl.hzj
24$ cd ..
25$ cd rpwnv
26$ ls
2710702 qsgv.fmf
28$ cd ..
29$ cd ..
30$ cd dvwfscw
31$ ls
32dir bvg
33dir fbfjs
34115450 gjftb.mgd
35dir gsmnprgz
36dir hdwdcvv
37dir mhjtrlqz
3875437 qsctddrw
39171722 qsgv.zqz
40$ cd bvg
41$ ls
4256335 cgtzb.szt
43139481 shvgmwn.vhv
44255200 wzqlgr.mhl
45</