Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a749bc7
Create module for `Directory` class
katzuv Dec 14, 2022
4f5f042
Create `Directory` class
katzuv Dec 14, 2022
940c6a6
Add constructor to `Directory` class
katzuv Dec 14, 2022
dbd8cf6
Create module for `File` class
katzuv Dec 14, 2022
196273f
Create `File` class
katzuv Dec 14, 2022
4ef518b
Add "name" field to `File` class
katzuv Dec 14, 2022
9545a4a
Add "size" field to `File` class
katzuv Dec 14, 2022
d459af9
Add method that adds a file to a directory
katzuv Dec 14, 2022
07c9df3
Add method that adds a subdirectory to a directory
katzuv Dec 14, 2022
facb4cc
Create solution file for part 1
katzuv Dec 14, 2022
60b0e41
Add constants module
katzuv Dec 14, 2022
43b8b1f
Add constant for commands seperator
katzuv Dec 14, 2022
5269b48
Create module for input parsing functions
katzuv Dec 14, 2022
05fd0f2
Add constant for parent directory symbol
katzuv Dec 15, 2022
70999b7
Create function that returns next directory according to `cd` command
katzuv Dec 14, 2022
73d05d1
Add constant for the directory symbol
katzuv Dec 14, 2022
609b671
Add `property` of the files to `Directory`
katzuv Dec 15, 2022
41cc799
Add `property` of the subdirectories to `Directory`
katzuv Dec 15, 2022
35ef210
Add function that adds contents to a directory based on `ls` output
katzuv Dec 15, 2022
602fca0
Add constant for `cd` command name
katzuv Dec 15, 2022
7b25866
Add constant for `ls` command name
katzuv Dec 15, 2022
8e4b891
Add constant for root directory symbol
katzuv Dec 15, 2022
ea0cd9e
Add `property` of size to `Directory`
katzuv Dec 16, 2022
22b2d73
Add `__str__` magic method to `Directory`
katzuv Dec 16, 2022
31d0a69
Add function that returns root directory after parsing terminal output
katzuv Dec 16, 2022
9bb0906
Add function that returns commands from the terminal output
katzuv Dec 16, 2022
ccdea66
Add function that returns all subdirectories under a given directory
katzuv Dec 16, 2022
09aac96
Add constant for small directory size threshold
katzuv Dec 16, 2022
9c944b1
Add wrapper function that returns filesystem based on terminal output
katzuv Dec 17, 2022
37ccc50
Add function that returns sum of sizes of all small directories
katzuv Dec 16, 2022
fd56428
Create solution file for part 2
katzuv Dec 16, 2022
4bdfcfd
Add constant for total disk space
katzuv Dec 16, 2022
f15e860
Add constant for required unused space
katzuv Dec 16, 2022
43dfb1e
Add function that size of the smallest directory that should be deleted
katzuv Dec 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions puzzles/solutions/2022/d07/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
COMMANDS_SEPARATOR = "$"
DIRECTORY_SYMBOL = "dir"
PARENT_DIRECTORY_SYMBOL = ".."
ROOT_DIRECTORY_SYMBOL = "/"
CHANGE_DIRECTORY_COMMAND = "cd"
LIST_CONTENTS_COMMAND = "ls"
SMALL_DIRECTORY_SIZE_THRESHOLD = 100_000
TOTAL_DISK_SPACE = 70_000_000
REQUIRED_UNUSED_SPACE = 30_000_000
64 changes: 64 additions & 0 deletions puzzles/solutions/2022/d07/directory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from typing import Self

from file import File


class Directory:
"""A directory which can store files and subdirectories."""

def __init__(self, name: str, parent: Self = None):
"""
Instantiate a directory.
:param name: name of the directory
:param parent: parent directory, if exists
"""
self.name = name
self.parent = parent
self._files = []
self._subdirectories = []

def __str__(self):
"""
:return: string representation of the directory, including its name, size, files, and subdirectories
"""
files = ", ".join(file.name for file in self.files) or "no files"
subdirectories = (
", ".join(subdirectory.name for subdirectory in self.subdirectories)
or "no subdirs"
)
return f"{self.name} ({self.size}): {files}; {subdirectories}"

def add_file(self, file: File) -> None:
"""
:param file: file to add to the directory
"""
self._files.append(file)

def add_subdirectory(self, subdirectory: Self) -> None:
"""
:param subdirectory: subdirectory to add to the directory
"""
self._subdirectories.append(subdirectory)

@property
def files(self) -> tuple[File, ...]:
"""
:return: files in the directory
"""
return tuple(self._files)

