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

Advent of code day 09

2021/livebooks/day-09.livemd

Advent of code day 09

Mix.install([
  {:kino, "~> 0.5.0"}
])

Setup input

example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")
defmodule Basin do
  def basin(point, grid) do
    basin(MapSet.new(), point, grid)
  end

  defp basin(set, {x, y} = point, grid) do
    if grid[point] in [nil, 9] or point in set do
      set
    else
      set
      |> MapSet.put(point)
      |> basin({x + 1, y}, grid)
      |> basin({x - 1, y}, grid)
      |> basin({x, y + 1}, grid)
      |> basin({x, y - 1}, grid)
    end
  end
end

Setup

grid =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    line
    |> String.split("", trim: true)
    |> Enum.map(&String.to_integer/1)
    |> List.to_tuple()
  end)
  |> List.to_tuple()

rows = grid |> tuple_size()
cols = grid |> elem(0) |> tuple_size()

Part 01

values =
  for x <- 0..(rows - 1),
      y <- 0..(cols - 1) do
    current = elem(grid, x) |> elem(y)

    left = if x == 0, do: 10, else: elem(grid, x - 1) |> elem(y)
    right = if x == rows - 1, do: 10, else: elem(grid, x + 1) |> elem(y)
    up = if y == 0, do: 10, else: elem(grid, x) |> elem(y - 1)
    down = if y == cols - 1, do: 10, else: elem(grid, x) |> elem(y + 1)

    if left > current and right > current and up > current and down > current do
      current
    else
      nil
    end
  end
  |> Enum.filter(&amp;Kernel.!=(&amp;1, nil))
  |> Enum.map(&amp;(&amp;1 + 1))
  |> Enum.sum()

Part 02

map_of_points =
  for x <- 0..(rows - 1),
      y <- 0..(cols - 1),
      into: %{} do
    current = elem(grid, x) |> elem(y)

    {{x, y}, current}
  end

lowest_points =
  for x <- 0..(rows - 1),
      y <- 0..(cols - 1) do
    current = elem(grid, x) |> elem(y)

    left = if x == 0, do: 10, else: elem(grid, x - 1) |> elem(y)
    right = if x == rows - 1, do: 10, else: elem(grid, x + 1) |> elem(y)
    up = if y == 0, do: 10, else: elem(grid, x) |> elem(y - 1)
    down = if y == cols - 1, do: 10, else: elem(grid, x) |> elem(y + 1)

    if left > current and right > current and up > current and down > current do
      {x, y}
    else
      nil
    end
  end
  |> Enum.filter(&amp;Kernel.!=(&amp;1, nil))

lowest_points
|> Enum.map(fn point ->
  Basin.basin(point, map_of_points)
end)
|> Enum.map(&amp;MapSet.size/1)
|> Enum.sort(:desc)
|> Enum.take(3)
|> Enum.product()