Powered by AppSignal & Oban Pro

Advent of Code 2022

2022/day07.livemd

Advent of Code 2022

Mix.install([
  {:req, "~> 0.3.2"}
])

Day 7

input =
  "https://adventofcode.com/2022/day/7/input"
  |> Req.get!(headers: [cookie: "session=#{System.get_env("AOC_COOKIE")}"])
  |> Map.get(:body)
sample = """
$ 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
"""
lines =
  input
  # sample
  |> String.split("\n", trim: true)

Part 1

{_cwd, dirs} =
  lines
  |> Enum.reduce({nil, %{}}, fn
    "$ cd /", {_, tree} ->
      {"/", Map.put(tree, "/", [])}

    "$ ls", acc ->
      acc

    <<"dir " <> dir>>, {cwd, tree} ->
      dir =
        Path.join(cwd, dir)
        |> IO.inspect(label: "dir")

      tree =
        Map.update!(tree, cwd, &[{:dir, dir} | &1])
        |> Map.update(dir, [], & &1)

      {cwd, tree}

    "$ cd ..", {cwd, tree} ->
      IO.puts("cd ..")

      cwd =
        String.split(cwd, "/")
        |> Enum.reverse()
        |> tl()
        |> Enum.reverse()
        |> Enum.join("/")
        |> then(fn
          "" -> "/"
          path -> path
        end)
        |> IO.inspect(label: "$")

      {cwd, tree}

    <<"$ cd " <> dir>>, {cwd, tree} ->
      cwd = Path.join(cwd, dir)

      {cwd, tree}

    file, {cwd, tree} ->
      [size, file] = String.split(file, " ")
      {cwd, Map.update!(tree, cwd, &[{:file, file, String.to_integer(size)} | &1])}
  end)
defmodule Day7 do
  def build_tree(path, dirs) do
    children =
      Map.get(dirs, path)
      |> Enum.map(fn
        {:dir, dir} -> build_tree(dir, dirs)
        {:file, _, _} = file -> file
      end)

    {:dir, path, children}
  end

  def calculate_sizes(tree) do
    {:dir, path, children} = tree

    get_sizes(path, children, %{})
    |> elem(0)
  end

  defp get_sizes(path, nodes, sizes) do
    # path |> IO.inspect(label: "path")
    {sizes, total} =
      Enum.reduce(nodes, {sizes, 0}, fn
        {:dir, path, children}, {sizes, total} ->
          {sizes, dir_size} = get_sizes(path, children, sizes)
          {sizes, dir_size + total}

        {:file, _name, size}, {sizes, total} ->
          {sizes, size + total}
      end)

    {Map.put(sizes, path, total), total}
  end
end

tree =
  Day7.build_tree("/", dirs)
  |> Day7.calculate_sizes()
  |> Enum.map(fn {_k, v} -> v end)
  |> Enum.filter(&(&1 <= 100_000))
  |> Enum.sum()

Part 2

sizes =
  Day7.build_tree("/", dirs)
  |> Day7.calculate_sizes()

total_disk = 70_000_000
required = 30_000_000
current = Map.get(sizes, "/")
to_delete = current - (total_disk - required)
sizes
|> Enum.map(fn {_k, v} -> v end)
|> Enum.filter(&(&1 >= to_delete))
|> Enum.min()