Powered by AppSignal & Oban Pro

Day 7: No Space Left On Device

2022/day07.livemd

Day 7: No Space Left On Device

Mix.install([:kino])

Section

input = Kino.Input.textarea("input")
commands =
  input
  |> Kino.Input.read()
  |> String.split("$", trim: true)
  |> Enum.map(fn cmd ->
    String.split(cmd, [" ", "\n"], trim: true)
  end)

defmodule FS do
  def build([], _path), do: []

  def build([["cd", ".."] | commands], [_ | path]) do
    build(commands, path)
  end

  def build([["cd", "/"] | commands], path) do
    build(commands, path)
  end

  def build([["cd", dir] | commands], path) do
    build(commands, [dir | path])
  end

  def build([["ls" | ls] | commands], path) do
    Enum.chunk_every(ls, 2)
    |> Enum.reject(fn [dir, _] -> dir == "dir" end)
    |> Enum.reduce(0, fn [size, _file], acc ->
      acc + String.to_integer(size)
    end)
    |> then(&[{path, &1} | build(commands, path)])
  end

  def calc_sizes([]), do: []

  def calc_sizes([path | paths]) do
    paths
    |> Enum.filter(fn {p, _s} -> List.starts_with?(p, elem(path, 0)) end)
    |> then(fn subdirs ->
      [
        {elem(path, 0),
         Enum.reduce(subdirs, elem(path, 1), fn
           {_, s}, acc ->
             s + acc
         end)}
        | calc_sizes(paths)
      ]
    end)
  end
end

FS.build(commands, [])
|> Enum.sort_by(fn {path, _size} -> length(path) end)
|> Enum.map(fn {k, v} -> {Enum.reverse(k), v} end)
|> FS.calc_sizes()
|> Enum.filter(fn {_, s} -> s <= 100_000 end)
|> Enum.map(&elem(&1, 1))
|> Enum.sum()
FS.build(commands, [])
|> Enum.sort_by(fn {path, _size} -> length(path) end)
|> Enum.map(fn {k, v} -> {Enum.reverse(k), v} end)
|> FS.calc_sizes()
|> then(fn list ->
  {_, s} = hd(list)

  space = 30_000_000 - (70_000_000 - s)

  Enum.reject(list, fn {_, s} -> s < space end)
end)
|> Enum.min_by(&elem(&1, 1))
|> elem(1)