Powered by AppSignal & Oban Pro

Day 8: Playground

2025/day08.livemd

Day 8: Playground

Mix.install([:kino])

distance = fn {x1, y1, z1}, {x2, y2, z2} ->
  :math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2)
end

Section

input = Kino.Input.textarea("input", monospace: true)
circuits = 
  input
  |> Kino.Input.read()
  |> String.split(["\n", ","], trim: true)
  |> Enum.map(&String.to_integer/1)
  |> Enum.chunk_every(3)
  |> Enum.map(&List.to_tuple/1)
  |> then(fn junctions ->
    for j1 <- junctions,
        j2 <- junctions,
        j1 < j2, 
        do: {MapSet.new([j1, j2]), distance.(j1, j2)}
  end)
  |> Enum.sort_by(&amp;elem(&amp;1, 1))
  |> Enum.map(&amp;elem(&amp;1, 0))
Stream.scan(circuits, [], fn circuit, circuits ->
  case Enum.split_with(circuits, &amp;MapSet.disjoint?(&amp;1, circuit)) do
    {disjoint_circuits, []} ->
      [circuit | disjoint_circuits]

    {disjoint_circuits, joint_circuits} ->
      [Enum.reduce(joint_circuits, circuit, &amp;MapSet.union/2) | disjoint_circuits]
  end
end)
|> Enum.at(999)
|> Enum.map(&amp;MapSet.size/1)
|> Enum.sort(:desc)
|> Enum.take(3)
|> Enum.product()
Enum.reduce_while(circuits, [], fn circuit, circuits ->
  case Enum.split_with(circuits, &amp;MapSet.disjoint?(&amp;1, circuit)) do
    {disjoint, []} ->
      [circuit | disjoint]

    {disjoint, joint} ->
      [Enum.reduce(joint, circuit, &amp;MapSet.union/2) | disjoint]
  end
  |> then(fn
    [%MapSet{map: map}] when map_size(map) == 1000 ->
      {:halt, circuit}

    circuits ->
      {:cont, circuits}
  end)
end)
|> Enum.product_by(&amp;elem(&amp;1, 0))