Powered by AppSignal & Oban Pro

AoC 2022 Day 5

2022/day05.livemd

AoC 2022 Day 5

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

Input

input_field = Kino.Input.textarea("Puzzle input")
input = Kino.Input.read(input_field)

Part 1

defmodule Stacks do
  @move_regex ~r/move (\d+) from (\d+) to (\d+)/
  @stack_regex ~r/(\s{3}|\[(\w)])\s?/

  def parse_row(row) do
    Regex.scan(@stack_regex, row, capture: :all_but_first)
    |> Enum.map(&parse_item/1)
  end

  def parse_item(["    "]), do: :empty
  def parse_item(["   "]), do: :empty
  def parse_item([_, letter]), do: String.to_atom(letter)

  def parse_move(move) do
    [amount, from, to] =
      Regex.run(@move_regex, move, capture: :all_but_first)
      |> Enum.map(&String.to_integer/1)

    {amount, from, to}
  end

  def move_one(stacks, from, to) do
    [item | from_stack] = stacks[from]
    to_stack = [item | stacks[to]]

    stacks
    |> Map.replace!(from, from_stack)
    |> Map.replace!(to, to_stack)
  end

  def apply_move(stacks, {0, _, _}), do: stacks

  def apply_move(stacks, {amount, from, to}) do
    stacks
    |> move_one(from, to)
    |> apply_move({amount - 1, from, to})
  end
end
[stacks_str, moves_str] = String.split(input, "\n\n")
# Parse the initial stacks

stacks =
  stacks_str
  |> String.split("\n")
  |> List.delete_at(-1)
  |> Enum.map(&Stacks.parse_row/1)
  # And for this trick, I will...
  |> Enum.zip()
  |> Enum.map(fn tup ->
    tup
    |> Tuple.to_list()
    |> Enum.reject(&match?(:empty, &1))
  end)
  |> Enum.with_index(fn element, index -> {index + 1, element} end)
  |> Enum.into(%{})
# Parse the moves

moves =
  moves_str
  |> String.split("\n")
  |> Enum.map(&Stacks.parse_move/1)
# Apply the moves to the stacks

moves
|> Enum.reduce(stacks, &Stacks.apply_move(&2, &1))
|> Map.values()
|> Enum.map(&List.first/1)
|> Enum.join()

Part 2

defmodule Stacks2 do
  def apply_move(stacks, {amount, from, to}) do
    {items, from_stack} = Enum.split(stacks[from], amount)
    to_stack = items ++ stacks[to]

    stacks
    |> Map.replace!(from, from_stack)
    |> Map.replace!(to, to_stack)
  end
end
moves
|> Enum.reduce(stacks, &Stacks2.apply_move(&2, &1))
|> Map.values()
|> Enum.map(&List.first/1)
|> Enum.join()