2022 Day 07

This commit is contained in:
Akumatic 2022-12-07 19:51:29 +01:00
parent 8879b7c1c8
commit 64a31b5a05
7 changed files with 1330 additions and 1 deletions

109
2022/07/README.md Normal file
View File

@ -0,0 +1,109 @@
# 2022 Day 07: No Space Left On Device
Copyright (c) Eric Wastl
#### [Direct Link](https://adventofcode.com/2022/day/7)
## Part 1
You can hear birds chirping and raindrops hitting leaves as the expedition proceeds. Occasionally, you can even hear much louder sounds in the distance; how big do the animals get out here, anyway?
The device the Elves gave you has problems with more than just its communication system. You try to run a system update:
```
$ system-update --please --pretty-please-with-sugar-on-top
Error: No space left on device
```
Perhaps you can delete some files to make space for the update?
You browse around the filesystem to assess the situation and save the resulting terminal output (your puzzle input). For example:
```
$ 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
```
The filesystem consists of a tree of files (plain data) and directories (which can contain other directories or files). The outermost directory is called `/`. You can navigate around the filesystem, moving into or out of directories and listing the contents of the directory you're currently in.
Within the terminal output, lines that begin with `$` are **commands you executed**, very much like some modern computers:
- `cd` means **change directory**. This changes which directory is the current directory, but the specific result depends on the argument:
- `cd x` moves **in** one level: it looks in the current directory for the directory named `x` and makes it the current directory.
- `cd ..` moves **out** one level: it finds the directory that contains the current directory, then makes that directory the current directory.
- `cd /` switches the current directory to the outermost directory, `/`.
- `ls` means **list**. It prints out all of the files and directories immediately contained by the current directory:
- `123 abc` means that the current directory contains a file named `abc` with size `123`.
- `dir xyz` means that the current directory contains a directory named `xyz`.
Given the commands and output in the example above, you can determine that the filesystem looks visually like this:
```
- / (dir)
- a (dir)
- e (dir)
- i (file, size=584)
- f (file, size=29116)
- g (file, size=2557)
- h.lst (file, size=62596)
- b.txt (file, size=14848514)
- c.dat (file, size=8504156)
- d (dir)
- j (file, size=4060174)
- d.log (file, size=8033020)
- d.ext (file, size=5626152)
- k (file, size=7214296)
```
Here, there are four directories: `/` (the outermost directory), `a` and `d` (which are in `/`), and `e` (which is in `a`). These directories also contain files of various sizes.
Since the disk is full, your first step should probably be to find directories that are good candidates for deletion. To do this, you need to determine the **total size** of each directory. The total size of a directory is the sum of the sizes of the files it contains, directly or indirectly. (Directories themselves do not count as having any intrinsic size.)
The total sizes of the directories above can be found as follows:
- The total size of directory `e` is **584** because it contains a single file `i` of size 584 and no other directories.
- The directory `a` has total size **94853** because it contains files `f` (size 29116), `g` (size 2557), and `h.lst` (size 62596), plus file `i` indirectly (`a` contains `e` which contains `i`).
- Directory `d` has total size **24933642**.
- As the outermost directory, `/` contains every file. Its total size is **48381165**, the sum of the size of every file.
To begin, find all of the directories with a total size of **at most 100000**, then calculate the sum of their total sizes. In the example above, these directories are `a` and `e`; the sum of their total sizes is **`95437`** (94853 + 584). (As in this example, this process can count files more than once!)
Find all of the directories with a total size of at most 100000. **What is the sum of the total sizes of those directories?**
## Part 2
Now, you're ready to choose a directory to delete.
The total disk space available to the filesystem is **`70000000`**. To run the update, you need unused space of at least **`30000000`**. You need to find a directory you can delete that will **free up enough space** to run the update.
In the example above, the total size of the outermost directory (and thus the total amount of used space) is `48381165`; this means that the size of the **unused** space must currently be `21618835`, which isn't quite the `30000000` required by the update. Therefore, the update still requires a directory with total size of at least `8381165` to be deleted before it can run.
To achieve this, you have the following options:
- Delete directory `e`, which would increase unused space by `584`.
- Delete directory `a`, which would increase unused space by `94853`.
- Delete directory `d`, which would increase unused space by `24933642`.
- Delete directory `/`, which would increase unused space by `48381165`.
Directories `e` and `a` are both too small; deleting them would not free up enough space. However, directories `d` and `/` are both big enough! Between these, choose the **smallest**: d, increasing unused space by **`24933642`**.
Find the smallest directory that, if deleted, would free up enough space on the filesystem to run the update. **What is the total size of that directory?**

