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

Advent of Code - Day 9

2024/09.livemd

Advent of Code - Day 9

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

Problem

https://adventofcode.com/2024/day/9

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2024", "9", System.fetch_env!("LB_AOC_SESSION"))
example_input = "2333133121414131402"
small_example = "12345"
defmodule Day9 do

  def parse(str) do
    str
    |> String.split("", trim: true)
    |> Enum.map(&String.to_integer(&1))
    |> Enum.chunk_every(2)
    |> Enum.map(fn # file_len, free_len, slots_occupied by free
      [file_len, free_len] -> {file_len, free_len, []}
      [file_len] -> {file_len, 0, []}
    end)
    |> Enum.with_index()
    |> Enum.reduce(Map.new(), fn {val, idx}, acc ->
      Map.put(acc, idx, val)
    end)
  end

  def defrag(diskmap) do
    file_count = length(Map.keys(diskmap))
    defrag(diskmap, 0, file_count - 1)
  end

  def defrag(diskmap, _, 0), do: diskmap
  def defrag(diskmap, a, b) when a == b, do: diskmap

  def defrag(diskmap, cur_free_p, cur_p) do
    {file_len, free_len, blocks } = Map.get(diskmap, cur_free_p)
    if free_len == 0 do
      defrag(diskmap, cur_free_p + 1, cur_p)
    else
      {src_file_len, src_free_len, src_blocks } = Map.get(diskmap, cur_p)
      if src_file_len == 0 do
        defrag(diskmap, cur_free_p, cur_p - 1)
      else
        diskmap
        |> Map.put(cur_free_p, { file_len, free_len - 1, [cur_p | blocks]})
        |> Map.put(cur_p, { src_file_len - 1, src_free_len + 1, src_blocks })
        |> defrag(cur_free_p, cur_p)
      end
    end
  end

  def calc_sum(start, scalar, terms) do
    sum = terms * (2 * start + terms - 1) / 2
    round(scalar * sum)
  end

  def calc_block_sum(start_id, blocks) do
    #IO.inspect("start: #{start_id}")

    blocks
    |> Enum.reverse()
    |> Enum.with_index()
    |> Enum.reduce(0, fn {x, idx}, acc ->
      acc + ((start_id + idx) * x)
    end)
  end

  def checksum_block({block_id, {file_len, _, blocks}}, cur_p) do
      ws = calc_sum(cur_p, block_id, file_len)
      #Apex.ap([ws,  calc_block_sum(cur_p + file_len, blocks)])

      { cur_p + file_len + length(blocks), ws + calc_block_sum(cur_p + file_len, blocks)}
  end

  def render(diskmap) do
    file_count = length(Map.keys(diskmap))
    Enum.reduce(0..file_count - 1, "", fn block_id, acc -> 
      {file_len, free_len, blocks} = Map.get(diskmap, block_id)
      a = List.duplicate(block_id, file_len) |> Enum.join("")
      free = List.duplicate(".", free_len) |> Enum.join("")
      b = Enum.join(Enum.reverse(blocks), "")

      acc <> a <> free <> b
      end)
  end

  
  def checksum(diskmap) do
    {_, sum } = Enum.reduce(diskmap, {0, 0}, fn block, {cur_p, acc} ->
      {end_id, sum } = checksum_block(block, cur_p)

      { end_id, acc + sum }
    end)

    sum
  end

  def solve_part1(str) do
    parse(str)
    |> defrag()
    |> render()
  end

end
Day9.solve_part1(puzzle_input) |> Apex.ap