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

Day 21

2024/day21.livemd

Day 21

Solution

defmodule Numeric do
  defp keypad do
    %{
      ?7 => {0, 0},
      ?8 => {0, 1},
      ?9 => {0, 2},
      ?4 => {1, 0},
      ?5 => {1, 1},
      ?6 => {1, 2},
      ?1 => {2, 0},
      ?2 => {2, 1},
      ?3 => {2, 2},
      ?0 => {3, 1},
      ?A => {3, 2}
    }
  end

  defp moves({from, to}) do
    {a, b} = keypad()[from]
    {c, d} = keypad()[to]
    {u, r} = {a - c, d - b}

    vert = String.duplicate(if(u >= 0, do: "^", else: "v"), abs(u))
    horz = String.duplicate(if(r >= 0, do: ">", else: "<"), abs(r))

    dirs =
      cond do
        a == 3 and d == 0 -> [vert <> horz]
        b == 0 and c == 3 -> [horz <> vert]
        true -> [vert <> horz, horz <> vert] |> Enum.uniq()
      end

    Enum.map(dirs, &amp;String.to_charlist(&amp;1 <> "A"))
  end

  def inputs(code) do
    [?A | code] |> Enum.zip(code) |> Enum.map(&amp;moves/1)
  end
end
defmodule Cache do
  use Agent

  def start do
    Agent.start_link(fn -> %{} end, name: __MODULE__)
    Agent.update(__MODULE__, fn _ -> %{} end)
  end

  def get(k) do
    Agent.get(__MODULE__, &amp;Map.get(&amp;1, k))
  end

  def put(k, v) do
    Agent.update(__MODULE__, &amp;Map.put(&amp;1, k, v))
  end
end

defmodule Directional do
  defp keypad do
    %{
      ?^ => {0, 1},
      ?A => {0, 2},
      ?< => {1, 0},
      ?v => {1, 1},
      ?> => {1, 2}
    }
  end

  defp moves({from, to}) do
    {a, b} = keypad()[from]
    {c, d} = keypad()[to]
    {u, r} = {a - c, d - b}

    vert = String.duplicate(if(u >= 0, do: "^", else: "v"), abs(u))
    horz = String.duplicate(if(r >= 0, do: ">", else: "<"), abs(r))

    dirs =
      cond do
        a == 0 and d == 0 -> [vert <> horz]
        b == 0 and c == 0 -> [horz <> vert]
        true -> [vert <> horz, horz <> vert] |> Enum.uniq()
      end

    Enum.map(dirs, &amp;String.to_charlist(&amp;1 <> "A"))
  end

  defp f(code, 0), do: length(code)

  defp f(code, n) do
    cache = Cache.get({code, n})

    if is_nil(cache) do
      result = [?A | code] |> Enum.zip(code) |> Enum.map(&amp;moves/1) |> score(n - 1)
      Cache.put({code, n}, result)
      result
    else
      cache
    end
  end

  def score(moves, n) do
    moves
    |> Enum.map(fn codes -> codes |> Enum.map(&amp;f(&amp;1, n)) |> Enum.min() end)
    |> Enum.sum()
  end
end
complexity = fn code, n ->
  len = code |> String.to_charlist() |> Numeric.inputs() |> Directional.score(n)
  num = code |> String.replace("A", "") |> String.to_integer()
  len * num
end

{:ok, contents} = File.read("#{__DIR__}/inputs/day21.txt")
Cache.start()

contents
|> String.split("\n", trim: true)
|> Enum.map(&amp;complexity.(&amp;1, 25))
|> Enum.sum()