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

Day9

2021/day9.livemd

Day9

Untitled

data =
  "input"
  |> IO.getn(1_000_000)
  |> String.trim()
  |> String.split(["\n", "\r\n"], trim: true)
  |> Enum.map(&(&1 |> String.split("", trim: true) |> Enum.map(fn n -> String.to_integer(n) end)))

data =
  for {line, row} <- Enum.with_index(data), {point, col} <- Enum.with_index(line), into: %{} do
    {{row, col}, point}
  end
defmodule P1 do
  @offsets [{-1, 0}, {1, 0}, {0, -1}, {0, 1}]

  def calc(data) do
    data
    |> Enum.filter(fn {{row, col}, point} ->
      @offsets
      |> Enum.all?(fn {dr, dc} ->
        (data[{row + dr, col + dc}] || 10) > point
      end)
    end)
    |> Enum.map(&amp;(elem(&amp;1, 1) + 1))
    |> Enum.sum()
  end
end
P1.calc(data)
defmodule P2 do
  @offsets [{-1, 0}, {1, 0}, {0, 0}, {0, -1}, {0, 1}]

  def calc(data) do
    data
    |> Enum.reject(fn {_, v} -> v == 9 end)
    |> Enum.reduce(%{}, fn item, path ->
      {_, path} = mark_low(item, path, data)
      path
    end)
    |> Enum.map(&amp;elem(&amp;1, 1))
    |> Enum.frequencies()
    |> Map.values()
    |> Enum.sort()
    |> Enum.take(-3)
    |> Enum.product()
  end

  defp mark_low({{row, col} = coords, _point} = current, path, data) do
    if dest = path[coords] do
      {dest, path}
    else
      low =
        @offsets
        |> Enum.map(fn {dr, dc} ->
          point = {row + dr, col + dc}
          {point, data[point] || 10}
        end)
        |> Enum.min_by(&amp;elem(&amp;1, 1))

      if low == current do
        {elem(low, 0), Map.put(path, coords, elem(low, 0))}
      else
        {dest, path} = mark_low(low, path, data)
        {dest, Map.put(path, coords, dest)}
      end
    end
  end
end
P2.calc(data)