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

Day 7

day7.livemd

Day 7

Section

input = """
$ 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
"""
defmodule Dir do
  defstruct [:parent, :name, children: [], total_size: 0]
end

defmodule File do
  defstruct [:size]
end

defmodule FS do
  def find_child(fs, id, name) do
    Enum.find(fs[id].children, fn id ->
      case fs[id] do
        %Dir{name: ^name} -> true
        _ -> false
      end
    end)
  end

  def add_dir(fs, id, name) do
    case find_child(fs, id, name) do
      nil ->
        new_id = map_size(fs)

        fs =
          fs
          |> Map.update!(id, fn d -> %Dir{d | children: [new_id | d.children]} end)
          |> Map.put(new_id, %Dir{name: name, parent: id})

        {fs, new_id}

      child ->
        {fs, child}
    end
  end

  def add_to_size(fs, nil, _), do: fs

  def add_to_size(fs, id, size) do
    fs = Map.update!(fs, id, fn d -> %Dir{d | total_size: size + d.total_size} end)
    add_to_size(fs, fs[id].parent, size)
  end

  def add_file(fs, id, size) do
    new_id = map_size(fs)

    fs
    |> Map.update!(id, fn d -> %Dir{d | children: [new_id | d.children]} end)
    |> Map.put(new_id, %File{size: size})
    |> add_to_size(id, size)
  end
end
fs = %{0 => %Dir{name: "a", children: [2]}, 1 => %Dir{name: "b"}, 2 => %Dir{name: "c"}}
FS.find_child(fs, 1, "b")
{fs, _} = FS.add_dir(fs, 0, "new")
FS.add_file(fs, 2, 582)
fs = %{0 => %Dir{}}

input
|> String.split("\n", trim: true)
|> Enum.reduce({fs, 0}, fn line, {fs, cwd} ->
  case line do
    "$ cd /" ->
      {fs, 0}

    "$ cd .." ->
      {fs, fs[cwd].parent}

    "$ cd " <> d ->
      FS.add_dir(fs, cwd, d)

    "$ ls" ->
      {fs, cwd}

    "dir " <> d ->
      {fs, _} = FS.add_dir(fs, cwd, d)
      {fs, cwd}

    s ->
      size = s |> String.split() |> List.first() |> String.to_integer()
      {FS.add_file(fs, cwd, size), cwd}
  end
end)
|> elem(0)
|> Map.values()
|> Enum.flat_map(fn
  %Dir{total_size: total_size} when total_size <= 100_000 -> [total_size]
  _ -> []
end)
|> Enum.sum()
fs = %{0 => %Dir{}}

fs =
  input
  |> String.split("\n", trim: true)
  |> Enum.reduce({fs, 0}, fn line, {fs, cwd} ->
    case line do
      "$ cd /" ->
        {fs, 0}

      "$ cd .." ->
        {fs, fs[cwd].parent}

      "$ cd " <> d ->
        FS.add_dir(fs, cwd, d)

      "$ ls" ->
        {fs, cwd}

      "dir " <> d ->
        {fs, _} = FS.add_dir(fs, cwd, d)
        {fs, cwd}

      s ->
        size = s |> String.split() |> List.first() |> String.to_integer()
        {FS.add_file(fs, cwd, size), cwd}
    end
  end)
  |> elem(0)

need_to_free = 30_000_000 - (70_000_000 - fs[0].total_size)

fs
|> Map.values()
|> Enum.flat_map(fn
  %Dir{total_size: total_size} when total_size >= need_to_free -> [total_size]
  _ -> []
end)
|> Enum.sort()
|> List.first()