Powered by AppSignal & Oban Pro

Day 15

2021/day15.livemd

Day 15

input =
  File.read!("day15.txt")
  |> String.trim()
  |> String.split("\n")
  |> Enum.map(&String.to_charlist/1)
  |> Enum.with_index()
  |> Enum.flat_map(fn {row, y} ->
    row
    |> Enum.with_index()
    |> Enum.map(fn {v, x} -> {{x, y}, v - ?0} end)
  end)
  |> Map.new()

{width, height} = Enum.max(Map.keys(input))
{99, 99}

Task 1

shortest_paths =
  for y <- height..0//-1,
      x <- width..0//-1,
      reduce: %{} do
    acc ->
      right = acc[{x + 1, y}]
      bottom = acc[{x, y + 1}]

      value =
        case {right, bottom} do
          {nil, nil} -> input[{x, y}]
          _ -> input[{x, y}] + min(right, bottom)
        end

      Map.put(acc, {x, y}, value)
  end

shortest_paths[{0, 0}] - input[{0, 0}]
429

Task 2

defmodule Day15.Task2 do
  def expand_grid(board) do
    {width, height} = Enum.max(Map.keys(board))

    board
    |> Enum.flat_map(fn {{x, y}, v} ->
      for rx <- 0..4, ry <- 0..4 do
        {{x + (width + 1) * rx, y + (height + 1) * ry}, rem(v - 1 + rx + ry, 9) + 1}
      end
    end)
    |> Map.new()
  end

  def find_path(board, start, finish) do
    dists = :gb_sets.singleton({0, start})

    find_path(board, finish, dists, MapSet.new())
  end

  @surround for dx <- -1..1, dy <- -1..1, abs(dx) != abs(dy), do: {dx, dy}

  def find_path(board, finish, dists, visited) do
    {{dist, {x, y} = curr}, dists} = :gb_sets.take_smallest(dists)

    if curr == finish do
      dist
    else
      visited = MapSet.put(visited, curr)

      dists =
        for {dx, dy} <- @surround,
            next = {x + dx, y + dy},
            next not in visited,
            is_map_key(board, next),
            alt = dist + board[next],
            reduce: dists do
          acc ->
            :gb_sets.add_element({alt, next}, acc)
        end

      find_path(board, finish, dists, visited)
    end
  end
end
{:module, Day15.Task2, <<70, 79, 82, 49, 0, 0, 16, ...>>, {:find_path, 4}}
input
|> Day15.Task2.expand_grid()
|> Day15.Task2.find_path({0, 0}, {499, 499})
2844