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

Day 9

livebook/9.livemd

Day 9

Part 1

# input = "2333133121414131402"
input = File.read!("9/input.txt")
defmodule U do
  def parse(input) do
    parse(String.codepoints(input), [], 0, :file)
  end

  def parse([], res, _, _) do
    res |> Enum.reverse()
  end

  def parse([n | tail], res, id, :file) do
    parse(tail, [{:file, id, String.to_integer(n)} | res], id + 1, :free)
  end

  def parse(["0" | tail], res, id, :free) do
    parse(tail, res, id, :file)
  end

    def parse(["\n" | tail], res, id, type) do
    parse(tail, res, id, type)
  end

  def parse([n | tail], res, id, :free) do
    parse(tail, [{:free, String.to_integer(n)} | res], id, :file)
  end

  def stringify([]), do: ""
  def stringify([{:free, n} | tail]) do
    String.duplicate(".", n) <> stringify(tail)
  end

  def stringify([{:file, id, n} | tail]) do
    String.duplicate(Integer.to_string(id), n) <> stringify(tail)
  end
end
layout = U.parse(input)
defmodule P1 do
  def rightmost(layout) do
    # IO.inspect({:rightmost, layout})
    rightmost(Enum.reverse(layout), [])
  end

  def rightmost([{:file, _, _} = file | tail], acc) do
    rearrange(file, Enum.reverse(tail) ++ acc)
  end

  def rightmost([block | tail], acc) do
    rightmost(tail, [block | acc])
  end

  def rearrange(file, layout) do
    rearrange(file, layout, [])
  end

  def rearrange(file, [], acc), do: [file | acc] |> Enum.reverse()

  def rearrange({:file, id, size}, [{:free, free} | tail], acc) do
    # IO.inspect({:rearrange,{:file, id, size}, [{:free, free} | tail], acc})
    cond do
      free == size ->
        rightmost(Enum.reverse([{:file, id, size} | acc]) ++ tail)

      free < size ->
        rearrange({:file, id, size - free}, tail, [{:file, id, free} | acc])

      free > size ->
        rightmost(Enum.reverse([{:free, free - size}, {:file, id, size} | acc]) ++ tail)
    end
  end

  def rearrange(file, [x | tail], acc) do
    rearrange(file, tail, [x | acc])
  end

  def checksum(layout) do
    checksum(0, layout, 0)
  end

  def checksum(_, [], acc) do
    acc
  end

  def checksum(block_id, [{:file, _, 0} | tail], acc) do
    checksum(block_id, tail, acc)
  end

  def checksum(block_id, [{:file, id, n} | tail], acc) do
    checksum(block_id + 1, [{:file, id, n - 1} | tail], acc + block_id * id)
  end

  def checksum(block_id, [{:free, size} | tail], acc) do
    checksum(block_id + size, tail, acc)
  end
end
rearranged = P1.rightmost(layout)
rearranged |> P1.checksum()

Part 2

defmodule P2 do
  def rearrange(layout) do
    rearrange(Enum.reverse(layout), [])
  end

  def rearrange([], acc) do
    acc
  end

  def rearrange([{:free, _} = x | tail], acc) do
    rearrange(tail, [x | acc])
  end

  def rearrange([{:file, _, size} = x | tail], acc) do
    inserted = insert(x, Enum.reverse(tail), [])
    if inserted do
      rearrange(Enum.reverse(inserted), [{:free, size} | acc])
    else
      rearrange(tail, [x | acc])
    end
  end

  def insert(_, [], _), do: nil

  def insert({:file, id, size}, [{:free, free} | tail], acc) when free >= size do
    Enum.reverse([{:free, free - size}, {:file, id, size} | acc]) ++ tail
  end

  def insert(f, [x | tail], acc) do
    insert(f, tail, [x | acc])
  end
end
rearranged2 = P2.rearrange(layout)
P1.checksum(rearranged2)