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

--- Day 9: Smoke Basin ---

2021/day09.livemd

— Day 9: Smoke Basin —

Setup

defmodule Setup do
  def get_input(prompt) do
    case IO.gets(prompt) do
      :eof -> ""
      line -> line <> get_input(prompt)
    end
  end

  def parse_input(binary) do
    binary
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      line
      |> String.split("", trim: true)
      |> Enum.map(&amp;String.to_integer/1)
    end)
    |> then(fn input ->
      {height, width} = {length(input), length(hd(input))}

      for x <- 0..(height - 1), y <- 0..(width - 1), into: %{} do
        {{x, y}, get_in(input, [Access.at(x), Access.at(y)])}
      end
    end)
  end
end

map =
  Setup.get_input("input")
  |> Setup.parse_input()
defmodule LavaTubes do
  def lowpoints(map) do
    map
    |> Enum.filter(fn {{x, y}, h} ->
      map
      |> Map.take([{x + 1, y}, {x - 1, y}, {x, y - 1}, {x, y + 1}])
      |> Map.values()
      |> Enum.all?(&amp;(&amp;1 > h))
    end)
    |> Map.new()
  end

  def explore(point, map, basin \\ MapSet.new())

  def explore({x, y} = point, map, basin) do
    if map[point] &amp;&amp; point not in basin do
      [{x, y + 1}, {x, y - 1}, {x + 1, y}, {x - 1, y}]
      |> Enum.reduce(MapSet.put(basin, point), fn point, basin ->
        next = map[point]

        if next < 9 do
          MapSet.union(basin, explore(point, map, basin))
        else
          basin
        end
      end)
    else
      MapSet.new()
    end
  end
end

Part1

map
|> LavaTubes.lowpoints()
|> Map.values()
|> then(fn values -> Enum.count(values) + Enum.sum(values) end)

Part2

map
|> LavaTubes.lowpoints()
|> Map.keys()
|> Enum.map(&amp;LavaTubes.explore(&amp;1, map))
|> Enum.uniq()
|> Enum.map(&amp;MapSet.size/1)
|> Enum.sort(:desc)
|> Enum.take(3)
|> Enum.product()

ASCII MAP

Setup.get_input("input")
|> String.split("\n", trim: true)
|> then(fn [l | ls] ->
  border = String.duplicate("9", String.length(l))
  [border, l | ls] ++ [border]
end)
|> Enum.map(fn line ->
  line
  |> then(&amp;("9" <> &amp;1 <> "9"))
  |> String.split("", trim: true)
  |> Enum.map(fn
    "9" -> "#"
    _ -> " "
  end)
  |> Enum.join()
end)
|> Enum.join("\n")
|> IO.puts()