Contents

Advent of Code 2025 - Day 8: Playground

Day 8 was brutal. Not because the algorithm is complex - but because I kept making implementation mistakes that cost hours of debugging.

The Problem

Given 3D coordinates of junction boxes, connect them with light strings. Connect the closest pairs first, forming circuits. After making a certain number of connections, find the sizes of the three largest circuits.

This is essentially asking: perform a greedy connection strategy (always connect the two closest unconnected boxes) and track which connected components (circuits) form.

Part 1: Connect 1000 Pairs

After 1000 connections, multiply the sizes of the three largest circuits.

Mistake 1: Single Loop Instead of Double Loop

My first attempt calculated distances wrong:

1
2
3
for i in range(len(lines)):
    # Only comparing each box to the next one
    dist = math.dist(lines[i], lines[i+1])

This only compared consecutive boxes. I needed to compare every pair:

1
2
3
4
5
6
for i in range(len(lines)):
    for j in range(i + 1, len(lines)):
        cords1 = list2int(lines[i])
        cords2 = list2int(lines[j])
        dist = math.dist(cords1, cords2)
        heapq.heappush(heap, (dist, tuple(cords1), tuple(cords2)))

The heapq gives us the closest pairs in order. This is the greedy part - always connect the two closest boxes.

Mistake 2: Forgetting to Merge Circuits

This cost me hours. When connecting two boxes, there are four cases:

  1. Both in same circuit → Do nothing (already connected)
  2. Both in different circuits → Merge the two circuits
  3. One in circuit, one not → Add the solo box to the circuit
  4. Neither in circuit → Create new circuit with both

I implemented case 3 and case 4, but forgot case 2. My circuits never merged, so I never got large circuits.

The fix:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
left_junction = None
right_junction = None

# Find which circuits contain left and right boxes
for j in junctions:
    if left in j:
        left_junction = j
    if right in j:
        right_junction = j

# Case 2: Both in different circuits - merge
if left_junction != right_junction and (left_junction != None and right_junction != None):
    left_junction += right_junction
    junctions.remove(right_junction)

# Case 3a: Left in circuit, right not
if left_junction is not None and right_junction is None:
    left_junction.append(right)

# Case 3b: Right in circuit, left not
if left_junction is None and right_junction is not None:
    right_junction.append(left)

# Case 4: Neither in circuit
if left_junction is None and right_junction is None:
    junctions.append([left, right])

Mistake 3: Forgetting to Remove Merged Circuit

After merging circuits, I initially forgot junctions.remove(right_junction). This left duplicate circuits in the list, throwing off the count.

Sorting by Inner List Length

To find the three largest circuits, I needed to sort by circuit size. Lambda syntax reminder:

1
2
3
4
5
out = sorted(junctions, key=lambda item: len(item), reverse=True)

score = 1
for i in range(0, 3):
    score *= len(out[i])

First try (after fixing all bugs).

Part 2: Connect Until One Circuit

Part 2 was straightforward after Part 1’s struggle. Instead of stopping after 1000 connections, keep connecting until all boxes are in one circuit.

The only changes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Remove the fixed limit
while True:
    box = heapq.heappop(heap)
    left = tuple(box[1])
    right = tuple(box[2])
    
    # ... same circuit merging logic ...
    
    # Stop when all boxes are in one circuit
    if len(junctions[0]) == len(lines):
        break

# Multiply X coordinates of the last two boxes connected
score = left[0] * right[0]

The check len(junctions[0]) == len(lines) works because once all boxes are connected, there’s only one circuit remaining, and its size equals the total number of boxes.

Answer: multiply the X coordinates of the final connection.

What This Really Is

This is a Union-Find (Disjoint Set Union) problem disguised as circuit building.

My list-based approach works but is slower (O(n) to find which circuit contains a box). Union-Find with path compression is O(α(n)) ≈ O(1).

The Lesson

Part 1 took hours because of small bugs:

  • Wrong loop structure (single vs double)
  • Missing merge logic
  • Forgetting to remove merged circuit

Part 2 took minutes because the logic was already there.

The real lesson: when something seems conceptually simple but won’t work, step through an example by hand. I would have caught the “circuits never merge” bug immediately if I’d traced through the example instead of staring at code.

Sixteen stars total. A humbling reminder that implementation details matter.