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

Advent of Code: Day 05

2022/Day05.livemd

Advent of Code: Day 05

Helpers

defmodule Helpers do
  @spec read_file_contents(String.t()) :: {:ok, binary()}
  def read_file_contents(filename) do
    file_path = "/Users/charlie/github.com/charlieroth/advent-of-code/2022/#{filename}"

    case File.read(file_path) do
      {:ok, contents} -> contents
      {:error, _} -> raise("Failed to read file contents")
    end
  end
end
defmodule Stack do
  defstruct items: []
  def new, do: %Stack{}
  def size(%Stack{items: items}), do: length(items)
  def push(%Stack{items: items}, item), do: %Stack{items: [item | items]}
  def pop(%Stack{items: [item | rest]}), do: {item, %Stack{items: rest}}
  def pop(stack = %Stack{items: []}), do: {nil, stack}
end

Part 01

The Elves just need to know which crate will end up on top of each stack

In this example, the top crates are C in stack 1, M in stack 2, and Z in stack 3, so you should combine these together and give the Elves the message CMZ.

input = ~S"""
    [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
"""

[stacks, moves] = input |> String.split("\n\n", trim: true)

# https://elixirforum.com/t/advent-of-code-2022-day-5/52258/2

stacks =
  stacks
  |> String.split("\n", trim: true)
  |> Enum.drop(-1)
  # list({char(), integer()})
  |> Enum.flat_map(fn line ->
    line
    # list(char())
    |> String.graphemes()
    # remove first empty string
    |> Enum.drop(1)
    # take the 0th and every 4th element
    |> Enum.take_every(4)
    # tuple {character, index}
    |> Enum.with_index(1)
  end)
  # group by index into map(String.t(), { String.t(), integer() })
  |> Enum.group_by(&elem(&1, 1))
  # transform map(String.t(), tuple(String.t(), integer())) into list(tuple(integer(), list(String.t())))
  |> Enum.map(fn {column, vals} ->
    # take the character from tuple(String.t(), integer())
    # remove empty ("") characters
    val =
      vals
      |> Enum.map(&elem(&1, 0))
      |> Enum.filter(&(&1 !== " "))

    {column, val}
  end)
  # transform list(tuple(integer(), list(String.t()))) to map(integer(), list(String.t()))
  |> Map.new()

moves =
  moves
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    line
    |> String.split(["move ", " from ", " to "], trim: true)
    |> Enum.map(&String.to_integer(&1))
  end)

defmodule PartOne do
  def move([n, from, to], s) do
    1..n
    |> Enum.reduce(s, fn _, new_stacks ->
      case Map.get(new_stacks, from) do
        nil ->
          new_stacks

        [] ->
          new_stacks

        # pop crate from bottom of "from" stack
        [crate | from_stack] ->
          to_stack = Map.get(new_stacks, to)

          new_stacks
          |> Map.put(from, from_stack)
          |> Map.put(to, [crate | to_stack])
      end
    end)
  end
end

final_stack =
  [[1, 2, 1], [3, 1, 3], [2, 2, 1], [1, 1, 2]]
  |> Enum.map(fn instruction ->
    PartOne.move(instruction, stacks)
  end)
  |> List.last()
m = %{1 => ["N", "Z"]}
[a | b] = Map.get(m, 1)
a

Part 02