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

Advent of Code 2022 - Day 8

day08.livemd

Advent of Code 2022 - Day 8

Day 8: Treetop Tree House

Description

Utils

defmodule Load do
  def file(path) do
    File.read!(__DIR__ <> path)
    |> String.split("\n")
  end

  def string(value) do
    value |> String.split("\n", trim: true)
  end
end

Input

input = Load.file("/data/day08.txt")

Part 1

Consider your map; how many trees are visible from outside the grid?

defmodule Part1 do
  def count(forest) do
    forest_width = forest |> Enum.at(0) |> Enum.count()
    forest_height = forest |> Enum.count()

    visible_inner_trees =
      1..(forest_height - 2)
      |> Enum.flat_map(fn index_y ->
        1..(forest_width - 2)
        |> Enum.map(fn index_x ->
          tree_height = forest |> Enum.at(index_y) |> Enum.at(index_x) |> String.to_integer()

          check(forest, index_x, index_y, tree_height, :right) or
            check(forest, index_x, index_y, tree_height, :left) or
            check(forest, index_x, index_y, tree_height, :up) or
            check(forest, index_x, index_y, tree_height, :down)
        end)
      end)
      |> Enum.filter(&amp; &amp;1)
      |> Enum.count()

    visible_inner_trees + forest_width * 2 + forest_height * 2 - 4
  end

  defp check(forest, index_x, index_y, tree_height, :right) do
    forest_width = forest |> Enum.at(0) |> Enum.count()

    (index_x + 1)..(forest_width - 1)
    |> Enum.all?(fn x ->
      check(forest, x, index_y, tree_height)
    end)
  end

  defp check(forest, index_x, index_y, tree_height, :left) do
    0..(index_x - 1)
    |> Enum.all?(fn x ->
      check(forest, x, index_y, tree_height)
    end)
  end

  defp check(forest, index_x, index_y, tree_height, :up) do
    0..(index_y - 1)
    |> Enum.all?(fn y ->
      check(forest, index_x, y, tree_height)
    end)
  end

  defp check(forest, index_x, index_y, tree_height, :down) do
    forest_height = forest |> Enum.count()

    (index_y + 1)..(forest_height - 1)
    |> Enum.all?(fn y ->
      check(forest, index_x, y, tree_height)
    end)
  end

  defp check(forest, x, y, tree_height) do
    forest |> Enum.at(y) |> Enum.at(x) |> String.to_integer() < tree_height
  end
end

input
|> Enum.map(fn row -> row |> String.split("", trim: true) end)
|> Part1.count()

Result

1829

Part 2

Consider each tree on your map. What is the highest scenic score possible for any tree?

defmodule Part2 do
  def scenic_scores(forest) do
    forest_width = forest |> Enum.at(0) |> Enum.count()
    forest_height = forest |> Enum.count()

    1..(forest_height - 2)
    |> Enum.flat_map(fn index_y ->
      1..(forest_width - 2)
      |> Enum.map(fn index_x ->
        tree_height = forest |> Enum.at(index_y) |> Enum.at(index_x) |> String.to_integer()

        count(forest, index_x, index_y, tree_height, :right) *
          count(forest, index_x, index_y, tree_height, :left) *
          count(forest, index_x, index_y, tree_height, :up) *
          count(forest, index_x, index_y, tree_height, :down)
      end)
    end)
  end

  defp count(forest, index_x, index_y, tree_height, :right) do
    forest_width = forest |> Enum.at(0) |> Enum.count()

    (index_x + 1)..(forest_width - 1)
    |> Enum.reduce_while(0, fn x, sum ->
      height = forest |> Enum.at(index_y) |> Enum.at(x) |> String.to_integer()
      check(height, tree_height, sum)
    end)
  end

  defp count(forest, index_x, index_y, tree_height, :left) do
    0..(index_x - 1)
    |> Enum.reverse()
    |> Enum.reduce_while(0, fn x, sum ->
      height = forest |> Enum.at(index_y) |> Enum.at(x) |> String.to_integer()
      check(height, tree_height, sum)
    end)
  end

  defp count(forest, index_x, index_y, tree_height, :up) do
    0..(index_y - 1)
    |> Enum.reverse()
    |> Enum.reduce_while(0, fn y, sum ->
      height = forest |> Enum.at(y) |> Enum.at(index_x) |> String.to_integer()
      check(height, tree_height, sum)
    end)
  end

  defp count(forest, index_x, index_y, tree_height, :down) do
    forest_height = forest |> Enum.count()

    (index_y + 1)..(forest_height - 1)
    |> Enum.reduce_while(0, fn y, sum ->
      height = forest |> Enum.at(y) |> Enum.at(index_x) |> String.to_integer()
      check(height, tree_height, sum)
    end)
  end

  defp check(height, tree_height, sum) do
    cond do
      height == tree_height -> {:halt, sum + 1}
      height < tree_height -> {:cont, sum + 1}
      true -> {:halt, sum + 1}
    end
  end
end

input
|> Enum.map(fn row -> row |> String.split("", trim: true) end)
|> Part2.scenic_scores()
|> Enum.max()

Result

291840