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

Day09

09.livemd

Day09

Section

example = "2333133121414131402"

defmodule Day09 do
  def parse_input(input) do
    input
    |> String.split("", trim: true)
    |> Enum.map(&String.to_integer/1)
    |> Enum.chunk_every(2)
    |> Enum.with_index(fn
      [f], i -> List.duplicate(to_string(i), f)
      [f, s], i -> [List.duplicate(to_string(i), f), List.duplicate(".", s)]
    end)
    |> List.flatten()
  end

  def part1(raw_input) do
    input = parse_input(raw_input)
    length = Enum.count(input, &(&1 != "."))

    rearrange(input, Enum.reverse(input), [], length)
    |> Enum.with_index(fn e, i -> String.to_integer(e) * i end)
    |> Enum.sum()
  end

  def rearrange(_, _, acc, l) when length(acc) == l, do: Enum.reverse(acc)
  def rearrange(i, ["." | rt], acc, l), do: rearrange(i, rt, acc, l)
  def rearrange(["." | it], [rh | rt], acc, l), do: rearrange(it, rt, [rh | acc], l)
  def rearrange([ih | it], r, acc, l), do: rearrange(it, r, [ih | acc], l)

  def parse_input2(input) do
    input
    |> String.split("", trim: true)
    |> Enum.map(&String.to_integer/1)
    |> Enum.chunk_every(2)
    |> Enum.with_index(fn
      [f], i -> {f, i}
      [f, s], i -> [{f, i}, {s, "."}]
    end)
    |> Enum.reduce([], fn
      [a, b], acc -> [b | [a | acc]]
      last, acc -> [last | acc]
    end)
    |> Enum.reverse()
  end

  def part2(raw_input) do
    input = parse_input2(raw_input)

    merge(input)
    |> Enum.map(fn {size, i} -> List.duplicate(i, size) end)
    |> List.flatten()
    |> Enum.with_index(fn
      ".", _i -> 0
      e, i -> e * i
    end)
    |> Enum.sum()
  end

  def merge(input) do
    input
    |> Enum.reverse()
    |> Enum.reject(&(elem(&1, 1) == "."))
    |> Enum.reduce(input, fn {size, data}, list ->
      list
      |> move_item({size, data})
      |> Enum.reverse()
      |> merge_gaps()
    end)
  end

  def move_item(list, {size, data}) do
    Enum.reduce(list, {[], :find}, fn
      {slot_size, "."}, {items, :find} ->
        cond do
          slot_size > size ->
            {[{slot_size - size, "."} | [{size, data} | items]], :hit}

          slot_size == size ->
            {[{size, data} | items], :hit}

          true ->
            {[{slot_size, "."} | items], :find}
        end

      {^size, ^data}, {items, :hit} ->
        {[{size, "."} | items], :hit}

      {^size, ^data}, {items, :find} ->
        {[{size, data} | items], :hit}

      item, {items, status} ->
        {[item | items], status}
    end)
    |> elem(0)
  end

  def merge_gaps(list) do
    list
    |> merge_gaps([])
    |> Enum.reverse()
  end

  def merge_gaps([], acc), do: acc
  def merge_gaps([{h1s, "."}, {h2s, "."}], acc), do: [{h1s + h2s, "."} | acc]
  def merge_gaps([h1, h2], acc), do: [h2 | [h1 | acc]]

  def merge_gaps([{h1s, "."} | [{h2s, "."} | rest]], acc),
    do: merge_gaps(rest, [{h1s + h2s, "."} | acc])

  def merge_gaps([h | rest], acc), do: merge_gaps(rest, [h | acc])
end



Day09.part1(example)
Day09.part2(example)