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

🎄 Year 2024 🔔 Day 09

elixir/notebooks/2024/day09.livemd

🎄 Year 2024 🔔 Day 09

Mix.install([
  {:arrays, "~> 2.1"}
])

Setup

elems =
  File.read!("#{__DIR__}/../../../inputs/2024/day09.txt")
  # "2333133121414131402"
  |> String.trim()
  |> String.graphemes()
  |> Enum.with_index(fn e, i ->
    asdf = if rem(i, 2) == 0, do: :file, else: :free
    {String.to_integer(e), asdf}
  end)
  |> Enum.flat_map_reduce(0, fn
    {count, :file}, acc ->
      {List.duplicate(acc, count), acc + 1}

    {count, :free}, acc ->
      {List.duplicate(nil, count), acc}
  end)
  |> elem(0)

{file_indexes, max_i} =
  File.read!("#{__DIR__}/../../../inputs/2024/day09.txt")
  # "2333133121414131402"
  |> String.trim()
  |> String.graphemes()
  |> Enum.with_index(fn e, i ->
    file_n = if rem(i, 2) == 0, do: div(i, 2), else: nil
    {String.to_integer(e), file_n}
  end)
  |> Enum.map_reduce(0, fn {count, file_n}, start_i ->
    {{file_n, start_i, count}, start_i + count}
  end)
  |> then(fn {elems, max_i} -> {elems, max_i - 1} end)

Part 1

defmodule Helper do
  def solve(elems) do
    elems = Enum.with_index(elems)
    reversed = elems |> Enum.reverse()
    solve(elems, reversed, [])
  end

  defp solve([], _, result), do: Enum.reverse(result)

  defp solve(alist = [{a, ai} | atail], blist = [{b, bi} | btail], result) do
    cond do
      b == nil -> solve(alist, btail, result)
      bi < ai -> solve(atail, blist, [nil | result])
      a != nil -> solve(atail, blist, [a | result])
      a == nil -> solve(atail, btail, [b | result])
    end
  end
end

Helper.solve(elems)
|> Enum.filter(fn e -> e != nil end)
|> Enum.with_index(fn e, i -> e * i end)
|> Enum.sum()

Part 2

defmodule Helper2 do
  def step(elem_array, nil_indexes, to_move)

  def step(elem_array, [{_, _, _count = 0} | nil_indexes], to_move) do
    step(elem_array, nil_indexes, to_move)
  end

  def step(elem_array, nil_indexes, {file_n, start_i, move_count}) do
    case find_nil_index(nil_indexes, 0, move_count, start_i) do
      {{nil, nil_start_i, nil_size}, nil_list_i} ->
        new_nil_indexes =
          List.update_at(
            nil_indexes,
            nil_list_i,
            fn _ -> {nil, nil_start_i + move_count, nil_size - move_count} end
          )

        nil_end_i = nil_start_i + move_count - 1
        nil_range = nil_start_i..nil_end_i
        end_i = start_i + move_count - 1
        move_range = start_i..end_i

        new_elem_array =
          elem_array
          |> then(
            &amp;Enum.reduce(nil_range, &amp;1, fn ui, elem_array ->
              put_in(elem_array[ui], file_n)
            end)
          )
          |> then(
            &amp;Enum.reduce(move_range, &amp;1, fn ui, elem_array ->
              put_in(elem_array[ui], nil)
            end)
          )

        {new_elem_array, new_nil_indexes}

      nil ->
        {elem_array, nil_indexes}
    end
  end

  defp find_nil_index(nil_indexes, i, min_size, max_i) do
    [nil_index = {nil, start_i, size} | tail] = nil_indexes

    cond do
      start_i > max_i ->
        nil

      size >= min_size ->
        {nil_index, i}

      true ->
        find_nil_index(tail, i + 1, min_size, max_i)
    end
  end
end

elem_array = elems |> Enum.into(Arrays.new())
nil_indexes = Enum.take_every(Enum.drop(file_indexes, 1), 2)
file_indexes = Enum.take_every(file_indexes, 2) |> Enum.reverse()

file_indexes
|> Enum.reduce(
  {elem_array, nil_indexes},
  fn file_loc, {elem_array, nil_indexes} ->
    Helper2.step(elem_array, nil_indexes, file_loc)
  end
)
|> elem(0)
|> Enum.with_index(fn file_n, i -> {file_n, i} end)
|> Enum.filter(fn {char, _} -> char != nil end)
|> Enum.map(fn {char, i} -> char * i end)
|> Enum.sum()