Powered by AppSignal & Oban Pro

Day 09

2025/day09.livemd

Day 09

Mix.install([:kino_aoc, :image])

Setup

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2025", "9", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
tiles =
  puzzle_input
  |> String.split()
  |> Enum.map(fn raw ->
    raw
    |> String.split(",")
    |> Enum.map(&String.to_integer/1)
    |> List.to_tuple()
  end)

Implementation

defmodule Combinatorics do
  def combinations2(list) do
    Stream.unfold(list, fn
      [] -> nil
      [x | rest] ->
        curr = for y <- rest, do: [x, y]

        {curr, rest}
    end)
    |> Stream.flat_map(&amp; &amp;1)
  end
end
defmodule Rect do
  require Record

  Record.defrecordp(:rect, l: 0, t: 0, r: 0, b: 0)

  def new({ax, ay}, {bx, by}) do
    rect(l: min(ax, bx), r: max(ax, bx), t: min(ay, by), b: max(ay, by))
  end

  def area(rect() = r) do
    width(r) * height(r)
  end

  def intersect?(
        rect(l: al, r: ar, t: at, b: ab),
        rect(l: bl, r: br, t: bt, b: bb)
      ) do
    al < br and ar > bl and at < bb and ab > bt
  end

  def width(rect(r: r, l: l)), do: r - l + 1
  def height(rect(t: t, b: b)), do: b - t + 1

  def to_svg(rect(l: x, t: y) = r, opts \\ []) do
    ~s"""
     ~s(#{k}="#{v}") end)} />
    """
  end
end
rects =
  Combinatorics.combinations2(tiles)
  |> Stream.map(fn [a, b] -> Rect.new(a, b) end)
  |> Enum.sort()

Part 1

rects
|> Enum.max_by(&amp;Rect.area/1)
|> IO.inspect()
|> Rect.area()

Part 2

edges =
  tiles
  |> Enum.chunk_every(2, 1, tiles)
  |> Enum.map(&amp;apply(Rect, :new, &amp;1))
  |> Enum.sort()
# [{1916, 50285}, {94619, 50285}, {94619, 48466}, {1668, 48466}]
# |> Stream.flat_map(fn a ->
#  for b <- tiles do
#    Rect.new(a, b)
#  end
# end)
rects
|> Enum.reduce({0, nil}, fn r, {max, p} ->
  a = Rect.area(r)

  if a > max and not Enum.any?(edges, &amp;Rect.intersect?(r, &amp;1)) do
    {a, r}
  else
    {max, p}
  end
end)

Draw

{{min_x, _}, {max_x, _}} = Enum.min_max(tiles)
{{_, min_y}, {_, max_y}} = Enum.min_max_by(tiles, &amp;elem(&amp;1, 1))
h = max_y + min_y
w = max_x + min_x
{x, y} = hd(tiles)

p1 = {:rect, 16055, 14805, 85282, 83613}
p2 = {:rect, 5741, 50285, 94619, 67351}

svg = """

  
  
  #{Rect.to_svg(p1, stroke: :orange, fill: :transparent, "stroke-width": 200)}
  #{Rect.to_svg(p2, stroke: :yellow, fill: :transparent, "stroke-width": 200)}
  #{
    for {x, y} <- tiles do
      ~s()
    end
  }

"""

Kino.Image.new(svg, :svg)