Powered by AppSignal & Oban Pro

Day 12

2022/day12.livemd

Day 12

Mix.install([
  {:libgraph, "~> 0.16.0"}
])

Section

defmodule Day12 do
  def part1({graph, src, dst, _}) do
    Graph.a_star(graph, src, dst, &manhattan(&1, dst)) |> length() |> Kernel.-(1)
  end

  def part2({graph, _, dst, srcs}) do
    srcs
    |> Enum.map(fn x -> Graph.a_star(graph, x, dst, &manhattan(&1, dst)) end)
    |> Enum.reject(&is_nil/1)
    |> Enum.map(&length/1)
    |> Enum.min()
    |> Kernel.-(1)
  end

  defp manhattan({x1, y1}, {x2, y2}) do
    abs(x1 - x2) + abs(y1 - y2)
  end

  def parse_input(path) do
    grid =
      path
      |> File.stream!()
      |> Stream.map(&String.trim/1)
      |> Enum.map(&String.to_charlist/1)

    grid =
      for {row, i} <- Enum.with_index(grid),
          {col, j} <- Enum.with_index(row),
          into: %{},
          do: {{i, j}, col}

    graph =
      for {{i, j} = cf, from} <- grid,
          ct <- [{i - 1, j}, {i + 1, j}, {i, j - 1}, {i, j + 1}],
          to = grid[ct],
          grid[ct] != nil,
          reduce: Graph.new(type: :directed) do
        g ->
          case {from, to} do
            {?S, to} when to in 'ab' ->
              Graph.add_edge(g, cf, ct)

            {?S, _} ->
              g

            {from, ?E} when from in 'yz' ->
              Graph.add_edge(g, cf, ct)

            {_, ?E} ->
              g

            {from, to} when to - from <= 1 ->
              Graph.add_edge(g, cf, ct)

            _ ->
              g
          end
      end

    src = Enum.find(grid, fn {_, v} -> v == ?S end) |> elem(0)
    dst = Enum.find(grid, fn {_, v} -> v == ?E end) |> elem(0)
    srcs = Enum.filter(grid, fn {_, v} -> v in 'Sa' end) |> Enum.map(&elem(&1, 0))

    {graph, src, dst, srcs}
  end
end
input = "#{__DIR__}/day12.txt" |> Day12.parse_input()
Day12.part1(input)
Day12.part2(input)