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

aoc 2022 - day 15

2022/elixir/day-15.livemd

aoc 2022 - day 15

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

Part 1

  • input line이 몇개 안되고 특정 y가 주어졌으니 해당 y에 대해서만 필터링하면 쉽게 풀릴듯
input_1 = Kino.Input.textarea("")
input = Kino.Input.textarea("")
defmodule Part1 do
  def parse(input) do
    input
    |> String.split("\n")
    |> Enum.map(&parse_line/1)
  end

  defp parse_line(line) do
    ~r/Sensor at x=(?-?\d+), y=(?-?\d+): closest beacon is at x=(?-?\d+), y\=(?-?\d+)/
    |> Regex.run(line, capture: :all_but_first)
    |> Enum.map(&String.to_integer/1)
  end

  def solve(data, ty) do
    data
    |> Stream.map(fn [sx, sy, bx, by] ->
      {[sx, sy, bx, by], manht(sx, sy, bx, by)}
    end)
    |> Stream.filter(fn {[_sx, sy, _bx, _by], dist} ->
      ty >= sy - dist and ty <= sy + dist
    end)
    |> Enum.reduce(%{}, fn {[sx, sy, bx, by], dist}, acc ->
      for x <- (sx - dist)..(sx + dist),
          # y <- (sy - dist)..(sy + dist),
          y = ty,
          {x, y} != {bx, by},
          manht(x, y, sx, sy) <= dist,
          into: %{} do
        {x, true}
      end
      |> Map.merge(acc)
    end)
    |> Enum.count()
  end

  defp manht(ax, ay, bx, by), do: abs(ax - bx) + abs(ay - by)
end

input
|> Kino.Input.read()
|> Part1.parse()
# |> Enum.count()
|> Part1.solve(2_000_000)
defmodule Part2 do
  def solve(raw_data, mx) do
    data =
      raw_data
      |> Enum.map(fn [sx, sy, bx, by] ->
        {{sx, sy}, manht(sx, sy, bx, by)}
      end)

    data
    |> Stream.flat_map(fn {{sx, sy}, dist} ->
      peri({sx, sy}, dist + 1)
    end)
    |> Stream.filter(fn {x, y} ->
      x >= 0 and x <= mx and y >= 0 and y <= mx
    end)
    |> Stream.reject(fn {x, y} ->
      Enum.any?(data, &amp;in_range?({x, y}, &amp;1))
    end)
    |> Enum.uniq()
  end

  def manht(ax, ay, bx, by), do: abs(ax - bx) + abs(ay - by)

  def peri({cx, cy}, rad) do
    Stream.unfold({{cx, cy - rad}, {1, 1}}, fn
      # top
      {{x, _y}, {1, -1}} when x == cx ->
        nil

      # right
      {{x, y}, {1, 1}} when y == cy ->
        {{x, y}, {{x - 1, y + 1}, {-1, 1}}}

      # bottom
      {{x, y}, {-1, 1}} when x == cx ->
        {{x, y}, {{x - 1, y - 1}, {-1, -1}}}

      # left
      {{x, y}, {-1, -1}} when y == cy ->
        {{x, y}, {{x + 1, y - 1}, {1, -1}}}

      {{x, y}, {dx, dy}} ->
        {{x, y}, {{x + dx, y + dy}, {dx, dy}}}
    end)
  end

  defp in_range?({x, y}, {{cx, cy}, dist}) do
    manht(x, y, cx, cy) <= dist
  end
end

input
|> Kino.Input.read()
|> Part1.parse()
|> Part2.solve(4_000_000)

# |> Enum.take(1)
# |> Enum.sum()
2_889_465 * 4_000_000 + 3_040_754