@property
def subdirectories(self) -> tuple[Self, ...]:
"""
:return: subdirectories of the directory
"""
return tuple(self._subdirectories)

@property
def size(self) -> int:
"""
:return: size of the files and subdirectories in the directory
"""
return sum(file.size for file in self.files) + sum(
subdirectory.size for subdirectory in self.subdirectories
)
9 changes: 9 additions & 0 deletions puzzles/solutions/2022/d07/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import dataclasses


@dataclasses.dataclass
class File:
"""A file with a name and size."""

name: str
size: int
33 changes: 33 additions & 0 deletions puzzles/solutions/2022/d07/input_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import consts
from directory import Directory
from file import File


def get_next_directory(current_directory: Directory, command: str) -> Directory:
"""
:param current_directory: current directory
:param command: `cd` command to parse
:return: `cd` command object
"""
destination = command.split()[1]
if destination == consts.PARENT_DIRECTORY_SYMBOL:
return current_directory.parent
for subdirectory in current_directory.subdirectories:
if subdirectory.name == destination:
return subdirectory


def handle_ls_command(directory: Directory, command: str) -> None:
"""
Add contents to the given directory, according to the given `ls` command output.
:param command: `ls` command output
:param directory: directory to add contents to
"""
contents = command.splitlines()[1:]
for content in contents:
if content.startswith(consts.DIRECTORY_SYMBOL):
directory_name = content.split()[1]
directory.add_subdirectory(Directory(directory_name, directory))
else:
size, filename = content.split()
directory.add_file(File(filename, int(size)))
81 changes: 81 additions & 0 deletions puzzles/solutions/2022/d07/p1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import sys
from typing import Sequence

import consts
import input_parsing
from directory import Directory


def get_commands(input_text: str) -> tuple[str, ...]:
"""
:param input_text: puzzle input
:return: tuple of commands from the terminal output
"""
# Remove the first `$` so we don't have an empty string after running `split()`.
input_text = input_text[1:]
# First command is `cd /` which we can skip.
commands = input_text.split(consts.COMMANDS_SEPARATOR)[1:]
return tuple(command.strip() for command in commands)


def build_filesystem(commands: Sequence[str]) -> Directory:
"""
Build the filesystem according to the given commands and return the root directory.
:param commands: commands output
:return: root directory
"""
root = Directory(consts.ROOT_DIRECTORY_SYMBOL)
current_directory = root
for command in commands:
if command.startswith(consts.CHANGE_DIRECTORY_COMMAND):
current_directory = input_parsing.get_next_directory(
current_directory, command
)
elif command.startswith(consts.LIST_CONTENTS_COMMAND):
input_parsing.handle_ls_command(current_directory, command)

return root


def get_all_subdirectories(
directory: Directory, subdirectories=None
) -> list[Directory]:
"""
:param directory: directory to traverse
:param subdirectories: list to add subdirectories to
:return: subdirectories under the given directory
"""
if subdirectories is None:
subdirectories = list()
subdirectories.append(directory)
for subdirectory in directory.subdirectories:
subdirectories.extend(get_all_subdirectories(subdirectory))
return subdirectories


def get_filesystem(terminal_output: str) -> list[Directory]:
"""
:param terminal_output: puzzle input
:return: filesystem built according to the terminal output
"""
commands = get_commands(terminal_output)
root = build_filesystem(commands)
filesystem = get_all_subdirectories(root)
return filesystem


def get_answer(input_text: str):
"""Return the sum of the total sizes of all the directories with a total size of at most 100000."""
filesystem = get_filesystem(input_text)
return sum(
directory.size
for directory in filesystem
if directory.size < consts.SMALL_DIRECTORY_SIZE_THRESHOLD
)


if __name__ == "__main__":
try:
print(get_answer(sys.argv[1]))
except IndexError:
pass # Don't crash if no input was passed through command line arguments.
28 changes: 28 additions & 0 deletions puzzles/solutions/2022/d07/p2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import sys

import consts
import p1


def get_answer(input_text: str):
"""Return total size of the smallest directory that, if deleted, would free up enough space on the filesystem."""
filesystem = p1.get_filesystem(input_text)
root = filesystem[0]
total_used_space = root.size
unused_space_needed = consts.REQUIRED_UNUSED_SPACE - (
consts.TOTAL_DISK_SPACE - total_used_space
)
candidate_directories = (
directory for directory in filesystem if directory.size >= unused_space_needed
)
directory_to_delete = min(
candidate_directories, key=lambda directory: directory.size
)
return directory_to_delete.size


if __name__ == "__main__":
try:
print(get_answer(sys.argv[1]))
except IndexError:
pass # Don't crash if no input was passed through command line arguments.