Powered by AppSignal & Oban Pro

AoC 2022 Day 12

2022/day12.livemd

AoC 2022 Day 12

Mix.install([:kino, :libgraph])

defmodule Utils do
  def split(line, sep \\ "") do
    String.split(line, sep, trim: true)
  end

  def split_all_lines(text, sep \\ "") do
    text
    |> String.split("\n", trim: true)
    |> Enum.map(&split(&1, sep))
  end

  def to_numbers(number) when is_binary(number) do
    String.to_integer(number)
  end

  def to_numbers(numbers) when is_list(numbers) do
    Enum.map(numbers, &to_numbers/1)
  end

  def to_matrix(text, sep \\ "") do
    text
    |> split_all_lines(sep)
    |> then(fn data ->
      for {row, r} <- Enum.with_index(data), {col, c} <- Enum.with_index(row) do
        {{r, c}, col}
      end
    end)
    |> Map.new()
  end
end

Setup

import Utils
input = Kino.Input.textarea("Input:")
text = Kino.Input.read(input)
map =
  text
  |> to_matrix()
  |> Map.new(fn {k, <>} -> {k, v} end)

start_spot = map |> Enum.find(fn {_k, v} -> v == ?S end) |> elem(0)
end_spot = map |> Enum.find(fn {_k, v} -> v == ?E end) |> elem(0)

map =
  map
  |> Map.put(start_spot, ?a - 1)
  |> Map.put(end_spot, ?z + 1)
neighbours = fn {x, y} -> [{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}] end

paths =
  Enum.reduce(map, MapSet.new(), fn {pos, el}, set ->
    Enum.reduce(neighbours.(pos), set, fn npos, set ->
      nel = Map.get(map, npos, -1)

      if nel - el <= 1 do
        MapSet.put(set, {pos, npos})
      else
        set
      end
    end)
  end)

graph = Graph.add_edges(Graph.new(), Enum.to_list(paths))

P1

Graph.get_shortest_path(graph, start_spot, end_spot) |> length |> Kernel.-(1)

P2

Enum.filter(map, fn {_k, v} -> v == ?a end)
|> Enum.map(&amp;elem(&amp;1, 0))
|> Kernel.++([start_spot])
|> Enum.map(fn spot ->
  Graph.get_shortest_path(graph, spot, end_spot)
end)
|> Enum.reject(&amp;is_nil/1)
|> Enum.map(
  &amp;(&amp;1
    |> length
    |> Kernel.-(1))
)
|> Enum.min()