Powered by AppSignal & Oban Pro

2022 - day 8

2022/elixir/day-8.livemd

2022 - day 8

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

Section

input = Kino.Input.textarea("")
input_1 = Kino.Input.textarea("")
defmodule Part1 do
  def parse(input) do
    grid =
      input
      |> String.split("\n")
      |> Enum.with_index(1)
      |> Enum.reduce(%{}, fn {line, row_n}, grid ->
        String.graphemes(line)
        |> Enum.with_index(1)
        |> Enum.reduce(grid, fn {height, col_n}, grid ->
          grid
          |> Map.put({row_n, col_n}, String.to_integer(height))
        end)
      end)

    {row, col} = Map.keys(grid) |> Enum.max()
    {grid, {row, col}}
  end

  def count_visible({grid, {row, col}}) do
    [
      filter_visible({1, 1}, {1, col}, :top, grid),
      filter_visible({row, 1}, {row, col}, :bottom, grid),
      filter_visible({1, col}, {row, col}, :left, grid),
      filter_visible({1, 1}, {row, 1}, :right, grid)
    ]
    |> Enum.reduce(&MapSet.union(&1, &2))
  end

  def filter_visible({sr, sc}, {er, ec}, direction, grid) do
    for r <- sr..er, c <- sc..ec, reduce: MapSet.new() do
      acc ->
        acc = MapSet.put(acc, {r, c})
        do_filter_visible({r, c}, dir(direction), grid, acc, grid[{r, c}])
    end
  end

  defp do_filter_visible({r, c}, {dr, dc}, grid, acc, _cur_max_height)
       when not is_map_key(grid, {r + dr, c + dc}),
       do: acc

  defp do_filter_visible({r, c}, {dr, dc}, grid, acc, max_height) do
    new_acc =
      if max_height < grid[{r + dr, c + dc}] do
        acc |> MapSet.put({r + dr, c + dc})
      else
        acc
      end

    new_max_height = max(grid[{r + dr, c + dc}], max_height)
    do_filter_visible({r + dr, c + dc}, {dr, dc}, grid, new_acc, new_max_height)
  end

  defp dir(:top), do: {1, 0}
  defp dir(:bottom), do: {-1, 0}
  defp dir(:right), do: {0, 1}
  defp dir(:left), do: {0, -1}
end

input
|> Kino.Input.read()
|> Part1.parse()
|> Part1.count_visible()
|> MapSet.size()
|> dbg()
defmodule Part2 do
  def solve({grid, {rown, coln}}) do
    for r <- 1..rown, c <- 1..coln do
      scenic_scores({r, c}, grid)
    end
    |> Enum.max()
  end

  def scenic_scores({r, c}, grid) do
    for d <- [:up, :down, :left, :right] do
      count_visible({r, c}, dir(d), grid[{r, c}], grid)
    end
    |> Enum.product()
  end

  defp count_visible({sr, sc}, {dr, dc}, height, grid) do
    Stream.iterate({sr + dr, sc + dc}, fn {r, c} -> {r + dr, c + dc} end)
    |> Enum.reduce_while(0, fn
      {r, c}, cnt ->
        cond do
          not is_map_key(grid, {r, c}) ->
            {:halt, cnt}

          grid[{r, c}] >= height ->
            {:halt, cnt + 1}

          true ->
            {:cont, cnt + 1}
        end
    end)
  end

  defp dir(:up), do: {-1, 0}
  defp dir(:down), do: {1, 0}
  defp dir(:right), do: {0, 1}
  defp dir(:left), do: {0, -1}
end

input
|> Kino.Input.read()
|> Part1.parse()
|> Part2.solve()
|> dbg()