Powered by AppSignal & Oban Pro

Day 05

2022/day-05.livemd

Day 05

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

example_input =
  Kino.Input.textarea("example input:")
  |> Kino.render()

real_input = Kino.Input.textarea("real input:")

Common

parse = fn input ->
  [stack_data, procedure_data] =
    input
    |> Kino.Input.read()
    |> String.split("\n\n", trim: true)
    |> Enum.map(&String.split(&1, "\n", trim: true))

  stacks =
    stack_data
    |> Enum.drop(-1)
    |> Enum.map(fn line ->
      line
      |> String.split("", trim: true)
      |> tl()
      |> Enum.take_every(4)
    end)
    |> Enum.zip_with(& &1)
    |> Enum.map(fn stack ->
      stack
      |> Enum.reverse()
      |> Enum.reduce([], fn
        " ", acc -> acc
        value, acc -> [value | acc]
      end)
    end)

  procedures =
    procedure_data
    |> Enum.map(fn line ->
      [_, count, _, from, _, to] = String.split(line)
      [count, from, to] = [count, from, to] |> Enum.map(&String.to_integer/1)
      %{count: count, from: from - 1, to: to - 1}
    end)

  {stacks, procedures}
end

read_heads = fn stacks ->
  stacks
  |> Enum.map(&hd/1)
  |> Enum.join()
end

Part 1

follow_procedures = fn {stacks, procedures} ->
  procedures
  |> Enum.reduce(stacks, fn procedure, stacks ->
    1..procedure.count
    |> Enum.reduce(stacks, fn _, stacks ->
      {item, stacks} = get_and_update_in(stacks, [Access.at(procedure.from)], &Enum.split(&1, 1))

      update_in(stacks, [Access.at(procedure.to)], &[item | &1])
    end)
  end)
end

real_input
|> then(parse)
|> then(follow_procedures)
|> then(read_heads)

Part 2

follow_procedures = fn {stacks, procedures} ->
  procedures
  |> Enum.reduce(stacks, fn procedure, stacks ->
    {items, stacks} =
      get_and_update_in(stacks, [Access.at(procedure.from)], &Enum.split(&1, procedure.count))

    update_in(stacks, [Access.at(procedure.to)], &Enum.concat(items, &1))
  end)
end

real_input
|> then(parse)
|> then(follow_procedures)
|> then(read_heads)