Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

AOC 2022 Day 07

2022/day07/solution.livemd

AOC 2022 Day 07

Section

Mix.install([
  {:kino, "~> 0.7.0"}
])

Inputs

input = Kino.Input.textarea("Input")
test_input = Kino.Input.textarea("Input")

Structs that ended up being useless and are now a shell of what they were when I originally built them out

defmodule Dir do
  defstruct contents: []
end

defmodule F do
  defstruct [:name, size: 0]
end

The main functions

defmodule Terminal do
  def build(lines, current \\ nil, filesystem \\ %{})
  def build([], _, filesystem), do: filesystem

  def build([line | rest], current, filesystem) do
    {current, filesystem} =
      case line do
        ["$", cmd, dir] -> command(cmd, current, dir, filesystem)
        ["$", "ls"] -> {current, filesystem}
        ["dir", name] -> add_dir(name, current, filesystem)
        [size, name] -> add_file(String.to_integer(size), name, current, filesystem)
      end

    build(rest, current, filesystem)
  end

  defp command("cd", current, to, filesystem) do
    case to do
      "/" ->
        if filesystem[to] do
          {to, filesystem}
        else
          fs = filesystem |> Map.put(to, %Dir{})
          {to, fs}
        end

      _ ->
        path = Path.expand(to, current)

        if filesystem[path] do
          {path, filesystem}
        else
          fs = filesystem |> Map.put(path, %Dir{})
          {path, fs}
        end
    end
  end

  defp add_dir(name, current, filesystem) do
    curr = filesystem[current]
    path = Path.expand(name, current)

    content = [path | curr.contents]
    fs = %{filesystem | current => %{curr | contents: content}}

    {current, fs}
  end

  defp add_file(size, name, current, filesystem) do
    curr = filesystem[current]
    f = %F{name: name, size: size}

    new_content = [f | curr.contents]

    fs = %{filesystem | current => %{curr | contents: new_content}}

    {current, fs}
  end

  def get_size(fs, name) do
    fs[name].contents
    |> Enum.map(fn x ->
      case x do
        %F{} -> x.size
        _ -> get_size(fs, x)
      end
    end)
    |> Enum.sum()
  end
end

Get the data and build the file tree

data =
  Kino.Input.read(input)
  |> String.split("\n", trim: true)
  |> Enum.map(&String.split(&1, " "))
filesystem = data |> Terminal.build()

Get all the sizes

sizes =
  filesystem
  |> Enum.map(fn {k, _} ->
    Terminal.get_size(filesystem, k)
  end)

Part 1

sizes
|> Enum.reduce(0, fn num, acc ->
  if num > 100_000, do: acc, else: acc + num
end)

Part 2

max_space = 70_000_000
needed_space = 30_000_000

[top | sorted] =
  sizes
  |> Enum.sort(:desc)

free_space = max_space - top

sizes
|> Enum.filter(fn x ->
  x + free_space >= needed_space
end)
|> Enum.min()