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

Day 8

2022/day_8.livemd

Day 8

Setup

Mix.install([:kino])
input =
  Kino.Input.textarea("Paste input here",
    default: """
    30373
    25512
    65332
    33549
    35390
    """
  )
trees = Kino.Input.read(input)

forest =
  trees
  |> String.splitter("\n", trim: true)
  |> Stream.with_index()
  |> Stream.flat_map(fn {row, y} ->
    row
    |> String.splitter("", trim: true)
    |> Stream.with_index()
    |> Stream.map(fn {height, x} -> {{x, y}, String.to_integer(height)} end)
  end)
  |> Enum.into(%{})

Part 1

defmodule Forest do
  def is_visible?(_, _, {0, _}), do: true
  def is_visible?(_, _, {_, 0}), do: true
  def is_visible?(_, {xm, _}, {xm, _}), do: true
  def is_visible?(_, {_, ym}, {_, ym}), do: true

  def is_visible?(forest, {forest_width, forest_length}, {x, y}) do
    tree_height = forest[{x, y}]

    Enum.all?((x - 1)..0, fn n -> forest[{n, y}] < tree_height end) or
      Enum.all?((x + 1)..forest_width, fn n -> forest[{n, y}] < tree_height end) or
      Enum.all?((y - 1)..0, fn n -> forest[{x, n}] < tree_height end) or
      Enum.all?((y + 1)..forest_length, fn n -> forest[{x, n}] < tree_height end)
  end
end

{forest_width, forest_length} =
  Enum.reduce(forest, {0, 0}, fn {{x, y}, _}, {max_x, max_y} ->
    {max(x, max_x), max(y, max_y)}
  end)

forest
|> Enum.filter(fn {coords, _} ->
  Forest.is_visible?(forest, {forest_width, forest_length}, coords)
end)
|> Enum.count()

Part 2

defmodule Part2 do
  def scenic_score(_forest, _dims, {0, _}), do: 0
  def scenic_score(_forest, _dims, {_, 0}), do: 0
  def scenic_score(_, {xm, _}, {xm, _}), do: 0
  def scenic_score(_, {_, ym}, {_, ym}), do: 0

  def scenic_score(forest, {forest_width, forest_length}, {x, y}) do
    tree_height = forest[{x, y}]

    up =
      (y - 1)..0
      |> Enum.reduce_while(0, fn n, acc ->
        if forest[{x, n}] < tree_height, do: {:cont, acc + 1}, else: {:halt, acc + 1}
      end)

    left =
      (x - 1)..0
      |> Enum.reduce_while(0, fn n, acc ->
        if forest[{n, y}] < tree_height, do: {:cont, acc + 1}, else: {:halt, acc + 1}
      end)

    right =
      (x + 1)..forest_width
      |> Enum.reduce_while(0, fn n, acc ->
        if forest[{n, y}] < tree_height, do: {:cont, acc + 1}, else: {:halt, acc + 1}
      end)

    down =
      (y + 1)..forest_length
      |> Enum.reduce_while(0, fn n, acc ->
        if forest[{x, n}] < tree_height, do: {:cont, acc + 1}, else: {:halt, acc + 1}
      end)

    up * down * left * right
  end
end

forest
|> Stream.map(fn {coords, _} ->
  Part2.scenic_score(forest, {forest_width, forest_length}, coords)
end)
|> Enum.max()