Powered by AppSignal & Oban Pro

Advent of code - Day 7

day_7.livemd

Advent of code - Day 7

Mix.install(
  [
    {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
  ],
  force: true
)

Section

{:ok, puzzle_input} = KinoAOC.download_puzzle("2022", "7", System.fetch_env!("LB_SESSION"))
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 AddSize do
  def add(%{size: _} = m), do: m

  def add(m) do
    new_map = Enum.reduce(m, %{}, fn {k, v}, acc -> Map.put(acc, k, add(v)) end)
    size = Enum.reduce(new_map, 0, fn {_, %{size: size}}, acc -> acc + size end)

    Map.put(new_map, :size, size)
  end
end

tree =
  puzzle_input
  |> String.split("\n")
  |> Enum.reduce({%{}, []}, fn command, {directories, current_path} ->
    case command do
      "$ cd /" ->
        {directories, []}

      "$ cd .." ->
        {
          directories,
          case current_path do
            [_ | t] -> t
            _ -> current_path
          end
        }

      "$ cd " <> dirname ->
        {directories, [dirname | current_path]}

      "dir " <> dirname ->
        {
          put_in(
            directories,
            Enum.reverse([dirname | current_path]),
            %{}
          ),
          current_path
        }

      "$ ls" ->
        {directories, current_path}

      fsize_name ->
        with [fsize, fname] <- String.split(fsize_name) do
          {
            put_in(
              directories,
              Enum.reverse([fname | current_path]),
              %{size: String.to_integer(fsize)}
            ),
            current_path
          }
        else
          _ ->
            {directories, current_path}
        end
    end
  end)
  |> elem(0)
  |> AddSize.add()
defmodule Search do
  def search(%{size: size} = dirs) when map_size(dirs) > 1 do
    debut =
      if size <= 100_000 do
        size
      else
        0
      end

    dirs
    |> Enum.reject(fn {k, _} -> k == :size end)
    |> Enum.reduce(debut, fn {_, x}, acc -> acc + search(x) end)
  end

  def search(_), do: 0
end

Search.search(tree)

Part2

total_space = 70_000_000
free_space = total_space - tree[:size]
needed_space = 30_000_000 - free_space

defmodule Search2 do
  def search(dirs, needed_space) when map_size(dirs) > 1 do
    dirs
    |> Enum.reduce([], fn
      {k, %{size: size} = m}, acc when size >= needed_space ->
        acc ++ [{k, size}] ++ search(m, needed_space)

      {_, m}, acc ->
        acc ++ search(m, needed_space)
    end)
  end

  def search(_, _), do: []
end

tree
|> Search2.search(needed_space)
|> Enum.min_by(fn {_, s} -> s end)
|> elem(1)