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

Day7

day07.livemd

Day7

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

Input

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

GenServer

defmodule FileServer do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
  end

  @impl true
  def init(_) do
    {:ok, %{}}
  end

  def flush() do
    GenServer.call(__MODULE__, :flush)
  end

  def get_dir(path) do
    GenServer.call(__MODULE__, {:get, path})
  end

  def list_all_dirs() do
    GenServer.call(__MODULE__, :list_all)
  end

  def list_all_under(max_size) do
    GenServer.call(__MODULE__, {:all_under, max_size})
  end

  def increase_dir_size(path, size) do
    GenServer.call(__MODULE__, {:increase_dir_size, path, size})
  end

  @impl true
  def handle_call(:flush, _, _) do
    {:reply, "", %{}}
  end

  @impl true
  def handle_call({:get, path}, _, state) do
    {:reply, Map.get(state, path, 0), state}
  end

  @impl true
  def handle_call(:list_all, _, state) do
    {:reply, state, state}
  end

  @impl true
  def handle_call({:all_under, max_size}, _, state) do
    {:reply, Enum.filter(state, fn {_, v} -> v < max_size end), state}
  end

  @impl true
  def handle_call({:increase_dir_size, path, size}, _, state) do
    {:reply, path, change_dir_size(state, path, size)}
  end

  defp change_dir_size(state, path, offset) do
    current_dir_size = Map.get(state, path, 0)
    new_dir_size = current_dir_size + offset

    state =
      case path do
        "/" -> state
        _ -> change_dir_size(state, previous_path(path), offset)
      end

    Map.put(state, path, new_dir_size)
  end

  def next_path(path, dirname) do
    [path, dirname]
    |> Enum.reject(&amp;(&amp;1 == ""))
    |> Path.join()
  end

  def previous_path(path) do
    path
    |> Path.split()
    |> Enum.reverse()
    |> Enum.drop(1)
    |> Enum.reverse()
    |> Path.join()
  end
end

Starting GenServer

FileServer.start_link([])

Register data into GenServer

GenServer.call(FileServer, :flush)

input
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.reduce("", fn line, current_path ->
  case String.split(line, " ", trim: true) do
    [_, "cd", ".."] ->
      FileServer.previous_path(current_path)

    [_, "cd", dirname] ->
      FileServer.next_path(current_path, dirname)

    [size, _] when size not in ["$", "dir"] ->
      size = String.to_integer(size)

      FileServer.increase_dir_size(current_path, size)

      current_path

    _ ->
      current_path
  end
end)

Part 1

100_000
|> FileServer.list_all_under()
|> Enum.map(fn {_, v} -> v end)
|> Enum.sum()

Part 2

total_disk_size = 70_000_000
required_space = 30_000_000

space_taken = FileServer.get_dir("/")

unused_space = total_disk_size - space_taken
missing_space = required_space - unused_space

FileServer.list_all_dirs()
|> Enum.reject(fn {_k, v} -> v < missing_space end)
|> Enum.sort_by(fn {_k, v} -> v end)
|> Enum.take(1)