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

Day 8

day8.livemd

Day 8

Mix.install([
  {:kino, "~> 0.14.2"}
])

Part 1

matrix = Kino.FS.file_path("day8-in.txt")
  |> File.read!()
  |> String.trim()
  |> String.split("\n")
  |> Enum.map(&String.graphemes/1)

matrix_to_map = fn matrix ->
  row_count = length(matrix) - 1
  col_count = length(Enum.at(matrix, 0)) - 1

  Stream.flat_map(0..row_count, fn row ->
    Stream.map(0..col_count, fn col ->
      {
        {row, col},
        Enum.at(matrix, row) |> Enum.at(col)
      }
    end)
  end)
  |> Enum.into(%{})
end

grid = matrix_to_map.(matrix)
defmodule Antinode do
  def get({x, y}, {x1, y1}) do
    {x1 - (x - x1), y1 - (y - y1)}
  end

  def from_list(_, []), do: []
  def from_list(a, [b | rest]) do
    this = if a != b, do: [get(a, b), get(b, a)], else: []
    this ++ from_list(a, rest)
  end
end

start = System.monotonic_time(:microsecond)

waves = Map.values(grid) |> MapSet.new() |> MapSet.delete(".")
valid_locs = Map.keys(grid)

result = waves
  |> Stream.map(fn wave ->  # Get tower locations
      Map.filter(grid, fn {_, v} -> v == wave end) 
      |> Map.keys() 
    end)
  |> Stream.flat_map(fn node_list ->
      Stream.flat_map(node_list, fn node ->
        Antinode.from_list(node, node_list)
      end)
    end)
  |> Stream.uniq()
  |> Stream.filter(& &1 in valid_locs)
  |> Enum.count()

IO.puts("Result: #{result}")
elapsed = System.monotonic_time(:microsecond) - start
IO.puts "Finished in #{elapsed / 1000}ms"

Part 2

defmodule Harmonodes do
  import Antinode, only: [get: 2]

  def rget(a, b, row_lim, col_lim, acc \\ [])
  def rget(a, b = {x1, y1}, row_lim, col_lim, acc)
    when 0 <= x1 and x1 <= row_lim and 0 <= y1 and y1 <= col_lim do
    antinode = get(a, b)
    rget(b, antinode, row_lim, col_lim, [antinode | acc])
  end
  def rget(_, _, _, _, [_discard | acc]), do: acc

  def from_list(a, nodes, row_lim, col_lim)
  def from_list(_, [], _, _), do: []
  def from_list(a, [b | rest], row_lim, col_lim) do
    this = if a != b do
      [rget(a, b, row_lim, col_lim), rget(b, a, row_lim, col_lim)] |> List.flatten()
    else
      []
    end
    this ++ [a] ++ from_list(a, rest, row_lim, col_lim)
  end
end

start = System.monotonic_time(:microsecond)

waves = Map.values(grid) |> Stream.uniq() |> Stream.filter(&amp; &amp;1 != ".")
{{row_lim, col_lim}, _} = Map.to_list(grid) |> Enum.max()

result = waves
  |> Stream.map(fn wave -> Map.filter(grid, fn {_, v} -> v == wave end) |> Map.keys() end)
  |> Stream.flat_map(fn node_set ->
      Stream.flat_map(node_set, fn node ->
        Harmonodes.from_list(node, node_set, row_lim, col_lim)
      end)
    end)
  |> Enum.uniq()
  |> Enum.filter(&amp; &amp;1 in valid_locs)
  |> Enum.count()

IO.puts("Result: #{result}")
elapsed = System.monotonic_time(:microsecond) - start
IO.puts "Finished in #{elapsed / 1000}ms"