This one felt tricky at first with lots of words and seemingly numerous combinations of how to arrange the boxes, but the solutions turned out to be fairly straightforward. It wasn’t necessary to recognize any particular trick to solving them; but I will acknowledge that using languages that have native set-based syntax likely made it easier for me to implement than if I had to write MEMBER OF, MULTISET INTERSECT, or MULTISET UNION functionality from scratch.
When measuring the distance between two junction boxes, for the purposes of sorting closest to furthest it’s not necessary to calculate the square root of the sum of the squared differences on each axis; simply using the sum of the squares is sufficient for ordering. However, the performance boost by skipping that step is negligible for either part 1 or 2.
So, when calculating the distance “d” between two points, it didn’t really matter which of these I used.
sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z)) or (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z)
In the code examples below I do fully calculate the distance with the square root, but if you’re looking for the absolute fastest method you could skip that step.
For Part 1, I join every junction box to all others. Since making a connection from A to B is the same as making the connection from B to A, I restrict the join to prevent these redundancies using “FROM data a JOIN data b ON b.seq > a.seq” thus ensuring each junction in the list is joined to one after it in the list.
For example, if there were just 3 junction boxes: 1, 2, and 3. I would join 1 to 2, 1 to 3, and 2 to 3; which would then be an exhaustive set. It would be redundant to also connect 2 to 1, 3 to 1 and 3 to 2.
Along with the join of boxes, I include the distance and then sort the pairs by those distances from closest together to furthest apart.
I use a slight difference in my list of circuits from that described in the puzzle text. It starts with circuits of 1 box each and then connects them. I only keep track of circuits of 2 or more boxes. For the purposes of solving the puzzle this difference is irrelevant in functionality, but made for more convenient coding.
As I iterate through each pair of boxes, I search through all circuits I have already built (which will be none on the first pass) looking of either or both boxes is a member of one of these circuits. If just one of the pair is part of a circuit then I add the other member to that circuit. After adding that new member, I then look through all remaining circuits until I find one that also contains that member. If I find one then I merge all members of that circuit into the first and shrink my list of circuits by one. If both members of the pair are already part of one circuit I do nothing. If I loop through all circuits and don’t find either member of the pair in any of them, then I start a new circuit of just that pair.
I repeat this process until I have processed 1000 pairs. Then I walk through my circuits keeping track of the 3 largest ones I find. I make the assumption there will always be at least 3 circuits built using the method above. If that assumption was not correct, I would dummy in 1 for each missing circuit to represent circuits of 1 box each, which, as I mentioned above, I’m not keeping track of explicitly.
In both the sample and my real data there were more than 3 circuits so I didn’t need to worry about that special case. Once I found the 3 largest, I multiply them together and get the result.
DECLARE
TYPE circuit_tab IS TABLE OF numtab;
v_circuits circuit_tab := circuit_tab();
v_connected BOOLEAN;
v_count1 PLS_INTEGER := 0;
v_count2 PLS_INTEGER := 0;
v_count3 PLS_INTEGER := 0;
c_limit CONSTANT PLS_INTEGER := 1000;
v_connections PLS_INTEGER := 0;
BEGIN
FOR i
IN (
WITH data
AS
(SELECT seq,
TO_NUMBER(REGEXP_SUBSTR(str, '[0-9]+', 1, 1), '99999') x,
TO_NUMBER(REGEXP_SUBSTR(str, '[0-9]+', 1, 2), '99999') y,
TO_NUMBER(REGEXP_SUBSTR(str, '[0-9]+', 1, 3), '99999') z
FROM advent.data2rows('advent2025-8'))
SELECT *
FROM ( SELECT a.seq aseq,
b.seq bseq,
SQRT((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z)) d
FROM data a JOIN data b ON b.seq > a.seq
ORDER BY d ASC)
)
LOOP
v_connected := FALSE;
FOR c IN 1 .. v_circuits.COUNT
WHEN NOT v_connected
LOOP
CASE
WHEN i.aseq MEMBER OF v_circuits(c) AND i.bseq NOT MEMBER OF v_circuits(c)
THEN
v_connected := TRUE;
v_connections := v_connections + 1;
v_circuits(c).EXTEND;
v_circuits(c)(v_circuits(c).COUNT) := i.bseq;
FOR j IN c + 1 .. v_circuits.COUNT
LOOP
IF v_circuits(j) MULTISET INTERSECT v_circuits(c) IS NOT EMPTY
THEN
v_circuits(c) := v_circuits(c) MULTISET UNION DISTINCT v_circuits(j);
v_circuits(j) := v_circuits(v_circuits.COUNT);
v_circuits.TRIM;
EXIT;
END IF;
END LOOP;
WHEN i.bseq MEMBER OF v_circuits(c) AND i.aseq NOT MEMBER OF v_circuits(c)
THEN
v_connected := TRUE;
v_connections := v_connections + 1;
v_circuits(c).EXTEND;
v_circuits(c)(v_circuits(c).COUNT) := i.aseq;
FOR j IN c + 1 .. v_circuits.COUNT
LOOP
IF v_circuits(j) MULTISET INTERSECT v_circuits(c) IS NOT EMPTY
THEN
v_circuits(c) := v_circuits(c) MULTISET UNION DISTINCT v_circuits(j);
v_circuits(j) := v_circuits(v_circuits.COUNT);
v_circuits.TRIM;
EXIT;
END IF;
END LOOP;
WHEN i.aseq MEMBER OF v_circuits(c) AND i.bseq MEMBER OF v_circuits(c)
THEN
v_connected := TRUE;
v_connections := v_connections + 1;
ELSE
NULL;
END CASE;
END LOOP;
IF v_connections = c_limit
THEN
EXIT;
END IF;
IF NOT v_connected
THEN
v_connections := v_connections + 1;
v_circuits.EXTEND;
v_circuits(v_circuits.COUNT) := numtab(i.aseq, i.bseq);
END IF;
IF v_connections = c_limit
THEN
EXIT;
END IF;
END LOOP;
FOR c IN 1 .. v_circuits.COUNT
LOOP
CASE
WHEN v_circuits(c).COUNT >= v_count1
THEN
v_count3 := v_count2;
v_count2 := v_count1;
v_count1 := v_circuits(c).COUNT;
WHEN v_circuits(c).COUNT >= v_count2
THEN
v_count3 := v_count2;
v_count2 := v_circuits(c).COUNT;
WHEN v_circuits(c).COUNT >= v_count3
THEN
v_count3 := v_circuits(c).COUNT;
ELSE
NULL;
END CASE;
END LOOP;
DBMS_OUTPUT.put_line(
'Circuits:'
|| v_circuits.COUNT
|| ' Largest: '
|| v_count1
|| ' '
|| v_count2
|| ' '
|| v_count3
|| ' product: '
|| (v_count1 * v_count2 * v_count3));
END;
For Part 2, I use most of the same framework but I don’t need to keep track of how many pairs I go through. Instead, I first count how many boxes there are and then I start building my circuits as before except I don’t stop at fixed number of pairs, instead I keep building until all of my circuits merge into one that contains all of the junction boxes. At that point, there is no need to continue searching as every subsequent pair will be found to already be part of that one circuit and thus add nothing. As I cycle through each pair of boxes, I keep track of the x coordinates and when I add the last pair (even if one part of the pair was already in the circuit) I multiply those two x-values to get the result.
DECLARE
c_file VARCHAR2(20) := 'advent2025-8';
TYPE circuit_tab IS TABLE OF numtab;
v_circuits circuit_tab := circuit_tab();
v_connected BOOLEAN;
v_x1 PLS_INTEGER := 0;
v_x2 PLS_INTEGER := 0;
v_junctions PLS_INTEGER;
BEGIN
SELECT COUNT(*) INTO v_junctions FROM advent.data2rows(c_file);
FOR i
IN (
WITH data
AS
(SELECT seq,
TO_NUMBER(REGEXP_SUBSTR(str, '[0-9]+', 1, 1), '99999') x,
TO_NUMBER(REGEXP_SUBSTR(str, '[0-9]+', 1, 2), '99999') y,
TO_NUMBER(REGEXP_SUBSTR(str, '[0-9]+', 1, 3), '99999') z
FROM advent.data2rows(c_file))
SELECT xx.*, ROWNUM rn
FROM ( SELECT a.seq aseq,
b.seq bseq,
a.x xa,
b.x xb,
SQRT((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z)) d
FROM data a JOIN data b ON b.seq > a.seq
ORDER BY d ASC) xx
)
LOOP
v_connected := FALSE;
FOR c IN 1 .. v_circuits.COUNT
WHEN NOT v_connected
LOOP
CASE
WHEN i.aseq MEMBER OF v_circuits(c) AND i.bseq NOT MEMBER OF v_circuits(c)
THEN
v_connected := TRUE;
v_circuits(c).EXTEND;
v_circuits(c)(v_circuits(c).COUNT) := i.bseq;
v_x1 := i.xa;
v_x2 := i.xb;
FOR j IN c + 1 .. v_circuits.COUNT
LOOP
IF v_circuits(j) MULTISET INTERSECT v_circuits(c) IS NOT EMPTY
THEN
v_circuits(c) := v_circuits(c) MULTISET UNION DISTINCT v_circuits(j);
v_circuits(j) := v_circuits(v_circuits.COUNT);
v_circuits.TRIM;
EXIT;
END IF;
END LOOP;
WHEN i.bseq MEMBER OF v_circuits(c) AND i.aseq NOT MEMBER OF v_circuits(c)
THEN
v_connected := TRUE;
v_circuits(c).EXTEND;
v_circuits(c)(v_circuits(c).COUNT) := i.aseq;
v_x1 := i.xa;
v_x2 := i.xb;
FOR j IN c + 1 .. v_circuits.COUNT
LOOP
IF v_circuits(j) MULTISET INTERSECT v_circuits(c) IS NOT EMPTY
THEN
v_circuits(c) := v_circuits(c) MULTISET UNION DISTINCT v_circuits(j);
v_circuits(j) := v_circuits(v_circuits.COUNT);
v_circuits.TRIM;
EXIT;
END IF;
END LOOP;
WHEN i.aseq MEMBER OF v_circuits(c) AND i.bseq MEMBER OF v_circuits(c)
THEN
v_connected := TRUE;
ELSE
NULL;
END CASE;
END LOOP;
IF v_circuits.COUNT > 0 AND v_circuits(1).COUNT = v_junctions
THEN
EXIT;
END IF;
IF NOT v_connected
THEN
v_circuits.EXTEND;
v_circuits(v_circuits.COUNT) := numtab(i.aseq, i.bseq);
v_x1 := i.xa;
v_x2 := i.xb;
END IF;
IF v_circuits(1).COUNT = v_junctions
THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.put_line('x1: ' || v_x1 || ' x2: ' || v_x2 || ' product: ' || (v_x1 * v_x2));
END;
This will likely be my last puzzle of the year as I take time off to spend with my family for the holidays.
Merry Christmas and Happy New Year!
Hi Sean,
Thanks a lot for all these nice posts 🙂
I really enjoy following step by step your logic and reading through all your code 🙂
Wish you and your family a Great and Happy Holidays Season,
and a New Year 2026 full of health, happiness and the joy of all the upcoming challenges 🙂
Best Regards,
Iudith Mentzel
Thank you Iudith and I wish you and your family well too.
I’m not sure how many more of the puzzles I’ll do, but I just did one this morning and will be writing up another article for it.