Powered by AppSignal & Oban Pro

Day 08

2022/day-08.livemd

Day 08

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

example_input =
  Kino.Input.textarea("example input:")
  |> Kino.render()

real_input = Kino.Input.textarea("real input:")

Common

defmodule Grid do
  def find_visible(grid, size) do
    for i <- 1..(size - 2), j <- 1..(size - 2), reduce: [] do
      acc -> if visible?(grid, size, {i, j}), do: [{i, j} | acc], else: acc
    end
  end

  defp lines_of_sight(grid, size, {i, j}) do
    [
      {(i - 1)..0//-1, Stream.iterate(j, &amp; &amp;1)},
      {(i + 1)..(size - 1), Stream.iterate(j, &amp; &amp;1)},
      {Stream.iterate(i, &amp; &amp;1), (j - 1)..0//-1},
      {Stream.iterate(i, &amp; &amp;1), (j + 1)..(size - 1)}
    ]
    |> Enum.map(fn {is, js} ->
      for coord <- Enum.zip(is, js), into: [], do: grid[coord]
    end)
  end

  def new(list) do
    size = length(list)

    grid =
      for {row, i} <- Enum.with_index(list), {val, j} <- Enum.with_index(row), into: %{} do
        {{i, j}, val}
      end

    {grid, size}
  end

  def scenic_scores(grid, size) do
    for i <- 1..(size - 2), j <- 1..(size - 2), into: [] do
      scenic_score(grid, size, {i, j})
    end
  end

  defp scenic_score(grid, size, coord) do
    height = grid[coord]

    lines_of_sight(grid, size, coord)
    |> Enum.map(
      &amp;Enum.reduce_while(&amp;1, 0, fn other, count ->
        if other < height, do: {:cont, count + 1}, else: {:halt, count + 1}
      end)
    )
    |> Enum.product()
  end

  defp visible?(grid, size, coord) do
    height = grid[coord]

    lines_of_sight(grid, size, coord)
    |> Enum.any?(fn line_of_sight -> Enum.all?(line_of_sight, &amp;(&amp;1 < height)) end)
  end
end

parse = fn input ->
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn row -> String.graphemes(row) |> Enum.map(&amp;String.to_integer/1) end)
end

Part 1

{grid, size} =
  real_input
  |> then(parse)
  |> Grid.new()

Grid.find_visible(grid, size)
|> length()
|> Kernel.+((size - 1) * 4)

Part 2

{grid, size} =
  real_input
  |> then(parse)
  |> Grid.new()

Grid.scenic_scores(grid, size)
|> Enum.max()