Compare commits

...

2 Commits

Author SHA1 Message Date
Danila Fedorin 6ba1b3f326 Tweak day 7 solution somewhat and start writing 2022-12-07 00:02:53 -08:00
Danila Fedorin 6029164067 Add day 7 solutions 2022-12-06 23:02:10 -08:00
2 changed files with 174 additions and 0 deletions

110
day7.chpl Normal file
View File

@ -0,0 +1,110 @@
// Advent of Code 2022, Day 7: Traversing Directories
// authors: ["Daniel Fedorin"]
// summary: "A solution to day seven of AoC 2022, introducing classes and memory management."
// tags: ["Advent of Code 2022"]
// date: 2022-12-07
/*
Welcome to day 7 of Chapel's Advent of Code 2022 series. We're over halway
through the twelve days of Chapel AoC! In case you haven't been following
the series, check out the introductory [Advent of Code 2022: Twelve
Days of Chapel](../aoc2022-day00-intro/) article for more context.
*/
/*
### The Task at Hand and My Approach
In today's puzzle, we are given a list of terminal-like commands (
[`ls`](https://man7.org/linux/man-pages/man1/ls.1.html) and [`cd`](https://man7.org/linux/man-pages/man1/cd.1p.html)
), as well as output corresponding to running these commands. The commands
explore a fictional file system, which can have files (objects with size)
as well as directories that group files and other (sub-)directories. The
problem then asks to compute the sizes of each folder, and to total up the
sizes of all folders that are smaller than a particular threshold.
The tree-like nature of the file system does not make it amenable to
representations based on arrays, lists, and maps alone. The trouble with
these data types is that they're flat. Our input could -- and will -- have arbitrary
levels of nested directories. However, arrays, lists, and maps cannot have
such arbitrary nesting -- we'd need something like a list of lists of lists...
We could, of course, use the `map` and `list` data types to represent the
file system with some sort of [adjacency list](https://en.wikipedia.org/wiki/Adjacency_list).
However, such an implementation would be somewhat clunky and hard to use.
Instead, we'll use a different tool from the repertoire of Chapel language
features, one we haven't seen so far: classes. Much like in most languages,
classes are a way to group together related pieces of data. up until now,
we've used tuples for this purpose.
*/
use IO, Map, List;
class TreeNode {
var name: string;
var files = new map(string, int);
var dirs = new list(owned TreeNode);
proc init(name: string) {
this.name = name;
}
/*
```Chapel
iter these(param tag: iterKind): (string, int) where tag == iterKind.standalone {
var size = + reduce files.values();
coforall dir in dirs with (+ reduce size) {
// Yield directory sizes from the dir.
forall subSize in dir do yield subSize;
// Count its size for our size.
size += dir.size;
}
yield (name, size);
this.size = size;
}
```
*/
iter dirSizes(ref parentSize = 0): (string, int) {
var size = + reduce files.values();
for dir in dirs {
// Yield directory sizes from the dir.
for subSize in dir.dirSizes(size) do yield subSize;
}
yield (name, size);
parentSize += size;
}
proc type fromInput(name: string, readFrom): owned TreeNode {
var line: string;
var newDir = new TreeNode(name);
while readFrom.readLine(line, stripNewline = true) {
if line == "$ cd .." {
break;
} else if line.startsWith("$ cd ") {
const dirName = line["$ cd ".size..];
newDir.dirs.append(TreeNode.fromInput(dirName, readFrom));
} else if !line.startsWith("$ ls") {
const (sizeOrDir, _, name) = line.partition(" ");
if sizeOrDir == "dir" {
// Ignore directories, we'll `cd` into them.
} else {
newDir.files[name] = sizeOrDir : int;
}
}
}
return newDir;
}
}
var rootFolder = TreeNode.fromInput("", stdin);
var rootSize = 0;
writeln(+ reduce [(_, size) in rootFolder.dirSizes(rootSize)] if size < 100000 then size);
const toDelete = rootSize - 40000000;
writeln(min reduce [(_, size) in rootFolder.dirSizes()] if size >= toDelete then size);

64
day7.cr Normal file
View File

@ -0,0 +1,64 @@
require "advent"
INPUT = input(2022, 7).lines[1..]
class DirTree
property name : String
property files : Hash(String, Int64)
property subDirs : Array(DirTree)
property parent : DirTree?
def initialize(@name, @parent)
@files = {} of String => Int64
@subDirs= [] of DirTree
@parent.try &.subDirs.<<(self)
end
def sum_yielding(&block : Int32 ->): Int32
size = 0
size += @subDirs.sum do |x|
x.sum_yielding(&block)
end
size += @files.sum { |k,v| v }
yield size
return size
end
end
dir = DirTree.new("", nil)
INPUT.each do |line|
if line =~ /\$ cd (.+)$/
if $1 == ".."
dir = dir.parent.not_nil!
else
dir = DirTree.new($1, dir)
end
elsif line == "$ ls"
else
x, y = line.split(" ")
if x == "dir"
else
dir.files[y] = x.to_i64
end
end
end
while par = dir.parent
dir = par
end
total = 0
outer_size = dir.sum_yielding do |i|
total += i if i <= 100000
end
puts total
puts "Used: #{outer_size}"
puts "Unused: #{70000000 - outer_size}"
to_delete = 30000000 - (70000000 - outer_size)
puts "To delete: #{to_delete}"
big_enough = [] of Int32
outer_size = dir.sum_yielding do |i|
big_enough << i if i >= to_delete
end
puts big_enough.min