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

Day 15

2022/day15.livemd

Day 15

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

Setup

input = Kino.Input.textarea("input file")

Part 1 - take 4

defmodule Day15_4 do
  def parse_line(line) do
    ~r/(?<=[xy]=)[^,:\s]+/
    |> Regex.scan(line)
    |> List.flatten()
    |> Enum.map(&amp;String.to_integer/1)
    |> then(fn [xs, ys, xb, yb] ->
      [{{xs, ys}, {:sensor, manhattan({xs, ys}, {xb, yb})}}, {{xb, yb}, :beacon}]
    end)
  end

  def ranges(sensors, y) do
    sensors
    |> Stream.reject(fn {{_xc, yc}, {:sensor, radius}} ->
      abs(y - yc) > radius
    end)
    |> Stream.map(fn {{xc, yc}, {:sensor, radius}} ->
      (xc - (radius - abs(y - yc)))..(xc + (radius - abs(y - yc)))
    end)
    |> merge_ranges()
  end

  # Private methods
  defp manhattan({x1, y1}, {x2, y2}) do
    abs(x1 - x2) + abs(y1 - y2)
  end

  defp merge_ranges(ranges) do
    [h | t] = Enum.sort(ranges)
    do_merge(t, [h])
  end

  defp do_merge([_s2..e2 = r2 | t2], [s1..e1 = r1 | t1] = acc) do
    if Range.disjoint?(r1, r2) do
      do_merge(t2, [r2 | acc])
    else
      do_merge(t2, [s1..max(e1, e2) | t1])
    end
  end

  defp do_merge([], acc), do: acc
end

{:ok, stream} =
  input
  |> Kino.Input.read()
  |> StringIO.open()

information =
  stream
  |> IO.binstream(:line)
  |> Stream.flat_map(&amp;Day15_4.parse_line/1)
  |> Map.new()

sensors = Enum.filter(information, &amp;match?({_, {:sensor, _}}, &amp;1))
ranges = Day15_4.ranges(sensors, 2_000_000)

covered =
  ranges
  |> Enum.map(fn s..e ->
    e - s + 1
  end)
  |> Enum.sum()

beacons =
  Enum.count(information, fn {{_, yb}, type} ->
    type == :beacon and yb == 2_000_000
  end)

covered - beacons

Part 2

{:ok, stream} =
  input
  |> Kino.Input.read()
  |> StringIO.open()

information =
  stream
  |> IO.binstream(:line)
  |> Stream.flat_map(&amp;Day15_4.parse_line/1)
  |> Map.new()

sensors = Enum.filter(information, &amp;match?({_, {:sensor, _}}, &amp;1))

{y, ranges} =
  0..4_000_000
  |> Stream.map(&amp;{&amp;1, Day15_4.ranges(sensors, &amp;1)})
  |> Enum.find(&amp;match?({_, [_, _]}, &amp;1))

[_..x1, x2.._] = Enum.sort(ranges)

div(x1 + x2, 2) * 4_000_000 + y