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

Advent of code day 09

2024/livebooks/day-09.livemd

Advent of code day 09

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

Setup input

example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")

Part 01

{list, _method, index} =
  example
  |> Kino.Input.read()
  |> String.split("", trim: true)
  |> Enum.map(&String.to_integer/1)
  |> Enum.reduce({[], true, 0}, fn e, {list, method, index} ->
    cond do
      e == 0 ->
        {list, !method, index}

      method == true ->
        elements = Enum.map(0..(e - 1), fn _x -> index end)
        {elements ++ list, !method, index + 1}

      true ->
        elements = Enum.map(0..(e - 1), fn _x -> "." end)
        {elements ++ list, !method, index}
    end
  end)

{list_2, index} = {Enum.reverse(list), index}

map_of_memory = Enum.frequencies(list)
list = List.to_tuple(list_2)

j = tuple_size(list) - 1
i = 0

result =
  0
  |> Stream.iterate(&(&1 + 1))
  |> Enum.reduce_while({list, i, j}, fn _, {list, i, j} ->
    if i == j do
      {:halt, list}
    else
      cond do
        elem(list, i) == "." && elem(list, j) == "." ->
          {:cont, {list, i, j - 1}}

        elem(list, i) == "." && elem(list, j) != "." ->
          list = put_elem(list, i, elem(list, j)) |> put_elem(j, ".")

          {:cont, {list, i + 1, j - 1}}

        elem(list, i) != "." && elem(list, j) != "." ->
          {:cont, {list, i + 1, j}}

        elem(list, i) != "." && elem(list, j) == "." ->
          {:cont, {list, i + 1, j - 1}}
      end
    end
  end)

Enum.reduce(i..j, {0, result}, fn p, {total, list} ->
  if elem(list, p) == "." do
    {total, list}
  else
    {total + elem(list, p) * p, list}
  end
  end) |> then(fn {t, _ } -> t end)

Part 02

list =
  example
  |> Kino.Input.read()
  |> String.split("", trim: true)
  |> Enum.map(&String.to_integer/1)
  |> List.to_tuple()

fid = 0
pos = 0

files = %{}
blanks = []

{pos, fid, %{files: files, blanks: blanks}} =
  Enum.reduce(
    0..(tuple_size(list) - 1),
    {pos, fid, %{files: files, blanks: blanks}},
    fn i,
       {pos, fid,
        %{
          files: files,
          blanks: blanks
        } = map} = _acc ->
      length = elem(list, i)

      if rem(i, 2) == 0 do
        files = Map.put(files, fid, {pos, length})
        fid = fid + 1

        {pos + length, fid, %{map | files: files}}
      else
        blanks = if length != 0, do: [{pos, length} | blanks] |> Enum.reverse(), else: blanks
        {pos + length, fid, %{map | blanks: blanks}}
      end
    end
  )

{_, response, _} =
  0
  |> Stream.iterate(&(&1 + 1))
  |> Enum.reduce_while(
    {fid, files, Enum.sort_by(blanks, fn {p, _} -> p end)},
    fn _, {fid, files, blanks} ->
      if fid == 0 do
        {:halt, {fid, files, blanks}}
      else
        fid = fid - 1

        {pos, size} = Map.get(files, fid)

        p_blanks =
          Enum.filter(Enum.with_index(blanks), fn {{b_pos, b_size}, _index} ->
            b_pos <= pos and b_size >= size
          end)

        case p_blanks do
          [{{start, length}, b_index} | _] ->
            blanks =
              if size == length do
                blanks |> List.delete_at(b_index)
              else
                blanks |> List.replace_at(b_index, {start + size, length - size})
              end

            files = Map.put(files, fid, {start, size})

            {:cont, {fid, files, blanks}}

          [] ->
            {:cont, {fid, files, blanks}}
        end
      end
    end
  )

response
|> Enum.reduce(0, fn {fid, {pos, size}}, total ->
    total +
      Enum.reduce(pos..(pos + size - 1), 0, fn x, acc ->
         acc + fid * x
      end)
end)