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

Advent 2022 - Day 8

day8.livemd

Advent 2022 - Day 8

Mix.install([
  {:kino, github: "livebook-dev/kino"}
])
:ok

Setup

input = Kino.Input.textarea("Please paste your input file:")
trees =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn row -> row |> String.graphemes() |> Enum.map(&String.to_integer/1) end)
[[3, 0, 3, 7, 3], [2, 5, 5, 1, 2], [6, 5, 3, 3, 2], [3, 3, 5, 4, 9], [3, 5, 3, 9, 0]]

Drawing

Not required for problem solving, but just to make the output pretty :)

defmodule Draw do
  def grid(grid) do
    for row <- grid do
      for x <- row do
        if x, do: "X", else: "_"
      end
      |> Enum.join("")
    end
    |> Enum.join("\n")
  end
end
{:module, Draw, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:grid, 1}}

Utils

defmodule Quadcopter do
  def navigate(trees) do
    x = trees
    x_rev = x |> Enum.map(&amp;Enum.reverse/1)

    y = trees |> Enum.zip() |> Enum.map(&amp;Tuple.to_list/1)
    y_rev = y |> Enum.map(&amp;Enum.reverse/1)

    [x, x_rev, y, y_rev]
  end

  def analyze_visibility(line) do
    Enum.map_reduce(line, -1, fn x, acc ->
      if x > acc do
        {false, x}
      else
        {true, acc}
      end
    end)
    |> elem(0)
  end

  def analyze_scenic_score(value, line, score \\ 0)

  def analyze_scenic_score(value, [compare | remainder], score) do
    if value > compare do
      analyze_scenic_score(value, remainder, score + 1)
    else
      score + 1
    end
  end

  def analyze_scenic_score(_value, [], score), do: score

  def analyze_scenic_score(line) do
    for {x, index} <- Enum.with_index(line) do
      seen = Enum.drop(line, index + 1)
      analyze_scenic_score(x, seen)
    end
  end

  def realign([x, x_rev, y, y_rev]) do
    x_rev = x_rev |> Enum.map(&amp;Enum.reverse/1)
    y = y |> Enum.zip() |> Enum.map(&amp;Tuple.to_list/1)
    y_rev = y_rev |> Enum.map(&amp;Enum.reverse/1) |> Enum.zip() |> Enum.map(&amp;Tuple.to_list/1)
    [x, x_rev, y, y_rev]
  end

  def combine_results(directions, combiner) do
    Enum.reduce(directions, fn direction, acc ->
      Enum.zip(direction, acc)
      |> Enum.map(&amp;Tuple.to_list/1)
      |> Enum.map(&amp;Enum.zip/1)
      |> Enum.map(fn line ->
        Enum.map(line, fn {a, b} -> combiner.(a, b) end)
      end)
    end)
  end
end
{:module, Quadcopter, <<70, 79, 82, 49, 0, 0, 18, ...>>, {:combine_results, 2}}

Part 1

visibility =
  trees
  |> Quadcopter.navigate()
  |> Enum.map(fn direction -> Enum.map(direction, &amp;Quadcopter.analyze_visibility/1) end)
  |> Quadcopter.realign()
  |> Quadcopter.combine_results(&amp;(&amp;1 and &amp;2))

Draw.grid(visibility) |> IO.write()

count_of_not_hidden =
  visibility
  |> List.flatten()
  |> Enum.count(&amp;(!&amp;1))
_____
___X_
__X__
_X_X_
_____
21

Part 2

scenic_scores =
  trees
  |> Quadcopter.navigate()
  |> Enum.map(fn direction -> Enum.map(direction, &amp;Quadcopter.analyze_scenic_score/1) end)
  |> Quadcopter.realign()
  |> Quadcopter.combine_results(&amp;(&amp;1 * &amp;2))

scenic_scores
|> List.flatten()
|> Enum.max()
8