Many Advent of Code problems involve the manipulation of points within a 2D space. Thus, my solutions typically include some variation of the same `move()`

and `neighbor()`

functions:

```
DIRECTION_TO_DELTA = {
"^": ( 0, -1),
"v": ( 0, 1),
">": ( 1, 0),
"<": (-1, 0),
}
def move(point, direction):
x, y = point
dx, dy = DIRECTION_TO_DELTA[direction]
return (x + dx, y + dy)
def neighbors(point):
for direction in DIRECTION_TO_DELTA:
yield move(point, direction)
print(list(neighbors(1, 2)))
```

Here, points are represented by 2-tuples, and I heavily rely on unpacking to extract the point's `x`

and `y`

values whenever I need to do any kind of computation on them. This is especially visible in the implementation shown for `move()`

. Also worth noting, the `x`

and `y`

values can be extracted using indexing too:

```
point = (2, 6)
# tuple indexing
x = point[0]
y = point[1]
# tuple unpacking
x, y = point
```

But there's another way to represent these `(x, y)`

points. Instead of using a tuple, we can take advantage of Python's built-in support for complex numbers! Real parts store the `x`

values while imaginary ones store the `y`

values. A point such as `(3, 7)`

is then represented as `3 + 7j`

instead. With this new way of representing points, extra steps for unpacking are no longer necessary, and the first code snippet above simplifies to this:

```
DIRECTION_TO_DELTA = {
"^": -1j,
"v": 1j,
">": 1,
"<": -1,
}
def move(point, direction):
return point + DIRECTION_TO_DELTA[direction]
def neighbors(point):
for direction in DIRECTION_TO_DELTA:
yield move(point, direction)
print(list(neighbors(1 + 2j)))
```

Whenever the individual `x`

or `y`

values need to be retrieved, they can be accessed using the built-in attributes for complex numbers:

```
point = 2 + 6j
x = int(point.real)
y = int(point.imag)
```

The only thing to note is that `number.real`

and `number.imag`

are both float values, so it's essential to cast them to integers if that's desired.

The use of complex numbers to represent 2D systems is elegant, so I will use them more in the future, but they're unfortunately limited to 2D spaces. For 3D spaces, 3-tuples are still the way to go. Or if you're feeling fancy, a dataclass:

```
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
z: int
def move(self, vector):
return Point(
self.x + vector.x,
self.y + vector.y,
self.z + vector.z,
)
def neighbors(self):
vectors = [
Point(x, y, z)
for x in range(-1, 2)
for y in range(-1, 2)
for z in range(-1, 2)
if x != y != z != 0
]
for vector in vectors:
yield self.move(vector)
print(list(Point(1, 2, 3).neighbors()))
```

This implementation is object-oriented, but it's easy to switch to the functional style used in the earlier code snippets. Either way, I don't know a built-in way to represent 3D spaces in Python instead of constructing my own representation. Do you?