I am a currently working on an implementation of A* pathfinding in python. I've been following a source and adapting it, but I am having an issue that I cannot resolve. The error involves pythons eq function, here it is:
Traceback (most recent call last):
File "C:/Users/Patrick/PycharmProjects/untitled/astarmaze.py", line 154, in <module>
main()
File "C:/Users/Patrick/PycharmProjects/untitled/astarmaze.py", line 149, in main
taken_path = a.astarloop()
File "C:/Users/Patrick/PycharmProjects/untitled/astarmaze.py", line 114, in astarloop
if adj == closed_adj:
File "C:/Users/Patrick/PycharmProjects/untitled/astarmaze.py", line 33, in __eq__
return self.position == other.position
File "C:/Users/Patrick/PycharmProjects/untitled/astarmaze.py", line 33, in __eq__
return self.position == other.position
AttributeError: 'list' object has no attribute 'position'
Process finished with exit code 1
and here is the code/function in question:
class Cell(object):
def __init__(self, position=None, parent=None):
self.parent = parent
self.position = position
# our g, h, f variables for f = g + h
self.g = 0
self.h = 0
self.f = 0
def __eq__(self, other):
return self.position == other.position
# def __getitem__(self, index):
# return self.position[index]
#Class for our A* pathfinding, which can take the
class Astar(object):
#initialise the pathfinding and all relevant variables by passing in the maze and its start/end
def __init__(self, maze, start, end, mazeX, mazeY):
# Initialising the open and closed lists for the cells
self.list_open = []
self.list_closed = []
self.maze = maze
self.mazeX = mazeX
self.mazeY = mazeY
self.start_cell = Cell(start, None)
self.start_cell.g = 0
self.start_cell.h = 0
self.start_cell.f = 0
self.end_cell = Cell(end, None)
self.end_cell.g = 0
self.start_cell.h = 0
self.start_cell.f = 0
def astarloop(self):
# adds the starting cell to the open list
self.list_open.append(self.start_cell)
# finding the end cell, loops until found
while len(self.list_open) > 0:
# current cell
current_cell = self.list_open[0]
current_i = 0
for i, item in enumerate(self.list_open):
if item.f < current_cell.f:
current_cell = item
current_i = i
# now it takes the current cell off the open list, and adds it to the closed list, since it has been visited
self.list_open.pop(current_i)
self.list_closed.append(current_cell)
# when the end cell is found, return an array that shows the path taken through the maze to reach the end
if current_cell == self.end_cell:
taken_path = []
current = current_cell
while current is not None:
taken_path.append(current.position)
current = current.parent
return taken_path[::-1] # return the taken path but in reverse
# Generate the children from the current cell, this is important for the algorithm to decide where to go
# based on the 'f' value - it should pick the lowest costed cell to get to the end fastest, adjecent cells to current one
adj_cells = []
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]: #adjacent squares in 8 directions
# getting the current cell position
cell_position = (current_cell.position[0] + new_position[0], current_cell.position[1] + new_position[1])
# make sure the cell pos is within the range of the maze array
if cell_position[0] > (len(self.maze) - 1) or cell_position[0] < 0 or cell_position[1] > (len(self.maze[len(self.maze)-1]) - 1) or cell_position[1] < 0:
continue
if self.maze[cell_position[0]][cell_position[1]] != 0:
continue
# new child cell to be appended to adjacent cells
new_cell = Cell(current_cell, cell_position)
adj_cells.append(new_cell)
# time to loop through the adjacent cells
for adj in adj_cells:
for closed_adj in self.list_closed:
if adj == closed_adj:
continue
# create f, g and h for the child adjacent cell
adj.g = current_cell.g + 1
adj.h = ((adj.position[0] - self.end_cell.position[0]) ** 2) + ((adj.position[1] - self.end_cell.position[1]) ** 2)
adj.f = adj.g + adj.h
# already on open list
for open_adj in self.list_open:
if adj == open_adj and adj.g > open_adj.g:
continue
# add to open
self.list_open.append(adj)
def main():
maze = [[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
start = (0, 0)
end = (7, 6)
a = Astar(maze, start, end, 0, 0)
taken_path = a.astarloop()
print(taken_path)
if __name__ == "__main__":
main()
As you can see, the eq function doesn't appear to working properly in the Cell class, and is not returning what I would like it to. I've tried changing the start and end points of the maze that are passed in from tuples to a list, but the same error persists. How can I fix my code?
This is the constructor
class Cell(object):
def __init__(self, position=None, parent=None):
self.parent = parent
self.position = position
This is where you use it
new_cell = Cell(current_cell, cell_position)
It looks like you have passed the position as the parent, and the parent as the position.
Later on, this leads to problems comparing Cell.eq(self.position, other.position) when self.position is actually a cell, but other.position is a raw tuple like you expected. So it calls eq again on self.position which tries to access the other.position.position (which doesn't exist because it's the position property of a raw tuple not of a Cell)
You can fix it with
new_cell = Cell(parent=current_cell, position=cell_position)
or by just putting the arguments in the right order.