Advent Of Code 2022 - Day 7
Day 7 - No Space Left On Device
See Day 7 for a detailed description of the problem.
Continuing to solve the Advent of Code 2022 problems (see Advent of Code - Day 1).
Links:
To run the example code in this post save the code into file such as
advent.jactl
and take your input from the Advent of Code site (e.g.advent.txt
) and run it like this:$ cat advent.txt | java -jar jactl-2.0.0.jar advent.jactl
Part 1
The puzzles are starting to get a little more complicated now.
For this one we have to parse some input consisting of cd
and ls
commands showing the contents of a file system
and then from that input find all directories whose total size (including children) is no more than 100000 and
calculate the total size of these directories.
For this solution we create a File
class and a Dir
class (which extends the File
class) to represent the files
and directories.
The only two methods that the classes implement are totalSize()
which for Files is the file size and for Dirs is
the combined size of all of its descendants, and descendants()
which returns a list of all the descendants.
For Files the descendants()
is just itself, while for Dirs the descendants()
returns all the children, and the
children of the children, etc.
class File {
String name
int size
def totalSize() { size }
def descendants() { this }
}
class Dir extends File {
Dir parent
Map children = [:]
def totalSize() { children.map{ it[1].totalSize() }.sum() }
def descendants() { [this] + children.map{ it[1] }.flatMap{ it.descendants() } }
}
Dir root = new Dir('/',0,null)
root.parent = root
Dir cwd = root
stream(nextLine).each{
/^\$ *cd +\/$/r and do { cwd = root; return } // cd /
/^\$ *cd +\.\.$/r and do { cwd = cwd.parent; return } // cd ..
/^\$ *cd +(.*)$/r and do { cwd = cwd.children[$1]; return } // cd some_dir
/^\$ *ls/r and return // nothing to do for ls
/^dir +(.*)$/r and cwd.children[$1] = new Dir($1,-1,cwd) // create directory
/^(\d+) +(.*)$/n and cwd.children[$2] = new File($2,$1) // use /.../n to get numeric size
}
root.descendants()
.filter{ it instanceof Dir }
.map{ it.totalSize() }
.filter{ it <= 100000 }
.sum()
Part 2
For part 2 we leave the parsing and creation of the data structures the same. The difference for part 2 is now we need to find a directory, that when deleted (along with its descendants) frees up enough space to leave a total free space of at least 30000000. Out of all the directories that we could delete we need the smallest one.
We know that the total disk space is 70000000, so we calculate the current free space by subtracting root.totalSize()
and then use this to work out how much we need to delete to get to our desired value.
Then, it is a simple matter of filtering for directories whose total size is greater than or equal to this threshold and return the size of the smallest such directory.
class File {
String name
int size
def totalSize() { size }
def descendants() { this }
}
class Dir extends File {
Dir parent
Map children = [:]
def totalSize() { children.map{ it[1].totalSize() }.sum() }
def descendants() { [this] + children.map{ it[1] }.flatMap{ it.descendants() } }
}
Dir root = new Dir('/',0,null)
root.parent = root
Dir cwd = root
stream(nextLine).each{
/^\$ *cd +\/$/r and do { cwd = root; return }
/^\$ *cd +\.\.$/r and do { cwd = cwd.parent; return }
/^\$ *cd +(.*)$/r and do { cwd = cwd.children[$1]; return }
/^\$ *ls/r and return
/^dir +(.*)$/r and cwd.children[$1] = new Dir($1,-1,cwd)
/^(\d+) +(.*)$/n and cwd.children[$2] = new File($2,$1)
}
def usedSize = root.totalSize()
def DISK_SIZE = 70000000
def freeSpace = DISK_SIZE - root.totalSize()
def NEEDED_FREE_SPACE = 30000000
def needToDelete = NEEDED_FREE_SPACE - freeSpace
root.descendants()
.filter{ it instanceof Dir }
.map{ it.totalSize() }
.filter{ it >= needToDelete }
.min()