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

Advent of Code 2022 - Day 05

livebooks/day_05.livemd

Advent of Code 2022 - Day 05

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

Setup

example_input = """
    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 

move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
"""

input = Kino.Input.textarea("Puzzle Input", default: example_input)

Parsing

{raw_crate_stacks, raw_instructions} =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.split_while(&(not String.starts_with?(&1, "move")))
{raw_crate_stacks, [raw_stack_numbers]} = Enum.split(raw_crate_stacks, -1)

stack_count =
  ~r/(\d+)+/
  |> Regex.scan(raw_stack_numbers)
  |> Enum.count()

initial_crate_stacks = List.duplicate([], stack_count)

stack_row_regex =
  1..stack_count
  |> Enum.map(fn n -> "(   |\\[(?\\w)\\])" end)
  |> Enum.join(" ")
  |> Regex.compile!()

crate_stacks =
  raw_crate_stacks
  |> Enum.map(&Regex.run(stack_row_regex, &1, capture: :all_names))
  |> Enum.reduce(initial_crate_stacks, fn row, crate_stacks ->
    row
    |> Enum.zip_with(crate_stacks, fn
      "", crate_stack -> crate_stack
      item, crate_stack -> [item | crate_stack]
    end)
  end)
  |> Enum.map(&Enum.reverse/1)
  |> Enum.with_index()
  |> Map.new(fn {stack, index} -> {index + 1, stack} end)
instructions =
  raw_instructions
  |> Enum.map(fn raw_instruction ->
    [_, amount, from, to] = Regex.run(~r/move (\d+) from (\d) to (\d)/, raw_instruction)

    %{
      amount: String.to_integer(amount),
      from: String.to_integer(from),
      to: String.to_integer(to)
    }
  end)

Part 1

finished_crate_stacks =
  Enum.reduce(instructions, crate_stacks, fn instruction, crate_stacks ->
    Enum.reduce(1..instruction.amount, crate_stacks, fn _, crate_stacks ->
      {elem, crate_stacks} =
        Map.get_and_update!(crate_stacks, instruction.from, fn [head | tail] ->
          {head, tail}
        end)

      Map.update!(crate_stacks, instruction.to, &[elem | &1])
    end)
  end)

finished_crate_stacks
|> Enum.map(fn {_, [head | _]} -> head end)
|> Enum.join()

Part 2

finished_crate_stacks =
  Enum.reduce(instructions, crate_stacks, fn instruction, crate_stacks ->
    {elems, crate_stacks} =
      Map.get_and_update!(crate_stacks, instruction.from, &Enum.split(&1, instruction.amount))

    Map.update!(crate_stacks, instruction.to, &(elems ++ &1))
  end)

finished_crate_stacks
|> Enum.map(fn {_, [head | _]} -> head end)
|> Enum.join()