top of page
Search
• Andrew Jones

# Applying the A* Path Finding Algorithm in Python (Part 3: 3D coordinate pairs)

If you've read part 1 and part 2, you'll know I mentioned that I wanted to move this logic into 3 dimensional space. Well, it turns out that the re-jigging of logic we did between part 1 (using a grid) and part 2 (using coordinate pairs) means this is actually a very simple transition.

If you're interesting in playing around with this code, I definitely recommend reading the earlier parts as I'm going to skip over a lot of detail here. We're still going to be using coordinate pairs, each coordinate will now just have a third part, the z-axis.

Let's run through a really simple example again to showcase the logic.

Just like part 2, let's import the python packages we need

import numpy as np

import heapq

import pandas as pd

from collections import OrderedDict

Now we can create some data to represent the coordinate pairs in the 3x3 cube, but let's just make sure in this case that the middle cube (2,2,2) is unavailable to travel through.

We want coordinates that represent all the possible positions at which we can reside, and then the related pairs for each position, of which you can travel to. Apologies for the length of these lists, that's what happens when you add another dimension!

x1 = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]

y1 = [1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3]

z1 = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]

x2 = [1,1,2,1,2,2,1,1,1,2,2,1,1,2,2,2,1,1,2,1,2,2,1,1,2,3,3,1,1,2,3,3,1,1,1,2,2,3,3,3,1,1,1,2,2,3,3,3,1,1,2,3,3,1,1,2,3,3,2,3,3,2,2,3,2,2,3,3,3,2,2,2,3,3,2,3,3,2,2,3,1,1,2,2,1,1,2,2,1,2,1,1,1,2,2,2,1,1,1,2,2,2,1,1,2,2,1,1,2,2,1,1,2,2,1,2,1,1,2,2,3,3,1,1,2,2,3,3,1,1,3,3,1,1,2,2,3,3,1,1,2,2,3,3,1,1,3,3,2,2,3,3,2,2,3,3,2,3,2,2,2,3,3,3,2,2,2,3,3,3,2,2,3,3,2,2,3,3,2,2,3,3,2,3,1,1,2,1,2,2,1,1,1,2,2,1,1,2,2,2,1,1,2,1,2,2,1,1,2,3,3,1,1,2,3,3,1,1,1,2,2,3,3,3,1,1,1,2,2,3,3,3,1,1,2,3,3,1,1,2,3,3,2,3,3,2,2,3,2,2,3,3,3,2,2,2,3,3,2,3,3,2,2,3]

y2 = [1,2,1,2,1,2,1,2,3,1,3,1,3,1,2,3,2,3,3,2,2,3,1,2,1,1,2,1,2,2,1,2,1,2,3,1,3,1,2,3,1,2,3,1,3,1,2,3,2,3,3,2,3,2,3,2,2,3,1,1,2,1,2,2,1,3,1,2,3,1,2,3,1,3,3,2,3,2,3,2,1,2,1,2,1,2,1,2,2,1,1,2,3,1,2,3,1,2,3,1,2,3,1,3,1,3,2,3,2,3,2,3,2,3,2,3,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,1,2,1,2,1,2,1,2,1,2,1,2,3,1,2,3,1,2,3,1,2,3,1,3,1,3,2,3,2,3,2,3,2,3,3,2,1,2,1,2,1,2,1,2,3,1,3,1,3,1,2,3,2,3,3,2,2,3,1,2,1,1,2,1,2,2,1,2,1,2,3,1,3,1,2,3,1,2,3,1,3,1,2,3,2,3,3,2,3,2,3,2,2,3,1,1,2,1,2,2,1,3,1,2,3,1,2,3,1,3,3,2,3,2,3,2]

z2 = [2,2,2,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,3,3,3,3,1,1,1,1,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,3,3,3]

coord_pairs = pd.DataFrame( OrderedDict((('x1', pd.Series(x1)), ('y1', pd.Series(y1)), ('z1', pd.Series(z1)), ('x2', pd.Series(x2)), ('y2', pd.Series(y2)), ('z2', pd.Series(z2)))))

coord_pairs = coord_pairs.sort_values(['x1', 'y1', 'z1'], ascending=[True,True])

print(coord_pairs)

You'll notice in the data that the coordinate (2,2,2) doesn't exist, as it's a location that we can travel to or from.

Just like last time, let's specify our start position and our goal position...this time including the z-axis. Let's go from bottom-front to top-back

start = (1,1,1)

goal = (3,3,3)

Now we're ready to create our path-finding functions. There are only minor changes to the ones from part 2...

def available_neighbours(current_x,current_y,current_z):

