Powered by AppSignal & Oban Pro

Day 5

2022/elixir/day5.livemd

Day 5

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

Section

input = Kino.Input.textarea("cargo")
defmodule Instruction do
  defstruct [:source, :destination, :count, :raw_instruction]

  def new(instruction_text) do
    [count, source, destination] =
      Regex.scan(~r/.*move (\d+) from (\d+) to (\d)+.*/, instruction_text, capture: :all_but_first)
      |> List.flatten()
      |> Enum.map(&String.to_integer/1)

    struct!(__MODULE__,
      source: source,
      destination: destination,
      count: count,
      raw_instruction: instruction_text
    )
  end
end
[stacks | cargo] =
  input
  |> Kino.Input.read()
  |> String.split("\n\n")
  |> hd()
  |> String.split("\n")
  |> Enum.map(fn row ->
    row
    |> String.split(~r//, trim: true)
    |> Enum.chunk_every(4)
    |> Enum.map(&(Enum.join(&1) |> String.trim()))
  end)
  |> Enum.reverse()

stacks =
  stacks |> Enum.map(&String.to_integer/1) |> Enum.zip(Stream.cycle([[]])) |> Enum.into(%{})

cargo =
  cargo
  |> Enum.map(fn crate ->
    Enum.with_index(crate, fn element, index -> {index + 1, element} end) |> Enum.into(%{})
  end)

stacked_crates =
  cargo
  |> Enum.reduce(stacks, fn crate, stacks ->
    stacks
    |> Map.merge(crate, fn
      _k, v1, "" ->
        v1

      _k, v1, v2 ->
        v1 ++ [v2]
    end)
  end)

# Just getting it in the right shape!
# %{1 => ["[Z]", "[N]"], 2 => ["[M]", "[C]", "[D]"], 3 => ["[P]"]}
stacked_crates |> Map.get(2) |> Enum.split(-1)

instructions =
  input
  |> Kino.Input.read()
  |> String.split("\n\n")
  |> List.last()
  |> String.split("\n")
  |> Enum.map(&Instruction.new/1)

instructions
|> Enum.reduce(stacked_crates, fn %{
                                    source: source,
                                    destination: destination,
                                    count: count
                                  },
                                  crates ->
  {remaining, moving} = crates |> Map.get(source) |> Enum.split(count * -1)

  moving = moving |> Enum.reverse()

  crates
  |> Map.put(source, remaining)
  |> Map.update(destination, [], &(&1 ++ moving))
end)
|> Map.values()
|> Enum.map(&List.last/1)
|> Enum.map(fn <<"[", char::size(8), "]">> -> char end)
instructions
|> Enum.reduce(stacked_crates, fn %{
                                    source: source,
                                    destination: destination,
                                    count: count
                                  },
                                  crates ->
  {remaining, moving} = crates |> Map.get(source) |> Enum.split(count * -1)

  crates
  |> Map.put(source, remaining)
  |> Map.update(destination, [], &amp;(&amp;1 ++ moving))
end)
|> Map.values()
|> Enum.map(&amp;List.last/1)
|> Enum.map(fn <<"[", char::size(8), "]">> -> char end)