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

Advent of Code 2024 Day 9 Part 2

2024_day9_part2.livemd

Advent of Code 2024 Day 9 Part 2

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

Get Inputs

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2024", "9", System.fetch_env!("LB_SESSION"))

My answer

sample_input = "2333133121414131402"
get_blocks = fn input ->
  input
  |> String.codepoints()
  |> Enum.with_index()
  |> Enum.map(fn {num, index} ->
    num = String.to_integer(num)

    id =
      case rem(index, 2) do
        0 ->
          div(index, 2)
        _ ->
          nil
      end

    {id, num}
  end)
end
blocks = get_blocks.(sample_input)
compact = fn blocks ->
  0..(blocks |> length() |> div(2) |> Kernel.-(1))
  |> Enum.reduce(blocks, fn index, acc_blocks ->
    taregt_index = length(blocks) - 1 - index * 2
    {target_id, target_num} = Enum.at(blocks, taregt_index)

    acc_blocks
    |> Enum.find_index(fn {id, num} ->
      is_nil(id) && num >= target_num
    end)
    |> case do
      nil ->
        acc_blocks

      target_blank_index ->
        {_, num} = Enum.at(acc_blocks, target_blank_index)
        taregt_index = Enum.find_index(acc_blocks, fn {id, _} -> id == target_id end)

        if taregt_index > target_blank_index do
          acc_blocks =
            acc_blocks
            |> List.update_at(target_blank_index, fn _ ->
              {target_id, target_num}
            end)
            |> List.update_at(taregt_index, fn _ ->
              {nil, target_num}
            end)

          if num == target_num do
            acc_blocks
          else
            List.insert_at(acc_blocks, target_blank_index + 1, {nil, num - target_num})
          end
          |> Enum.reduce({[], nil}, fn {id, num} = new, {acc_acc_blocks, pre_block} ->
            case {pre_block, id} do
              {{nil, pre_num}, nil} ->
                merged = {nil, num + pre_num}
                {[merged | tl(acc_acc_blocks)], merged}
              _ ->
                {[new | acc_acc_blocks], new}
            end
          end)
          |> elem(0)
          |> Enum.reverse()
        else
          acc_blocks
        end
    end
  end)
end
compacted_blocks = compact.(blocks)
get_checksum = fn blocks ->
  blocks
  |> Enum.flat_map(fn {id, num} ->
    List.duplicate(id, num)
  end)
  |> Enum.with_index()
  |> Enum.map(fn {num, index} ->
    case num do
      nil -> 0
      _ -> num * index
    end
  end)
  |> Enum.sum()
end
get_checksum.(compacted_blocks)
blocks = get_blocks.(puzzle_input)
compacted_blocks = compact.(blocks)
get_checksum.(compacted_blocks)