return list(zip(coord_pairs.loc[(coord_pairs.x1 == current_x) & (coord_pairs.y1 == current_y) & (coord_pairs.z1 == current_z)][["x2"]].x2,

coord_pairs.loc[(coord_pairs.x1 == current_x) & (coord_pairs.y1 == current_y) & (coord_pairs.z1 == current_z)][["y2"]].y2,

coord_pairs.loc[(coord_pairs.x1 == current_x) & (coord_pairs.y1 == current_y) & (coord_pairs.z1 == current_z)][["z2"]].z2))

All we've done here is add a line of code that will also look up the z-axis when searching for pairs.

Similarly, in the heuristic function below, we've added in the third dimension so we get an accurate distance between points

def heuristic(a, b):

return np.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2 + (b[2] - a[2]) ** 2)

The changes to our astar function are also very simple, essentially just ensuring we extract the third element of our coordinates. See the additions in bold below.

def astar(start, goal):

close_set = set()

came_from = {}

gscore = {start:0}

fscore = {start:heuristic(start, goal)}

oheap = []

heapq.heappush(oheap, (fscore[start], start))

while oheap:

current = heapq.heappop(oheap)[1]

neighbours = available_neighbours(current[0],current[1],current[2])

if current == goal:

data = []

while current in came_from:

data.append(current)

current = came_from[current]

return data

for x, y, z in neighbours:

neighbour = x, y, z

tentative_g_score = gscore[current] + heuristic(current, neighbour)

if neighbour in close_set and tentative_g_score >= gscore.get(neighbour, 0):

continue

if tentative_g_score < gscore.get(neighbour, 0) or neighbour not in [i[1]for i in oheap]:

came_from[neighbour] = current

gscore[neighbour] = tentative_g_score

fscore[neighbour] = tentative_g_score + heuristic(neighbour, goal)

heapq.heappush(oheap, (fscore[neighbour], neighbour))

return False

Now, for the fun bit - running the algorithm and finding our path!

route = astar(start, goal)

route = route + [start]

route = route[::-1]

print(route)

[(1, 1, 1), (1, 1, 2), (2, 2, 3), (3, 3, 3)]

That looks pretty good, it's gone around the (2,2,2) position to reach the goal...

We can visualise it, so see the path it took (note: I'm sure there are better ways to do this, but the code below did the simple job I wanted..!)

import matplotlib as mpl

from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt

#extract x and y coordinates from route list

x_coords = []

y_coords = []

z_coords = []

for i in (range(0,len(route))):

x = route[i][0]

y = route[i][1]

z = route[i][2]

x_coords.append(x)

y_coords.append(y)

z_coords.append(z)

x_coords = np.array(x_coords)

y_coords = np.array(y_coords)

z_coords = np.array(z_coords)

fig = plt.figure(figsize=(12,12))

ax.scatter3D(goal[0],goal[1],goal[2], marker = "*", color = "red", s = 100)

ax.scatter3D(1,1,1,marker = "o", color = "black", s = 100)

ax.scatter3D(1,2,1,marker = "o", color = "black", s = 100)

ax.scatter3D(1,3,1,marker = "o", color = "black", s = 100)

ax.scatter3D(2,1,1,marker = "o", color = "black", s = 100)

ax.scatter3D(2,2,1,marker = "o", color = "black", s = 100)

ax.scatter3D(2,3,1,marker = "o", color = "black", s = 100)

ax.scatter3D(3,1,1,marker = "o", color = "black", s = 100)

ax.scatter3D(3,2,1,marker = "o", color = "black", s = 100)

ax.scatter3D(3,3,1,marker = "o", color = "black", s = 100)

ax.scatter3D(1,1,2,marker = "o", color = "black", s = 100)

ax.scatter3D(1,2,2,marker = "o", color = "black", s = 100)

ax.scatter3D(1,3,2,marker = "o", color = "black", s = 100)

ax.scatter3D(2,1,2,marker = "o", color = "black", s = 100)

ax.scatter3D(2,2,2,marker = "x", color = "black", s = 100)

ax.scatter3D(2,3,2,marker = "o", color = "black", s = 100)

ax.scatter3D(3,1,2,marker = "o", color = "black", s = 100)

ax.scatter3D(3,2,2,marker = "o", color = "black", s = 100)

ax.scatter3D(3,3,2,marker = "o", color = "black", s = 100)

ax.scatter3D(1,1,3,marker = "o", color = "black", s = 100)

ax.scatter3D(1,2,3,marker = "o", color = "black", s = 100)

ax.scatter3D(1,3,3,marker = "o", color = "black", s = 100)

ax.scatter3D(2,1,3,marker = "o", color = "black", s = 100)

ax.scatter3D(2,2,3,marker = "o", color = "black", s = 100)

ax.scatter3D(2,3,3,marker = "o", color = "black", s = 100)

ax.scatter3D(3,1,3,marker = "o", color = "black", s = 100)

ax.scatter3D(3,2,3,marker = "o", color = "black", s = 100)

#ax.scatter3D(3,3,3,marker = "o", color = "black", s = 100)

ax.plot3D(x_coords, y_coords, z_coords, color = "pink")

plt.show()

You can see I put a cross in for (2,2,2) to represent the fact we can't access that point, and a red-start showing our goal position.

I hope you get some use from these, the scenarios we've using in each part are very simple but the logic will apply to any coordinates you can find!

FULL CODE:

##############################################################################

# import packages

##############################################################################

import numpy as np

import heapq

import pandas as pd

from collections import OrderedDict

##############################################################################

# coordinate pairs

##############################################################################

x1 = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]

