Powered by AppSignal & Oban Pro

AoC 2022 Day 8

2022/day08.livemd

AoC 2022 Day 8

Mix.install([:kino])

defmodule Utils do
  def split(line, sep \\ "") do
    String.split(line, sep, trim: true)
  end

  def split_all_lines(text, sep \\ "") do
    text
    |> String.split("\n", trim: true)
    |> Enum.map(&split(&1, sep))
  end

  def to_numbers(number) when is_binary(number) do
    String.to_integer(number)
  end

  def to_numbers(numbers) when is_list(numbers) do
    Enum.map(numbers, &to_numbers/1)
  end

  def to_matrix(text, sep \\ "") do
    text
    |> split_all_lines(sep)
    |> then(fn data ->
      for {row, r} <- Enum.with_index(data), {col, c} <- Enum.with_index(row) do
        {{r, c}, String.to_integer(col)}
      end
    end)
    |> Map.new()
  end
end

Setup

import Utils
input = Kino.Input.textarea("Input:")
text = Kino.Input.read(input)
data = to_matrix(text)

P1

defmodule P1 do
  def solve(text) do
    edge = text |> String.split("\n", trim: true) |> length
    edge_visible = edge |> Kernel.*(4) |> Kernel.-(4)

    data = to_matrix(text)

    visible =
      Enum.reduce(1..(edge - 2), MapSet.new(), fn row, set ->
        {_, set} =
          Enum.reduce(1..(edge - 2), {data[{row, 0}], set}, fn col, {max, set} ->
            process(row, col, max, set, data)
          end)

        {_, set} =
          Enum.reduce((edge - 2)..1//-1, {data[{row, edge - 1}], set}, fn col, {max, set} ->
            process(row, col, max, set, data)
          end)

        set
      end)

    visible =
      Enum.reduce(1..(edge - 2), visible, fn col, set ->
        {_, set} =
          Enum.reduce(1..(edge - 2), {data[{0, col}], set}, fn row, {max, set} ->
            process(row, col, max, set, data)
          end)

        {_, set} =
          Enum.reduce((edge - 2)..1//-1, {data[{edge - 1, col}], set}, fn row, {max, set} ->
            process(row, col, max, set, data)
          end)

        set
      end)

    MapSet.size(visible) + edge_visible
  end

  defp process(row, col, max, set, data) do
    if (h = data[{row, col}]) > max do
      {h, MapSet.put(set, {row, col})}
    else
      {max, set}
    end
  end
end

P1.solve(text)

P2

defmodule P2 do
  def solve(text) do
    edge = text |> String.split("\n", trim: true) |> length

    data = to_matrix(text)

    map =
      Enum.reduce(0..(edge - 1), %{}, fn row, map ->
        {_, map} =
          Enum.reduce(
            0..(edge - 1),
            {%{}, map},
            fn col, {near, map} ->
              h = data[{row, col}]
              v = Enum.map(9..h//-1, &amp;near[&amp;1]) |> Enum.reject(&amp;is_nil/1)

              v =
                if match?([], v) do
                  col
                else
                  col - Enum.max(v)
                end

              {Map.put(near, h, col), Map.update(map, {row, col}, v, &amp;(&amp;1 * v))}
            end
          )

        {_, map} =
          Enum.reduce(
            (edge - 1)..0//-1,
            {%{}, map},
            fn col, {near, map} ->
              h = data[{row, col}]
              v = Enum.map(9..h//-1, &amp;near[&amp;1]) |> Enum.reject(&amp;is_nil/1)

              v =
                if match?([], v) do
                  edge - 1 - col
                else
                  Enum.min(v) - col
                end

              {Map.put(near, h, col), Map.update(map, {row, col}, v, &amp;(&amp;1 * v))}
            end
          )

        map
      end)

    map =
      Enum.reduce(0..(edge - 1), map, fn col, map ->
        {_, map} =
          Enum.reduce(
            0..(edge - 1),
            {%{}, map},
            fn row, {near, map} ->
              h = data[{row, col}]
              v = Enum.map(9..h//-1, &amp;near[&amp;1]) |> Enum.reject(&amp;is_nil/1)

              v =
                if match?([], v) do
                  row
                else
                  row - Enum.max(v)
                end

              {Map.put(near, h, row), Map.update(map, {row, col}, v, &amp;(&amp;1 * v))}
            end
          )

        {_, map} =
          Enum.reduce(
            (edge - 1)..0//-1,
            {%{}, map},
            fn row, {near, map} ->
              h = data[{row, col}]
              v = Enum.map(9..h//-1, &amp;near[&amp;1]) |> Enum.reject(&amp;is_nil/1)

              v =
                if match?([], v) do
                  edge - 1 - row
                else
                  Enum.min(v) - row
                end

              {Map.put(near, h, row), Map.update(map, {row, col}, v, &amp;(&amp;1 * v))}
            end
          )

        map
      end)

    map |> Map.values() |> Enum.max()
  end
end

P2.solve(text)