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

Day 07

day07.livemd

Day 07

Support

defmodule AOCDirectory do
  defstruct name: "", tag: 0
end

defmodule AOCFile do
  defstruct name: "", size: 0
end

defmodule FileDirUtils do
  def subdir_with_name(tree, vertex, name) do
    :digraph.out_neighbours(tree, vertex)
    |> Enum.find(fn v ->
      %v_type{} = v
      v.name == name and v_type == AOCDirectory
    end)
  end

  def parent_dir(tree, vertex) do
    :digraph.in_neighbours(tree, vertex)
    |> List.first()
  end
end

defmodule FileDirUtilsTest do
  def test_subdir_with_name do
    tree = :digraph.new()
    a_vertex = :digraph.add_vertex(tree, %AOCDirectory{name: "a"})
    :digraph.add_vertex(tree, %AOCDirectory{name: "b"})
    :digraph.add_vertex(tree, %AOCDirectory{name: "c"})
    :digraph.add_vertex(tree, %AOCDirectory{name: "d"})
    :digraph.add_edge(tree, a_vertex, %AOCDirectory{name: "b"})
    :digraph.add_edge(tree, a_vertex, %AOCDirectory{name: "c"})
    :digraph.add_edge(tree, a_vertex, %AOCDirectory{name: "d"})

    FileDirUtils.subdir_with_name(tree, a_vertex, "b")
  end

  def test_parent_dir do
    tree = :digraph.new()
    a_vertex = :digraph.add_vertex(tree, %AOCDirectory{name: "a", tag: 1})
    b_vertex = :digraph.add_vertex(tree, %AOCDirectory{name: "b", tag: 2})
    c_vertex = :digraph.add_vertex(tree, %AOCDirectory{name: "c", tag: 3})
    d_vertex = :digraph.add_vertex(tree, %AOCDirectory{name: "b", tag: 4})
    :digraph.add_edge(tree, a_vertex, b_vertex)
    :digraph.add_edge(tree, b_vertex, c_vertex)
    :digraph.add_edge(tree, c_vertex, d_vertex)

    FileDirUtils.parent_dir(tree, d_vertex)
  end
end

FileDirUtilsTest.test_subdir_with_name()
# FileDirUtilsTest.test_parent_dir()

Setup

https://adventofcode.com/2022/day/07

defmodule Load do
  def input do
    File.read!(Path.join(Path.absname(__DIR__), "input/07.txt"))
  end
end
defmodule Parse do
  def dir_string(dir) do
    tag = Integer.to_string(dir.tag)
    dir.name <> " (" <> tag <> ")"
  end

  def create_tree_recursive(tree, _, [], _) do
    tree
  end

  def create_tree_recursive(tree, current_vertex, [command | commands], tag) do
    case command do
      "$ ls" ->
        create_tree_recursive(tree, current_vertex, commands, tag)

      "$ cd /" ->
        {root_dir, _} = :digraph.vertex(tree, %AOCDirectory{name: "/"})
        create_tree_recursive(tree, root_dir, commands, tag)

      "$ cd .." ->
        parent = FileDirUtils.parent_dir(tree, current_vertex)
        create_tree_recursive(tree, parent, commands, tag)

      "$ cd " <> dir_name ->
        target_vertex = FileDirUtils.subdir_with_name(tree, current_vertex, dir_name)
        create_tree_recursive(tree, target_vertex, commands, tag + 1)

      "dir " <> dir_name ->
        case FileDirUtils.subdir_with_name(tree, current_vertex, dir_name) do
          nil ->
            new_vertex = :digraph.add_vertex(tree, %AOCDirectory{name: dir_name, tag: tag})
            :digraph.add_edge(tree, current_vertex, new_vertex)

          _ ->
            {}
        end

        create_tree_recursive(tree, current_vertex, commands, tag + 1)

      file_line ->
        [size_str, name] = String.split(file_line, " ")
        {size, _} = Integer.parse(size_str)
        file = %AOCFile{name: name, size: size}
        new_vertex = :digraph.add_vertex(tree, file)
        :digraph.add_edge(tree, current_vertex, new_vertex)
        create_tree_recursive(tree, current_vertex, commands, tag)
    end
  end

  def input(inputString) do
    dir_tree = :digraph.new()
    root_dir = :digraph.add_vertex(dir_tree, %AOCDirectory{name: "/", tag: 0})

    input_lines =
      inputString
      |> String.split("\n", trim: true)

    create_tree_recursive(dir_tree, root_dir, input_lines, 0)
  end
end

testInput =
  """
  $ 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
  """
  |> Parse.input()
realInput =
  Load.input()
  |> Parse.input()

Part 1

defmodule Part1 do
  def all_file_descendents(tree, current_dir) do
    Enum.map(:digraph.out_neighbours(tree, current_dir), fn
      %AOCFile{} = file ->
        [file]

      %AOCDirectory{} = dir ->
        all_file_descendents(tree, dir)
    end)
    |> List.flatten()
  end

  def all_files_size(tree, current_dir) do
    all_file_descendents(tree, current_dir)
    |> Enum.map(fn v -> v.size end)
    |> Enum.sum()
  end

  def solve(input) do
    :digraph.vertices(input)
    |> Enum.filter(fn v ->
      %v_type{} = v
      v_type == AOCDirectory
    end)
    |> Enum.map(fn v ->
      {v.name, all_files_size(input, v)}
    end)
    # all_file_descendents(input, root_dir)
    |> Enum.filter(fn {_, size} ->
      size <= 100_000
    end)
    |> Enum.map(fn {_, size} -> size end)
    |> Enum.sum()
  end
end

Part1.solve(testInput)
Part1.solve(realInput)

Part 2

defmodule Part2 do
  def solve(input) do
    total_space = 70_000_000
    unused_space_needed = 30_000_000

    {root_dir, _} = :digraph.vertex(input, %AOCDirectory{name: "/", tag: 0})
    used_space = Part1.all_files_size(input, root_dir)
    free_space = total_space - used_space
    space_to_free = unused_space_needed - free_space

    :digraph.vertices(input)
    |> Enum.filter(fn v ->
      %v_type{} = v
      v_type == AOCDirectory
    end)
    |> Enum.map(fn v ->
      {v.name, Part1.all_files_size(input, v)}
    end)
    |> Enum.filter(fn {_, size} ->
      size >= space_to_free
    end)
    |> Enum.map(fn {_, size} -> size end)
    |> Enum.min()
  end
end

Part2.solve(testInput)
Part2.solve(realInput)