71
2022/07/code.py Normal file
View File

@ -0,0 +1,71 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2022 Akumatic
#
# https://adventofcode.com/2022/day/7
class Node:
def __init__(self, name, parent = None, size = None) -> None:
self.name = name
self.parent = parent
if parent:
parent.children[name] = self
self.size = size
self.children = dict()
def get_size(self) -> int:
if self.size:
return self.size
return sum(self.children[child].get_size() for child in self.children)
def build_dir_tree(terminal_output: list) -> Node:
root = Node("/")
cur_dir = root
for line in terminal_output:
if line.startswith("$ ls"):
continue # 'else' will deal with output
elif line.startswith("$ cd"):
dir = line[5:]
match dir:
case "/":
cur_dir = root
case "..":
cur_dir = cur_dir.parent
case _:
cur_dir = cur_dir.children[dir]
else: # line not starting with "$" => output of ls
type, name = line.split(" ")
match type:
case "dir":
new_node = Node(name=name, parent=cur_dir)
case _:
new_node = Node(name=name, parent=cur_dir, size=int(type))
return root
def read_file(filename: str = "input.txt") -> Node:
with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f:
lines = [line.strip() for line in f.readlines()]
return build_dir_tree(lines)
def get_possible_folder_sizes(node: Node, target_size: int, size_up_to: bool, sizes: list) -> None:
if len(node.children) == 0:
return
size = node.get_size()
if not size_up_to and size >= target_size or size_up_to and size <= target_size:
sizes.append(size)
for child in node.children:
get_possible_folder_sizes(node.children[child], target_size, size_up_to, sizes)
def part1(node: Node) -> int:
sizes = []
get_possible_folder_sizes(node, 100000, True, sizes)
return sum(sizes)
def part2(node: Node) -> int:
sizes = []
get_possible_folder_sizes(node, node.get_size() - 40000000, False, sizes)
return min(sizes)
if __name__ == "__main__":
vals = read_file()
print(f"Part 1: {part1(vals)}")
print(f"Part 2: {part2(vals)}")

1105
2022/07/input.txt Normal file

File diff suppressed because it is too large Load Diff

2
2022/07/solution.txt Normal file
View File

@ -0,0 +1,2 @@
Part 1: 1348005
Part 2: 12785886

19
2022/07/test_code.py Normal file
View File

@ -0,0 +1,19 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2022 Akumatic
from code import read_file, part1, part2
def test():
root = read_file("test_input.txt")
assert root.get_size() == 48381165
assert root.children["d"].get_size() == 24933642
assert root.children["a"].get_size() == 94853
assert root.children["a"].children["e"].get_size() == 584
print("Passed Tree Bulding")
assert part1(root) == 95437
print("Passed Part 1")
assert part2(root) == 24933642
print("Passed Part 2")
if __name__ == "__main__":
test()

23
2022/07/test_input.txt Normal file
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

@ -21,7 +21,7 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day
| 04 | :white_check_mark: | :white_check_mark: | [Solution](04/code.py) | [Day 04](https://adventofcode.com/2022/day/4) |
| 05 | :white_check_mark: | :white_check_mark: | [Solution](05/code.py) | [Day 05](https://adventofcode.com/2022/day/5) |
| 06 | :white_check_mark: | :white_check_mark: | [Solution](06/code.py) | [Day 06](https://adventofcode.com/2022/day/6) |
| 07 | | | | [Day 07](https://adventofcode.com/2022/day/7) |
| 07 | :white_check_mark: | :white_check_mark: | [Solution](07/code.py) | [Day 07](https://adventofcode.com/2022/day/7) |
| 08 | | | | [Day 08](https://adventofcode.com/2022/day/8) |
| 09 | | | | [Day 09](https://adventofcode.com/2022/day/9) |
| 10 | | | | [Day 10](https://adventofcode.com/2022/day/10) |