Add solutions for 2022:7 "No Space Left On Device"
tried to solve this one using only incrementing sums, which worked fine for the test input but not the actual puzzle input. By a complete rewrite to actually render the tree as a map, it worked. The spontanious data store is a mess and cost me much time to work around, since I wanted to have a list of tuples with all the sizes for each directory. Took 2 days to figure this one out. Not proud.
This commit is contained in:
parent
67ff3997f1
commit
bb708a5e58
2 changed files with 205 additions and 0 deletions
158
2022-elixir/lib/solutions/day_07.ex
Normal file
158
2022-elixir/lib/solutions/day_07.ex
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
defmodule Aoc.Solution.Day07 do
|
||||
import Aoc.Utils
|
||||
|
||||
@name "Day 7: No Space Left On Device"
|
||||
@behaviour Solution
|
||||
|
||||
@impl Solution
|
||||
def get_name, do: @name
|
||||
|
||||
@impl Solution
|
||||
def present(solution), do: "The small directories sizes sum is #{solution} (1644735)"
|
||||
|
||||
@impl Solution
|
||||
def present_again(solution), do: "The best dir to remove has a size of #{solution}"
|
||||
|
||||
@impl Solution
|
||||
def parse!(raw) do
|
||||
raw |> split_lines()
|
||||
end
|
||||
|
||||
@impl Solution
|
||||
def solve(output) do
|
||||
output
|
||||
|> traverse()
|
||||
|> small_dir_sum()
|
||||
end
|
||||
|
||||
@impl Solution
|
||||
def solve_again(output) do
|
||||
output
|
||||
|> traverse()
|
||||
|> removal_dir()
|
||||
end
|
||||
|
||||
def traverse(files) when is_list(files) do
|
||||
Enum.reduce(
|
||||
files,
|
||||
%{
|
||||
pwd: ["/"],
|
||||
tree: %{
|
||||
"/" => %{
|
||||
children: %{},
|
||||
files: []
|
||||
}
|
||||
}
|
||||
},
|
||||
&traverse/2
|
||||
)
|
||||
end
|
||||
|
||||
def traverse("$ cd /", tree) do
|
||||
%{tree | pwd: ["/"]}
|
||||
end
|
||||
|
||||
def traverse("$ cd ..", tree = %{pwd: path}) do
|
||||
%{tree | pwd: path |> Enum.drop(1)}
|
||||
end
|
||||
|
||||
def traverse("$ cd " <> dirname, tree = %{pwd: path}) do
|
||||
%{tree | pwd: [dirname | path]}
|
||||
end
|
||||
|
||||
def traverse("dir " <> dirname, state = %{pwd: pwd, tree: tree}) do
|
||||
pp = _path(pwd, :children)
|
||||
node = Map.put(get_in(tree, pp), dirname, %{children: %{}, files: []})
|
||||
%{state | tree: put_in(tree, pp, node)}
|
||||
end
|
||||
|
||||
def traverse("$ ls", tree) do
|
||||
tree
|
||||
end
|
||||
|
||||
def traverse(item, %{pwd: pwd, tree: tree}) do
|
||||
[size, _name] = String.split(item)
|
||||
pp = _path(pwd, :files)
|
||||
files = get_in(tree, pp)
|
||||
|
||||
%{
|
||||
pwd: pwd,
|
||||
tree: put_in(tree, pp, [String.to_integer(size) | files])
|
||||
}
|
||||
end
|
||||
|
||||
def _path(pwd, pos),
|
||||
do:
|
||||
[
|
||||
pos
|
||||
| pwd
|
||||
|> Enum.map(fn s -> [s, :children] end)
|
||||
]
|
||||
|> List.flatten()
|
||||
|> Enum.drop(-1)
|
||||
|> Enum.reverse()
|
||||
|
||||
def small_dir_sum(%{tree: tree}) do
|
||||
tree
|
||||
|> dir_sizes()
|
||||
|> Enum.filter(fn v -> v < 100_000 end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def removal_dir(%{tree: tree}) do
|
||||
needed = 30_000_000
|
||||
disk = 70_000_000
|
||||
|
||||
dirs =
|
||||
tree
|
||||
|> dir_sizes()
|
||||
|
||||
used = Enum.max(dirs)
|
||||
free = disk - used
|
||||
|
||||
dirs
|
||||
|> Enum.filter(fn s -> free + s > needed end)
|
||||
|> Enum.min()
|
||||
end
|
||||
|
||||
def dir_sizes(%{"/" => node}) do
|
||||
dir_sizes("/", node, [])
|
||||
|> Enum.map(fn {_k, v} -> v end)
|
||||
end
|
||||
|
||||
def dir_sizes(_name, children, seen) when is_list(children) do
|
||||
sums =
|
||||
Enum.map(children, fn {name, data} ->
|
||||
dir_sizes(name, data, seen)
|
||||
end)
|
||||
|> List.flatten()
|
||||
|
||||
sums ++ seen
|
||||
end
|
||||
|
||||
def dir_sizes(name, data, seen) do
|
||||
size = dir_size(data)
|
||||
|
||||
seen =
|
||||
case Enum.empty?(Map.get(data, :children)) do
|
||||
true -> seen
|
||||
false -> dir_sizes(name, Map.get(data, :children) |> Map.to_list(), seen)
|
||||
end
|
||||
|
||||
[{name, size} | seen]
|
||||
end
|
||||
|
||||
def dir_size(data) do
|
||||
size = Map.get(data, :files, []) |> Enum.sum()
|
||||
|
||||
child_size =
|
||||
data
|
||||
|> Map.get(:children)
|
||||
|> Enum.map(fn {_n, d} ->
|
||||
dir_size(d)
|
||||
end)
|
||||
|> Enum.sum()
|
||||
|
||||
size + child_size
|
||||
end
|
||||
end
|
||||
47
2022-elixir/test/solutions/day_07_test.exs
Normal file
47
2022-elixir/test/solutions/day_07_test.exs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
defmodule Day07Test do
|
||||
use ExUnit.Case
|
||||
doctest Aoc.Solution.Day07
|
||||
import Aoc.Solution.Day07
|
||||
|
||||
@input ~s(
|
||||
$ 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
|
||||
)
|
||||
|
||||
test "07: No Space Left On Device, part 1" do
|
||||
expected = 95437
|
||||
|
||||
result = @input |> parse!() |> solve()
|
||||
|
||||
assert result == expected
|
||||
end
|
||||
|
||||
test "07: No Space Left On Device, part 2" do
|
||||
expected = 24_933_642
|
||||
|
||||
result = @input |> parse!() |> solve_again()
|
||||
|
||||
assert result == expected
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue