Powered by AppSignal & Oban Pro

d08

d08/d08.livemd

d08

Section

defmodule D08 do
  def parse(input) do
    for {line, row} <- Enum.with_index(String.split(input, "\n", trim: true)),
        {char, col} <- Enum.with_index(String.to_charlist(line)),
        reduce: {%{}, 0, 0} do
      {frequencies, height, width} ->
        frequencies =
          if char == ?. do
            frequencies
          else
            Map.update(frequencies, char, [{row, col}], &amp;[{row, col} | &amp;1])
          end

        {frequencies, max(height, row + 1), max(width, col + 1)}
    end
  end

  def in_bounds?({r, c}, height, width) do
    r >= 0 and c >= 0 and r < height and c < width
  end

  def part1(frequencies, height, width) do
    Map.values(frequencies)
    |> Enum.flat_map(fn antennas ->
      for {ar, ac} = a <- antennas,
          {br, bc} = b <- antennas,
          a < b do
        {dr, dc} = {br - ar, bc - ac}

        Enum.filter([{ar - dr, ac - dc}, {br + dr, bc + dc}], &amp;D08.in_bounds?(&amp;1, height, width))
      end
      |> List.flatten()
    end)
    |> Enum.uniq()
    |> Enum.count()
  end

  def part2(frequencies, height, width) do
    Map.values(frequencies)
    |> Enum.flat_map(fn antennas ->
      for {ar, ac} = a <- antennas,
          {br, bc} = b <- antennas,
          a < b do
        {dr, dc} = {br - ar, bc - ac}

        Stream.concat(
          Stream.iterate(b, fn {r, c} -> {r - dr, c - dc} end)
          |> Stream.take_while(&amp;D08.in_bounds?(&amp;1, height, width)),
          Stream.iterate(b, fn {r, c} -> {r + dr, c + dc} end)
          |> Stream.take_while(&amp;D08.in_bounds?(&amp;1, height, width))
        )
        |> Enum.to_list()
      end
      |> List.flatten()
    end)
    |> Enum.uniq()
    |> Enum.count()
  end
end
ExUnit.start()

defmodule D08.Test do
  use ExUnit.Case

  test "sample" do
    sample = "............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
"

    {frequencies, height, width} = D08.parse(sample)
    assert 14 == D08.part1(frequencies, height, width)
    assert 34 == D08.part2(frequencies, height, width)
  end
end

ExUnit.run()
input = File.read!(__DIR__ <> "/input")
{frequencies, height, width} = D08.parse(input)
D08.part1(frequencies, height, width)
D08.part2(frequencies, height, width)