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

Advent of Code 2023 - Day 17

2023/17.livemd

Advent of Code 2023 - Day 17

Mix.install([
  {:kino, "~> 0.11.0"},
  {:kino_aoc, github: "ljgago/kino_aoc"},
  {:libgraph, "~> 0.16.0"}
])

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "17", System.fetch_env!("LB_AOC_SESSION"))
input =
  for {row, r} <- puzzle_input |> String.split("\n", trim: true) |> Enum.with_index(),
      {col, c} <- row |> String.graphemes() |> Enum.with_index(),
      into: %{},
      do: {{r, c}, String.to_integer(col)}
defmodule ClumsyCrucible do
  def build_graph(grid, range) do
    {max_r, max_c} = size(grid)

    grid
    |> Task.async_stream(
      fn {{r, c}, _loss} ->
        for blocks <- range do
          []
          |> maybe_add_edge({r, c, :v}, {r - blocks, c, :h}, grid)
          |> maybe_add_edge({r, c, :v}, {r + blocks, c, :h}, grid)
          |> maybe_add_edge({r, c, :h}, {r, c - blocks, :v}, grid)
          |> maybe_add_edge({r, c, :h}, {r, c + blocks, :v}, grid)
        end
      end,
      ordered: false
    )
    |> Stream.flat_map(&amp;elem(&amp;1, 1))
    |> Enum.reduce(Graph.new(), &amp;Graph.add_edges(&amp;2, &amp;1))
    |> Graph.add_edges([
      {:start, {0, 0, :v}, weight: 0},
      {:start, {0, 0, :h}, weight: 0},
      {{max_r, max_c, :v}, :destination, weight: 0},
      {{max_r, max_c, :h}, :destination, weight: 0}
    ])
  end

  def shortest_path(graph) do
    graph
    |> Graph.get_shortest_path(:start, :destination)
    |> Enum.slice(1..-2)
  end

  def path_heat_loss(path, grid) do
    len = length(path)

    pairs =
      Enum.zip(
        Enum.slice(path, 0..(len - 1)),
        Enum.slice(path, 1..len)
      )

    for {a, b} <- pairs, reduce: 0 do
      acc -> acc + heat_loss(grid, a, b)
    end
  end

  defp size(grid) do
    grid
    |> Map.keys()
    |> Enum.max()
  end

  defp maybe_add_edge(list, from, {tr, tc, _} = to, grid) do
    if Map.has_key?(grid, {tr, tc}) do
      [{from, to, weight: heat_loss(grid, from, to)} | list]
    else
      list
    end
  end

  defp heat_loss(grid, {fr, fc, _}, {tr, tc, _}) do
    for r <- fr..tr, c <- fc..tc, reduce: -grid[{fr, fc}] do
      acc -> acc + grid[{r, c}]
    end
  end
end
import ClumsyCrucible

Part 1

build_graph(input, 1..3)
|> shortest_path()
|> path_heat_loss(input)

Part 2

build_graph(input, 4..10)
|> shortest_path()
|> path_heat_loss(input)

Run in Livebook