Powered by AppSignal & Oban Pro

Day 08

2022/day08.livemd

Day 08

Section

defmodule Day08 do
  # input is %{{i, j} => val}
  def part1(input) do
    {w, h} = shape(input)

    set = MapSet.new()

    set =
      for i <- 0..w, reduce: set do
        set ->
          set =
            for j <- 0..h, reduce: {set, -1} do
              {set, tallest} ->
                tree = input[{i, j}]

                if tree > tallest do
                  {MapSet.put(set, {i, j}), tree}
                else
                  {set, tallest}
                end
            end
            |> elem(0)

          for j <- h..0//-1, reduce: {set, -1} do
            {set, tallest} ->
              tree = input[{i, j}]

              if tree > tallest do
                {MapSet.put(set, {i, j}), tree}
              else
                {set, tallest}
              end
          end
          |> elem(0)
      end

    set =
      for j <- 0..h, reduce: set do
        set ->
          set =
            for i <- 0..w, reduce: {set, -1} do
              {set, tallest} ->
                tree = input[{i, j}]

                if tree > tallest do
                  {MapSet.put(set, {i, j}), tree}
                else
                  {set, tallest}
                end
            end
            |> elem(0)

          for i <- w..0//-1, reduce: {set, -1} do
            {set, tallest} ->
              tree = input[{i, j}]

              if tree > tallest do
                {MapSet.put(set, {i, j}), tree}
              else
                {set, tallest}
              end
          end
          |> elem(0)
      end

    MapSet.size(set)
  end

  def part2(input) do
    {h, w} = shape = shape(input)

    for i <- 1..(h - 1), j <- 1..(w - 1) do
      visibility(input, shape, i, j)
    end
    |> Enum.max()
  end

  defp visibility(input, {h, w}, i, j) do
    curr = input[{i, j}]

    up =
      (i - 1)..0//-1
      |> Enum.find(fn ii ->
        input[{ii, j}] >= curr
      end)
      |> Kernel.||(0)
      |> then(&(i - &1))

    down =
      (i + 1)..h//1
      |> Enum.find(fn ii ->
        input[{ii, j}] >= curr
      end)
      |> Kernel.||(h)
      |> then(&(&1 - i))

    left =
      (j - 1)..0//-1
      |> Enum.find(fn jj ->
        input[{i, jj}] >= curr
      end)
      |> Kernel.||(0)
      |> then(&(j - &1))

    right =
      (j + 1)..w//1
      |> Enum.find(fn jj ->
        input[{i, jj}] >= curr
      end)
      |> Kernel.||(w)
      |> then(&(&1 - j))

    up * down * left * right
  end

  defp shape(input) do
    input
    |> Enum.max()
    |> elem(0)
  end
end
input_path = "#{__DIR__}/day08.txt"

input =
  File.read!(input_path)
  |> String.split("\n", trim: true, global: true)
  |> Enum.map(&String.split(&1, "", trim: true))

input =
  for {row, i} <- Enum.with_index(input),
      {col, j} <- Enum.with_index(row),
      into: %{},
      do: {{i, j}, String.to_integer(col)}
Day08.part1(input)
Day08.part2(input)