Yordi - A Lifelong Journey of Growth

Advent of Code 2025 - Day 8

Ah yes, there it is. The 3D-grid. I was wondering when it would reappear from the depths of the Advent of Code caves.

Prepare for battle.

Check out my full solution for day 8 at GitHub.

Part one

We are given a list of 3D-points, where each line shows the x, y, and z-coordinates of a single point:

162,817,812
57,618,57
906,360,560
...

Note that the actual input is much, much larger. Ultimately, we need to connect the first 1000 points that are closest together. When we connect two points together, they join into a single circuit. After making a 1000 connections, we will have a number of circuits, with different sizes. The result for part one will be to multiply the first three largest circuits.

Let's break the puzzle down in steps:

  1. Calculate distances between all points.
  2. Connect points into circuits a 1000 times.
  3. Calculate the result from the three largest circuits.

To calculate the distance between points in a space (of any dimension) we can use Euclidian distance. If we would have a 2D-space with two coordinates A = (3,4) and B = (7,1), we would calculate the distance by taking the square of the distances of both axes, sum those and then taking the square root (sqrt) from that sum:

sqrt((x2 − x1)^2 + (y2 − y1)^2)

For higher dimensions, such as our three dimensions, the principle is the same, but we add the third dimensions to the calculation as well. In Python, we can use the dist() function from the math library to calculate this Euclidian distance for us, for points in any dimension. If we provide this function two points in the form (x, y, z), it returns the Euclidian distance:

p1 = (162,817,812)
p2 = (57,618,57)
math.dist(p1, p2)

We can do this for all points in the input and add the results to a new array. Because we start the inner for-loop one step ahead of the outer for-loop, we make sure to not calculate the distance between the same two points twice. Each item in the resulting distances array will be of the form ((idx1, idx2), distance), where idx points to the index of a point in the original list with all points.

distances = []

for i in range(len(points)):
    for j in range(i + 1, len(points)):
        p1, p2 = points[i], points[j]
        d = dist(p1, p2)
        distances.append(((i, j), d))

After iterating over all points, we can sort the distances array on the distance (d), putting the lowest distance in front. We use an anonymous lambda function (essentially a function we program without giving it a name). Together with the sort() function, this lambda function shows on which element of an item in the distances array we want to sort. We want to sort on the distance, which is at the second position of the element:

distances.sort(key=lambda x: x[1])

Before we start iterating over the distances array and connect the first 1000 points, we initialize the circuits we're going to build. Before connecting any two points together, each point is its own circuit:

[{i} for i in range(num_points)]

Then, we will start the iteration process:

for i in range(1000):
    ((p1, p2), _) = distances[i]
    connect(circuits, p1, p2)

For each two points we encounter, we figure out if any of the two points is already contained in another circuit. If so, we add this point to the already existing circuit:

set1 = set2 = None

for s in circuits:
    if p1 in s:
        set1 = s
    if p2 in s:
        set2 = s

if set1 is not set2:
    set1.update(set2)
    circuits.remove(set2)

After we've done this a 1000 times, the circuits array contains sets of circuits. We only need to sort these by length, putting the biggest circuit at the front. Again, we use a lambda function for this, now with a key that shows we want to sort based on the length of each circuit (i.e. the number of points in each circuit):

circuits = sorted(circuits, key=lambda x: len(x), reverse=True)

Now we know that the biggest circuits are at the front of the array, we can take the first three elements and multiply them together. This is the result for part one.

len(circuits[0]) * len(circuits[1]) * len(circuits[2])

Part two

The second part wants us to continue connecting points until we are left with one big circuit, meaning all points are connected together. Most of the logic can stay the same, but we need to change the for-loop to something else. Instead of iterating over the 1000 closest points, we need to continue iterating until the number of circuits is equal to one. When we reach that, we have one big circuit left:

i = 0
p1 = p2 = -1
while len(circuits) != 1:
    ((p1, p2), _) = distances[i]
    connect(circuits, p1, p2)
    i += 1

As the result for part two is calculated by multiplying the x-coordinates of the last two points that complete the single circuit, I keep track of the points p1 and p2. When the length of circuits is equal to one, I can then take the x-coordinate (which is at the first position of a point) out of these two points and multiply them together. This is the result for part two.

points[p1][0] * points[p2][0]