Powered by AppSignal & Oban Pro

Day 9

2021/day09.livemd

Day 9

Setup

Mix.install([
  {:nx, github: "elixir-nx/nx", sparse: "nx"},
  {:kino, github: "livebook-dev/kino"}
])
:ok

Input

input =
  File.read!("day9.txt")
  |> String.split("\n", trim: true)
  |> Enum.map(&String.to_charlist(String.trim(&1)))
  |> Nx.tensor(names: [:y, :x])
  |> Nx.subtract(?0)
  |> Nx.add(1)

{width, height} = shape = Nx.shape(input)
{100, 100}

Task 1

minima = fn padded, size, axis ->
  shifted = Nx.slice_axis(padded, 0, size, axis)
  x1 = Nx.less(input, shifted)

  shifted = Nx.slice_axis(padded, 2, size, axis)
  x2 = Nx.less(input, shifted)

  Nx.logical_and(x1, x2)
end

padded = Nx.pad(input, 99, [{0, 0, 0}, {1, 1, 0}])

x = minima.(padded, width, :x)

padded = Nx.pad(input, 99, [{1, 1, 0}, {0, 0, 0}])

y = minima.(padded, height, :y)

minimas = Nx.logical_and(x, y)

input
|> Nx.multiply(minimas)
|> Nx.sum()
|> Nx.to_number()
452

Task 2

input
|> Nx.equal(10)
|> Nx.logical_not()
|> Nx.select(Nx.iota(shape), 9999)
|> Nx.to_flat_list()
|> Enum.reject(&(&1 == 9999))
|> Enum.map(fn point -> {div(point, width), rem(point, width)} end)
|> Enum.reduce([], fn {y, x} = point, basins ->
  basin_left = Enum.find_index(basins, &({y, x - 1} in &1))
  basin_up = Enum.find_index(basins, &({y - 1, x} in &1))

  case {basin_left, basin_up} do
    {nil, nil} ->
      [MapSet.new([point]) | basins]

    {idx, nil} ->
      List.update_at(basins, idx, &MapSet.put(&1, point))

    {nil, idx} ->
      List.update_at(basins, idx, &MapSet.put(&1, point))

    {idx, idx} ->
      List.update_at(basins, idx, &MapSet.put(&1, point))

    {idx1, idx2} ->
      {old, basins} = List.pop_at(basins, max(idx1, idx2))

      List.update_at(basins, min(idx1, idx2), &(&1 |> MapSet.union(old) |> MapSet.put(point)))
  end
end)
|> Enum.map(&MapSet.size/1)
|> Enum.sort(:desc)
|> Enum.take(3)
|> Enum.reduce(&*/2)
1263735