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

Day 8

2022/day_8.livemd

Day 8

Mix.install(
  [
    {:advent_of_code_utils, "~> 3.1"}
  ],
  config: [
    advent_of_code_utils: [session: System.fetch_env!("LB_AOC_TOKEN")]
  ]
)

Mix.Tasks.Aoc.Get.run(["--year", "2022", "--day", "8"])

Inputs

example_input = AOC.example_string(2022, 8)
input = AOC.input_string(2022, 8)

Parser

defmodule Parser do
  def parse(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, "", trim: true))
    |> Enum.map(fn row ->
      Enum.map(row, &String.to_integer/1)
    end)
  end
end
Parser.parse(example_input)

TreeGrid

defmodule TreeGrid do
  def transpose(matrix) do
    matrix
    |> List.zip()
    |> Enum.map(&Tuple.to_list/1)
  end

  def visible_from_outside?(grid, x, y) do
    visible_along_x_axis?(grid, x, y) or visible_along_y_axis?(grid, x, y)
  end

  def visible_along_x_axis?(grid, x, y) do
    grid_width = length(hd(grid))

    if x <= 0 or x >= grid_width - 1 do
      # It is automatically visible if it is on an edge
      true
    else
      # Check trees to the left and right of the tree
      {left_of, [self | right_of]} =
        grid
        |> Enum.at(y)
        |> Enum.split(x)

      Enum.max(left_of) < self or Enum.max(right_of) < self
    end
  end

  def visible_along_y_axis?(grid, x, y) do
    visible_along_x_axis?(transpose(grid), y, x)
  end

  def scenic_score(grid, x, y) do
    scenic_score_x(grid, x, y) * scenic_score_y(grid, x, y)
  end

  def scenic_score_x(grid, x, y) do
    # Get the trees to the left and right of the tree
    {left_of, [self | right_of]} =
      grid
      |> Enum.at(y)
      |> Enum.split(x)

    left_view_length =
      left_of
      |> Enum.reverse()
      |> Enum.find_index(&amp;(&amp;1 >= self))
      |> then(fn len ->
        if len == nil do
          length(left_of)
        else
          len
        end
      end)

    left_view = Enum.take(left_of, left_view_length + 1)

    right_view_length =
      right_of
      |> Enum.find_index(&amp;(&amp;1 >= self))
      |> then(fn len ->
        if len == nil do
          length(right_of)
        else
          len
        end
      end)

    right_view = Enum.take(right_of, right_view_length + 1)

    length(left_view) * length(right_view)
  end

  def scenic_score_y(grid, x, y) do
    scenic_score_x(transpose(grid), y, x)
  end
end

Part 1

grid = Parser.parse(example_input)
width = length(hd(grid))
height = length(grid)

for x <- 0..(width - 1),
    y <- 0..(height - 1),
    TreeGrid.visible_from_outside?(grid, x, y),
    reduce: 0 do
  acc -> acc + 1
end
grid = Parser.parse(input)
width = length(hd(grid))
height = length(grid)

for x <- 0..(width - 1),
    y <- 0..(height - 1),
    TreeGrid.visible_from_outside?(grid, x, y),
    reduce: 0 do
  acc -> acc + 1
end

Part 2

grid = Parser.parse(example_input)
width = length(hd(grid))
height = length(grid)

for x <- 0..(width - 1), y <- 0..(height - 1) do
  TreeGrid.scenic_score(grid, x, y)
end
|> Enum.max()
grid = Parser.parse(input)
width = length(hd(grid))
height = length(grid)

for x <- 0..(width - 1), y <- 0..(height - 1) do
  TreeGrid.scenic_score(grid, x, y)
end
|> Enum.max()