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

Day 9

2021/day_09.livemd

Day 9

Input

Mix.install([{:kino, github: "livebook-dev/kino"}])
textarea = Kino.Input.textarea("Input:")

Common

defmodule Common do
  def parse_input(raw_input) do
    raw_input
    |> String.split()
    |> Enum.with_index()
    |> Enum.flat_map(fn {line, y} ->
      line
      |> String.split("", trim: true)
      |> Enum.with_index()
      |> Enum.map(fn {num_as_str, x} ->
        {
          {x, y},
          String.to_integer(num_as_str)
        }
      end)
    end)
    |> Enum.into(%{})
  end
end
raw_input = Kino.Input.read(textarea)
input = Common.parse_input(raw_input)

Part 1

defmodule Part1 do
  def run(input) do
    input
    |> Enum.filter(fn {{x, y}, value} ->
      Enum.all?(
        [
          input[{x - 1, y}],
          input[{x + 1, y}],
          input[{x, y - 1}],
          input[{x, y + 1}]
        ],
        &(&1 > value)
      )
    end)
    |> Enum.map(&(elem(&1, 1) + 1))
    |> Enum.sum()
  end
end
Part1.run(input)

Part 2

defmodule Part2 do
  def run(input) do
    {{max_x, _}, _} = Enum.max_by(input, fn {{x, _y}, _} -> x end)
    {{_, max_y}, _} = Enum.max_by(input, fn {{_x, y}, _} -> y end)

    input = Map.map(input, fn {_key, value} -> {value, nil} end)

    Enum.reduce(0..max_x, input, fn x, input ->
      Enum.reduce(0..max_y, input, fn y, input ->
        find_basin(input, x, y, {x, y})
      end)
    end)
    |> Enum.group_by(fn {_, {_, basin}} -> basin end)
    |> Map.delete(nil)
    |> Enum.map(fn {_key, value} -> Enum.count(value) end)
    |> Enum.sort(:desc)
    |> Enum.take(3)
    |> Enum.product()
  end

  def find_basin(input, x, y, basin_start) do
    case input[{x, y}] do
      nil ->
        input

      {9, _} ->
        input

      {_height, nil} ->
        input
        |> Map.update!({x, y}, fn {height, _} -> {height, basin_start} end)
        |> find_basin(x - 1, y, basin_start)
        |> find_basin(x + 1, y, basin_start)
        |> find_basin(x, y - 1, basin_start)
        |> find_basin(x, y + 1, basin_start)

      _ ->
        input
    end
  end
end
Part2.run(input)