y1 = [1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3]

z1 = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]

x2 = [1,1,2,1,2,2,1,1,1,2,2,1,1,2,2,2,1,1,2,1,2,2,1,1,2,3,3,1,1,2,3,3,1,1,1,2,2,3,3,3,1,1,1,2,2,3,3,3,1,1,2,3,3,1,1,2,3,3,2,3,3,2,2,3,2,2,3,3,3,2,2,2,3,3,2,3,3,2,2,3,1,1,2,2,1,1,2,2,1,2,1,1,1,2,2,2,1,1,1,2,2,2,1,1,2,2,1,1,2,2,1,1,2,2,1,2,1,1,2,2,3,3,1,1,2,2,3,3,1,1,3,3,1,1,2,2,3,3,1,1,2,2,3,3,1,1,3,3,2,2,3,3,2,2,3,3,2,3,2,2,2,3,3,3,2,2,2,3,3,3,2,2,3,3,2,2,3,3,2,2,3,3,2,3,1,1,2,1,2,2,1,1,1,2,2,1,1,2,2,2,1,1,2,1,2,2,1,1,2,3,3,1,1,2,3,3,1,1,1,2,2,3,3,3,1,1,1,2,2,3,3,3,1,1,2,3,3,1,1,2,3,3,2,3,3,2,2,3,2,2,3,3,3,2,2,2,3,3,2,3,3,2,2,3]

y2 = [1,2,1,2,1,2,1,2,3,1,3,1,3,1,2,3,2,3,3,2,2,3,1,2,1,1,2,1,2,2,1,2,1,2,3,1,3,1,2,3,1,2,3,1,3,1,2,3,2,3,3,2,3,2,3,2,2,3,1,1,2,1,2,2,1,3,1,2,3,1,2,3,1,3,3,2,3,2,3,2,1,2,1,2,1,2,1,2,2,1,1,2,3,1,2,3,1,2,3,1,2,3,1,3,1,3,2,3,2,3,2,3,2,3,2,3,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,1,2,1,2,1,2,1,2,1,2,1,2,3,1,2,3,1,2,3,1,2,3,1,3,1,3,2,3,2,3,2,3,2,3,3,2,1,2,1,2,1,2,1,2,3,1,3,1,3,1,2,3,2,3,3,2,2,3,1,2,1,1,2,1,2,2,1,2,1,2,3,1,3,1,2,3,1,2,3,1,3,1,2,3,2,3,3,2,3,2,3,2,2,3,1,1,2,1,2,2,1,3,1,2,3,1,2,3,1,3,3,2,3,2,3,2]

z2 = [2,2,2,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,2,2,2,2,2,1,1,1,1,1,2,2,2,1,1,1,3,3,3,3,1,1,1,1,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,3,3,3,3,3,3,1,1,1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,3,3,3,2,2,2,2,2,3,3,3,3,3,2,2,2,3,3,3]

coord_pairs = pd.DataFrame( OrderedDict((('x1', pd.Series(x1)), ('y1', pd.Series(y1)), ('z1', pd.Series(z1)), ('x2', pd.Series(x2)), ('y2', pd.Series(y2)), ('z2', pd.Series(z2)))))

coord_pairs = coord_pairs.sort_values(['x1', 'y1', 'z1'], ascending=[True,True])

print(coord_pairs)

##############################################################################

# specify start and goal positions

##############################################################################

start = (1,1,1)

goal = (3,3,3)

##############################################################################

# a* path finding functions

##############################################################################

def available_neighbours(current_x,current_y,current_z):

