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

Day 15: Beacon Exclusion Zone

2022/day15.livemd

Day 15: Beacon Exclusion Zone

Mix.install([:kino])

Section

input = Kino.Input.textarea("Input")
sensors =
  input
  |> Kino.Input.read()
  |> String.split(["Sensor at x=", ", y=", ": closest beacon is at x=", "\n"], trim: true)
  |> Enum.map(&String.to_integer/1)
  |> Enum.chunk_every(2)
  |> Enum.chunk_every(2)
  |> Enum.map(fn [x, y] ->
    {List.to_tuple(x), List.to_tuple(y)}
  end)
defmodule Signal do
  def manhattan({x0, y0}, {x1, y1}) do
    abs(x0 - x1) + abs(y0 - y1)
  end

  def generate_edge({x, y}, d, range \\ 0..4_000_000, domain \\ 0..4_000_000) do
    []
    |> Stream.concat(Stream.map(0..(d - 1), &{x + d - &1, y + &1}))
    |> Stream.concat(Stream.map(0..(d - 1), &{x - &1, y + d - &1}))
    |> Stream.concat(Stream.map(0..(d - 1), &{x - d + &1, y - &1}))
    |> Stream.concat(Stream.map(0..(d - 1), &{x + &1, y - d + &1}))
    |> Stream.filter(fn {x, y} ->
      x in range and y in domain
    end)
  end

  def scan(sensors) do
    Enum.find_value(sensors, fn {sensor, d} ->
      sensor
      |> generate_edge(d + 1)
      |> Enum.find(fn coords ->
        not Enum.any?(sensors, fn {pos, d} ->
          manhattan(pos, coords) < d
        end)
      end)
    end)
  end

  def merge(r, []), do: [r]

  def merge(r, ranges) do
    case Enum.split_with(ranges, &amp;Range.disjoint?(&amp;1, r)) do
      {disjoint, []} ->
        [r | disjoint]

      {disjoint, joint} ->
        [r | joint]
        |> Enum.flat_map(fn first..last -> [first, last] end)
        |> Enum.min_max()
        |> then(fn {min, max} -> [min..max | disjoint] end)
    end
  end
end
beacons = Enum.count(sensors, fn {_, {_, y}} -> y == 2_000_000 end)

Enum.reduce(sensors, [], fn {{x, y}, beacon}, signals ->
  case Signal.manhattan({x, y}, beacon) - abs(y - 2_000_000) do
    d when d < 0 -> signals
    d -> Signal.merge((x - d)..(x + d), signals)
  end
end)
|> Enum.map(&amp;Range.size/1)
|> Enum.sum()
|> Kernel.-(beacons)
Enum.map(sensors, fn {sensor, beacon} ->
  {sensor, Signal.manhattan(sensor, beacon)}
end)
|> Signal.scan()
|> then(fn {x, y} ->
  x * 4_000_000 + y
end)