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

Supply Stacks

livebook/day05.livemd

Supply Stacks

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

Input

input = Kino.Input.textarea("Input:")
[stacks, instructions] = Kino.Input.read(input) |> String.trim_trailing() |> String.split("\n\n")
stacks = stacks |> String.split("\n")
{_, stacks} = List.pop_at(stacks, -1)
instructions = instructions |> String.split("\n")
defmodule Stacks do
  def new(stacks) do
    stacks = Enum.reverse(stacks)

    push_layers(stacks, %{})
  end

  def tops(stacks) do
    Map.to_list(stacks) |> Enum.sort() |> Enum.map(fn {_, [top | _]} -> top end)
    # Enum.reduce("", fn {_, [top | _]}, acc -> acc <> <> end)
  end

  defp push_layers([layer | more], acc) do
    IO.puts("#{layer}")
    acc = push_layer(layer, 1, acc)
    push_layers(more, acc)
  end

  defp push_layers([], acc), do: acc

  defp push_layer(<<"[", ch, "] ", rest::binary>>, index, acc) do
    acc = push(ch, index, acc)
    push_layer(rest, index + 1, acc)
  end

  defp push_layer(<<"[", ch, "]">>, index, acc) do
    push(ch, index, acc)
  end

  defp push_layer(<<"    ", rest::binary>>, index, acc) do
    push_layer(rest, index + 1, acc)
  end

  defp push_layer(<<"   ">>, index, acc) do
    acc
  end

  defp push(crate, index, acc) do
    Map.update(acc, index, [crate], fn stack -> [crate | stack] end)
  end
end
stacks = Stacks.new(stacks)
re = ~r/^move (\d+) from (\d+) to (\d+)/

instructions =
  Enum.map(instructions, fn instr ->
    [count, from, to] =
      Regex.run(re, instr, capture: :all_but_first)
      |> Enum.map(&amp;String.to_integer/1)

    %{count: count, from: from, to: to}
  end)
defmodule Crane9000 do
  def run(stacks, [%{count: count, from: from, to: to} | instructions]) do
    run(execute(stacks, count, from, to), instructions)
  end

  def run(stacks, []), do: stacks

  defp execute(stacks, 0, _, _), do: stacks

  defp execute(stacks, count, from, to) do
    {crate, stacks} = pop(stacks, from)
    stacks = push(stacks, to, crate)
    execute(stacks, count - 1, from, to)
  end

  defp pop(stacks, from) do
    Map.get_and_update(stacks, from, fn [crate | stack] -> {crate, stack} end)
  end

  defp push(stacks, to, crate) do
    Map.update(stacks, to, [crate], fn stack -> [crate | stack] end)
  end
end
part1 = Crane9000.run(stacks, instructions)
Stacks.tops(part1) |> List.to_string()
defmodule Crane9001 do
  def run(stacks, [%{count: count, from: from, to: to} | instructions]) do
    run(execute(stacks, count, from, to), instructions)
  end

  def run(stacks, []), do: stacks

  defp execute(stacks, count, from, to) do
    {crates, stacks} = pop(stacks, count, from)
    push(stacks, to, crates)
  end

  defp pop(stacks, count, from) do
    Map.get_and_update(stacks, from, fn stack -> Enum.split(stack, count) end)
  end

  defp push(stacks, to, crates) do
    Map.update(stacks, to, crates, fn stack -> crates ++ stack end)
  end
end
part2 = Crane9001.run(stacks, instructions)
Stacks.tops(part2) |> List.to_string()