return list(zip(coord_pairs.loc[(coord_pairs.x1 == current_x) & (coord_pairs.y1 == current_y) & (coord_pairs.z1 == current_z)][["x2"]].x2,

coord_pairs.loc[(coord_pairs.x1 == current_x) & (coord_pairs.y1 == current_y) & (coord_pairs.z1 == current_z)][["y2"]].y2,

coord_pairs.loc[(coord_pairs.x1 == current_x) & (coord_pairs.y1 == current_y) & (coord_pairs.z1 == current_z)][["z2"]].z2))

def heuristic(a, b):

return np.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2 + (b[2] - a[2]) ** 2)

def astar(start, goal):

close_set = set()

came_from = {}

gscore = {start:0}

fscore = {start:heuristic(start, goal)}

oheap = []

heapq.heappush(oheap, (fscore[start], start))

iter = 0

while oheap:

iter += 1

print(iter)

current = heapq.heappop(oheap)[1]

neighbours = available_neighbours(current[0],current[1],current[2])

if current == goal:

data = []

while current in came_from:

data.append(current)

current = came_from[current]

return data

for x, y, z in neighbours:

neighbour = x, y, z

tentative_g_score = gscore[current] + heuristic(current, neighbour)

if neighbour in close_set and tentative_g_score >= gscore.get(neighbour, 0):

continue

if tentative_g_score < gscore.get(neighbour, 0) or neighbour not in [i[1]for i in oheap]:

came_from[neighbour] = current

gscore[neighbour] = tentative_g_score

fscore[neighbour] = tentative_g_score + heuristic(neighbour, goal)

heapq.heappush(oheap, (fscore[neighbour], neighbour))

return False

##############################################################################

# calculate route

##############################################################################

route = astar(start, goal)

route = route + [start]

route = route[::-1]

print(route)

##############################################################################

# visualise the path

##############################################################################

import matplotlib as mpl

from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt

#extract x and y coordinates from route list

x_coords = []

y_coords = []

z_coords = []

for i in (range(0,len(route))):

x = route[i][0]

y = route[i][1]

z = route[i][2]

x_coords.append(x)

y_coords.append(y)

z_coords.append(z)

x_coords = np.array(x_coords)

y_coords = np.array(y_coords)

z_coords = np.array(z_coords)

fig = plt.figure(figsize=(12,12))

ax.scatter3D(goal[0],goal[1],goal[2], marker = "*", color = "red", s = 100)

ax.scatter3D(1,1,1,marker = "o", color = "black", s = 100)

ax.scatter3D(1,2,1,marker = "o", color = "black", s = 100)

ax.scatter3D(1,3,1,marker = "o", color = "black", s = 100)

ax.scatter3D(2,1,1,marker = "o", color = "black", s = 100)

ax.scatter3D(2,2,1,marker = "o", color = "black", s = 100)

ax.scatter3D(2,3,1,marker = "o", color = "black", s = 100)

ax.scatter3D(3,1,1,marker = "o", color = "black", s = 100)

ax.scatter3D(3,2,1,marker = "o", color = "black", s = 100)

ax.scatter3D(3,3,1,marker = "o", color = "black", s = 100)

ax.scatter3D(1,1,2,marker = "o", color = "black", s = 100)

ax.scatter3D(1,2,2,marker = "o", color = "black", s = 100)

ax.scatter3D(1,3,2,marker = "o", color = "black", s = 100)

ax.scatter3D(2,1,2,marker = "o", color = "black", s = 100)

ax.scatter3D(2,2,2,marker = "x", color = "black", s = 100)

ax.scatter3D(2,3,2,marker = "o", color = "black", s = 100)

ax.scatter3D(3,1,2,marker = "o", color = "black", s = 100)

ax.scatter3D(3,2,2,marker = "o", color = "black", s = 100)

ax.scatter3D(3,3,2,marker = "o", color = "black", s = 100)

ax.scatter3D(1,1,3,marker = "o", color = "black", s = 100)

ax.scatter3D(1,2,3,marker = "o", color = "black", s = 100)

ax.scatter3D(1,3,3,marker = "o", color = "black", s = 100)

ax.scatter3D(2,1,3,marker = "o", color = "black", s = 100)

ax.scatter3D(2,2,3,marker = "o", color = "black", s = 100)

ax.scatter3D(2,3,3,marker = "o", color = "black", s = 100)

ax.scatter3D(3,1,3,marker = "o", color = "black", s = 100)

ax.scatter3D(3,2,3,marker = "o", color = "black", s = 100)

#ax.scatter3D(3,3,3,marker = "o", color = "black", s = 100)

ax.plot3D(x_coords, y_coords, z_coords, color = "pink")

plt.show()