我尝试编写 A* 寻路算法的代码,并使用 g 和 h 成本以正确的方式扩展它。
它看起来真的很酷,但是当我尝试使用从结束节点开始的递归找到最终路径时,在大多数情况下都是错误的。
我想知道这是 A* 还是我的代码的问题,因为当我发现其他人的算法代码时,它也有同样的问题。
我在 python 中使用 pygame 做了这个。
这是我的代码的相关部分(不包括大部分 pygame 交互):
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PURPLE = (238, 130, 238)
pygame.init()
width = 1200
hight = 900
startNode = None
endNode = None
SCREEN = pygame.display.set_mode((width, hight), pygame.DOUBLEBUF)
class node:
def __init__(self, pos, h_cost, g_cost, f_cost, Color):
self.x = pos[0]
self.y = pos[1]
self.parent = None
self.f_cost = f_cost
self.g_cost = g_cost
self.h_cost = h_cost
self.pos = pos
self.Color = Color
def TraceBack(self):
if self.parent != None:
self.parent.Color = "BLUE"
self.parent.TraceBack()
def calG_cost(self, startNode):
self.g_cost = math.sqrt((self.x - startNode.x) ** 2 + abs(self.y - startNode.y) ** 2)
def calH_cost(self,endNode):
self.h_cost = math.sqrt((self.x - endNode.x) ** 2 + abs(self.y - endNode.y) ** 2)
def calF_cost(self,endNode,startNode):
self.calG_cost(startNode)
self.calH_cost(endNode)
self.f_cost = self.g_cost + self.h_cost
grid = []
def checkIfDone(endNode):
x = endNode.x
y = endNode.y
if grid[x + 1][y].Color == "PURPLE":
return True
if grid[x + 1][y + 1].Color == "PURPLE":
return True
if grid[x + 1][y - 1].Color == "PURPLE":
return True
if grid[x - 1][y - 1].Color == "PURPLE":
return True
if grid[x - 1][y].Color == "PURPLE":
return True
if grid[x - 1][y + 1].Color == "PURPLE":
return True
if grid[x][y + 1].Color == "PURPLE":
return True
if grid[x][y - 1].Color == "PURPLE":
return True
return False
def findLowestFCost():
lowest = math.inf
lowestNode = None
for row in grid:
for node in row:
if node.Color == "GREEN" and node.f_cost < lowest:
lowest = node.f_cost
lowestNode = node
return lowestNode
def GenerateGrid():
for x in range(100):
grid.append([])
for y in range(100):
grid[x].append("")
for y in range(len(grid[0]) ):
for x in range(len(grid) ):
grid[x][y] = node((x, y), 0, 0, 0, "")
def checkFound(parent):
x = parent.x
y = parent.y
try:
if grid[x + 1][y].Color == "RED": return True
if grid[x + 1][y + 1].Color == "RED": return True
if grid[x + 1][y - 1].Color == "RED": return True
if grid[x - 1][y - 1].Color == "RED": return True
if grid[x - 1][y].Color == "RED": return True
if grid[x - 1][y + 1].Color == "RED": return True
if grid[x][y + 1].Color == "RED": return True
if grid[x][y - 1].Color == "RED": return True
except:
pass
def expandParent(parent):
x = parent.x
y = parent.y
if grid[x + 1][y].Color == "" or grid[x + 1][y].Color == "GREEN":
grid[x + 1][y].Color = "GREEN"
if grid[x + 1][y].parent == None:
grid[x + 1][y].parent = parent
elif parent.f_cost < grid[x + 1][y].parent.f_cost:
grid[x + 1][y].parent = parent
if grid[x + 1][y + 1].Color == "" or grid[x + 1][y + 1].Color == "GREEN":
grid[x + 1][y + 1].Color = "GREEN"
if grid[x + 1][y + 1].parent == None:
grid[x + 1][y + 1].parent = parent
elif parent.f_cost < grid[x + 1][y + 1].parent.f_cost:
grid[x + 1][y + 1].parent = parent
if grid[x + 1][y - 1].Color == "" or grid[x + 1][y - 1].Color == "GREEN":
grid[x + 1][y - 1].Color = "GREEN"
if grid[x + 1][y - 1].parent == None:
grid[x + 1][y - 1].parent = parent
elif parent.f_cost < grid[x + 1][y - 1].parent.f_cost:
grid[x + 1][y - 1].parent = parent
if grid[x - 1][y - 1].Color == "" or grid[x - 1][y - 1].Color == "GREEN":
grid[x - 1][y - 1].Color = "GREEN"
if grid[x - 1][y - 1].parent == None:
grid[x - 1][y - 1].parent = parent
elif parent.f_cost < grid[x - 1][y - 1].parent.f_cost:
grid[x - 1][y - 1].parent = parent
if grid[x - 1][y].Color == "" or grid[x - 1][y].Color == "GREEN":
grid[x - 1][y].Color = "GREEN"
if grid[x - 1][y].parent == None:
grid[x - 1][y].parent = parent
elif parent.f_cost < grid[x - 1][y].parent.f_cost:
grid[x - 1][y].parent = parent
if grid[x - 1][y + 1].Color == "" or grid[x - 1][y + 1].Color == "GREEN":
grid[x - 1][y + 1].Color = "GREEN"
if grid[x - 1][y + 1].parent == None:
grid[x - 1][y + 1].parent = parent
elif parent.f_cost < grid[x - 1][y + 1].parent.f_cost:
grid[x - 1][y + 1].parent = parent
if grid[x][y + 1].Color == "" or grid[x][y + 1].Color == "GREEN":
grid[x][y + 1].Color = "GREEN"
if grid[x][y + 1].parent == None:
grid[x][y + 1].parent = parent
elif parent.f_cost < grid[x][y + 1].parent.f_cost:
grid[x][y + 1].parent = parent
if grid[x][y - 1].Color == "" or grid[x][y - 1].Color == "GREEN":
grid[x][y - 1].Color = "GREEN"
if grid[x][y - 1].parent == None:
grid[x][y - 1].parent = parent
elif parent.f_cost < grid[x][y - 1].parent.f_cost:
grid[x][y - 1].parent = parent
parent.Color = "PURPLE"
红色为结束节点,蓝色为最终路径,紫色为父节点。我看不到起始节点,因为它被覆盖了。我会在某个时候解决这个问题:
在这里工作得更好:
主要问题之一是如何计算𝑔-成本:
def calG_cost(self, startNode):
self.g_cost = math.sqrt((self.x - startNode.x) ** 2 + abs(self.y - startNode.y) ** 2)
这计算了从起始节点到当前节点的距离“如乌鸦飞翔”,但是𝑔应该反映actual遍历的路径。我们通常不能仅从起始节点和当前节点知道这一点。这需要随着节点的扩大而递增累加,下一步加入𝑔成本。
另一个问题是
findLowestFCost
效率不高:它需要访问网格中的每个单元格。为此,A* 算法专为与优先级队列一起使用而设计。
expandParent
有非常相似的代码,重复了好几次。这可以用更好的方式完成。由于您已经在使用 Node
类,为什么不在 neighbors
属性中注册每个节点的邻居。这样你就可以在一个循环中迭代那个 neighbors
集合。
这就是我在以下解决方案中所做的。请注意,它是基于文本的,没有与 pygame 集成。
from heapq import heappop, heappush
DIAGDIST = 2**0.5
# Define meaning of letters
WALL = "#"
START = "y"
END = "r"
NEW = "."
QUEUED = "?"
VISITED = ","
ONPATH = "B"
class Node:
def __init__(self, pos, *neighbors):
self.x, self.y = pos
self.pos = pos
self.color = NEW
self.parent = None
self.f_cost = self.g_cost = float("inf")
# Define neighbor bi-directional relations
self.neighbors = set()
for neighbor in neighbors:
if neighbor:
self.neighbors.add(neighbor)
neighbor.neighbors.add(self)
def minDistance(self, other):
# minimum distance with legal steps when there would be no walls
dx = abs(self.x - other.x)
dy = abs(self.y - other.y)
diag = min(dx, dy)
straight = max(dx, dy) - diag
return diag * DIAGDIST + straight
# Make Node instances comparable; for use by a heap
def __lt__(self, other):
return (self.f_cost - other.f_cost or self.x - other.x or self.y - other.y) < 0
def traceBack(self):
node = self
while node:
node.color = ONPATH
node = node.parent
class Maze:
def __init__(self, pattern):
pattern = pattern.replace(" ", "") # Remove intermediate spaces
lines = pattern.strip().splitlines()
row = [None] * len(lines[0])
self.startNode = self.endNode = None
self.grid = []
for x, line in enumerate(lines):
left = None
# Create a row of Nodes that are immediately connected to previously created neighbors
# Don't create Nodes for Walls.
row = [left := Node((x, y), left, *row[max(0, y-1):y+2]) if ch != WALL else None
for y, ch in enumerate(line)]
self.grid.append(row)
if not self.startNode:
self.startNode = next((node for node, ch in zip(row, line) if ch == START), None)
if not self.endNode:
self.endNode = next((node for node, ch in zip(row, line) if ch == END), None)
def __str__(self):
return "\n".join([
" ".join(node.color if node else WALL for node in row)
for row in self.grid
])
def aStarSearch(self):
self.startNode.g_cost = 0
self.startNode.f_cost = self.startNode.minDistance(self.endNode)
heap = [self.startNode]
while heap:
node = heappop(heap)
if node == self.endNode: # found target
node.traceBack()
# Restore indications of start/end nodes
self.startNode.color = "y"
self.endNode.color = "r"
return
if node.color != VISITED:
node.color = VISITED
for neighbor in node.neighbors:
if neighbor.color != VISITED:
g_cost = node.g_cost + node.minDistance(neighbor)
if g_cost < neighbor.g_cost:
h_cost = neighbor.minDistance(self.endNode)
neighbor.g_cost = g_cost
neighbor.f_cost = g_cost + h_cost
neighbor.parent = node
neighbor.color = QUEUED
heappush(heap, neighbor)
# Example run (text-based, no pygame)
maze = Maze("""
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . # . . . . . . . . . . . . . . . . . . . . . . . .
. . . . # . . . . . . . . y . . . . . . . . . . . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # # # # # # # # # # # # # # # # # # # # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . # . . . . . . . . . . . . . . . . . . # . . . . .
. . . . . . . . . . . r . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
""")
maze.aStarSearch()
print(maze)
这输出:
. . ? ? , , , , , , , , , , , , , , , , , , ? ? . . . . .
. ? ? , , , , , , , , , , , , , , , , , , , , ? ? . . . .
? ? , , , , , , , , , , , , , , , , , , , , , , ? ? . . .
? , , , B B , , , , , , , , , , , , , , , , , , , ? ? . .
? , , B # , B , , , , , , , , , , , , , , , , , , , ? ? .
, , , B # , , B B B B B B y , , , , , , , , , , , , , ? .
, , , B # , , , , , , , , , , , , , , , , , , # , , , ? ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , , ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , , ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , , ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , , ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , , ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , ? ?
, , , B # , , , , , , , , , , , , , , , , , , # , , , ? .
, , , B # # # # # # # # # # # # # # # # # # # # , , ? ? .
, , , B # . . . . . . . . . . . . . . . . . . # , , ? . .
, , , B # . . . . . . . . . . . . . . . . . . # , , ? . .
? , , B # . . . . . . . . . . . . . . . . . . # , ? ? . .
? , , B # . . . . . . . . . . . . . . . . . . # , ? . . .
? , , B # . . . . . . . . . . . . . . . . . . # ? ? . . .
? ? , B # . . . . . . . . . . . . . . . . . . # . . . . .
. ? , B # . . . . . . . . . . . . . . . . . . # . . . . .
. ? ? B # . . . . . . . . . . . . . . . . . . # . . . . .
. . ? B # . . . . . . . . . . . . . . . . . . # . . . . .
. . ? B # ? ? ? ? ? ? ? . . . . . . . . . . . # . . . . .
. . ? ? B B B B B B B r . . . . . . . . . . . . . . . . .
. . . ? ? ? ? ? ? ? ? ? . . . . . . . . . . . . . . . . .