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

Day 10

day10.livemd

Day 10

Mix.install([
  {:kino, "~> 0.14.2"},
  {:kino_aoc, "~> 0.1.7"}
])

Part 1

{:ok, input} = 
  KinoAOC.download_puzzle("2024", "10", System.fetch_env!("LB_AOC_SESSION"))
  
input = input
  |> String.trim() |> String.split("\n")
  |> Enum.map(fn l -> String.graphemes(l) |> Enum.map(&String.to_integer/1)end)

grid = for {row, i} <- Enum.with_index(input) do
  for {col, j} <- Enum.with_index(row) do
    {{i, j}, col}
  end
end |> List.flatten() |> Enum.into(%{})
defmodule Part1 do
  def get_neighbors(grid, {x, y}) do
    [{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}]
    |> Enum.reduce([], fn loc, acc -> 
      val = Map.get(grid, loc)
      if val != nil, do: [{loc, val} | acc], else: acc
    end)
  end

  def walk(grid, loc, elev, found \\ MapSet.new())
  def walk(_, loc, 9, found), do: MapSet.put(found, loc)
  def walk(grid, loc, elev, found) do
    get_neighbors(grid, loc) |> Enum.reduce(found, fn {nloc, nelev}, acc ->
      found_ = if nelev == elev + 1, do: walk(grid, nloc, nelev, acc), else: MapSet.new()
      MapSet.union(acc, found_)
    end)
  end
end

result = Enum.into(grid, []) |> Enum.filter(fn {_, v} -> v == 0 end)
  |> Enum.map(fn {loc, elev} ->
    Part1.walk(grid, loc, elev) |> Enum.into([]) |> Enum.count()
  end)
  |> Enum.sum()

Part 2

defmodule Part2 do
  import Part1, only: [get_neighbors: 2]

  def walk(_, _, 9), do: 1
  def walk(grid, loc, elev) do
    get_neighbors(grid, loc) |> Enum.reduce(0, fn {nloc, nelev}, acc ->
      acc + if nelev == elev + 1, do: walk(grid, nloc, nelev), else: 0
    end)
  end
end

result = Enum.into(grid, []) |> Enum.filter(fn {_, v} -> v == 0 end)
  |> Enum.map(fn {loc, elev} ->
    Part2.walk(grid, loc, elev)
  end)
  |> Enum.sum()