|
|
|
|
class Point:
|
|
|
|
|
"""
|
|
|
|
|
表示一个点
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, x, y):
|
|
|
|
|
self.x = x
|
|
|
|
|
self.y = y
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
|
if self.x == other.x and self.y == other.y:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return "x:" + str(self.x) + ",y:" + str(self.y)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return "x:" + str(self.x) + ",y:" + str(self.y)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AStar:
|
|
|
|
|
"""
|
|
|
|
|
AStar算法的Python3.x实现
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Node: # 描述AStar算法中的节点数据
|
|
|
|
|
def __init__(self, point, endPoint, g=0):
|
|
|
|
|
self.point = point # 自己的坐标
|
|
|
|
|
self.father = None # 父节点
|
|
|
|
|
self.g = g # g值,g值在用到的时候会重新算
|
|
|
|
|
self.h = (abs(endPoint.x - point.x) + abs(endPoint.y - point.y)) * 10 # 计算h值
|
|
|
|
|
|
|
|
|
|
def __init__(self, map2d, startPoint, endPoint, passTag=0, offset=1):
|
|
|
|
|
"""
|
|
|
|
|
构造AStar算法的启动条件
|
|
|
|
|
:param map2d: Array2D类型的寻路数组
|
|
|
|
|
:param startPoint: Point或二元组类型的寻路起点
|
|
|
|
|
:param endPoint: Point或二元组类型的寻路终点
|
|
|
|
|
:param passTag: int类型的可行走标记(若地图数据!=passTag即为障碍)
|
|
|
|
|
"""
|
|
|
|
|
# 开启表
|
|
|
|
|
self.openList = []
|
|
|
|
|
# 关闭表
|
|
|
|
|
self.closeList = []
|
|
|
|
|
# 寻路地图
|
|
|
|
|
self.map2d = map2d
|
|
|
|
|
# 起点终点
|
|
|
|
|
if isinstance(startPoint, Point) and isinstance(endPoint, Point):
|
|
|
|
|
self.startPoint = startPoint
|
|
|
|
|
self.endPoint = endPoint
|
|
|
|
|
else:
|
|
|
|
|
self.startPoint = Point(*startPoint)
|
|
|
|
|
self.endPoint = Point(*endPoint)
|
|
|
|
|
|
|
|
|
|
# 可行走标记
|
|
|
|
|
self.passTag = passTag
|
|
|
|
|
# 遍历周围格子的偏移量
|
|
|
|
|
self.offset = offset
|
|
|
|
|
|
|
|
|
|
def getMinNode(self):
|
|
|
|
|
"""
|
|
|
|
|
获得openlist中F值最小的节点
|
|
|
|
|
:return: Node
|
|
|
|
|
"""
|
|
|
|
|
currentNode = self.openList[0]
|
|
|
|
|
for node in self.openList:
|
|
|
|
|
if node.g + node.h < currentNode.g + currentNode.h:
|
|
|
|
|
currentNode = node
|
|
|
|
|
return currentNode
|
|
|
|
|
|
|
|
|
|
def pointInCloseList(self, point):
|
|
|
|
|
for node in self.closeList:
|
|
|
|
|
if node.point == point:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def pointInOpenList(self, point):
|
|
|
|
|
for node in self.openList:
|
|
|
|
|
if node.point == point:
|
|
|
|
|
return node
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def endPointInCloseList(self):
|
|
|
|
|
for node in self.openList:
|
|
|
|
|
if node.point == self.endPoint:
|
|
|
|
|
return node
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def searchNear(self, minF, offsetX, offsetY):
|
|
|
|
|
"""
|
|
|
|
|
搜索节点周围的点
|
|
|
|
|
:param minF:F值最小的节点
|
|
|
|
|
:param offsetX:坐标偏移量
|
|
|
|
|
:param offsetY:
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
|
|
|
|
# 越界检测
|
|
|
|
|
if minF.point.x + offsetX < 0 or minF.point.x + offsetX > self.map2d.w - 1 or minF.point.y + offsetY < 0 or minF.point.y + offsetY > self.map2d.h - 1:
|
|
|
|
|
return
|
|
|
|
|
# 如果是障碍,就忽略
|
|
|
|
|
if self.map2d[minF.point.x + offsetX][minF.point.y + offsetY] != self.passTag:
|
|
|
|
|
return
|
|
|
|
|
# 如果在关闭表中,就忽略
|
|
|
|
|
currentPoint = Point(minF.point.x + offsetX, minF.point.y + offsetY)
|
|
|
|
|
if self.pointInCloseList(currentPoint):
|
|
|
|
|
return
|
|
|
|
|
# 设置单位花费
|
|
|
|
|
if offsetX == 0 or offsetY == 0:
|
|
|
|
|
step = 10
|
|
|
|
|
else:
|
|
|
|
|
step = 14
|
|
|
|
|
# 如果不再openList中,就把它加入openlist
|
|
|
|
|
currentNode = self.pointInOpenList(currentPoint)
|
|
|
|
|
if not currentNode:
|
|
|
|
|
currentNode = AStar.Node(currentPoint, self.endPoint, g=minF.g + step)
|
|
|
|
|
currentNode.father = minF
|
|
|
|
|
self.openList.append(currentNode)
|
|
|
|
|
return
|
|
|
|
|
# 如果在openList中,判断minF到当前点的G是否更小
|
|
|
|
|
if minF.g + step < currentNode.g: # 如果更小,就重新计算g值,并且改变father
|
|
|
|
|
currentNode.g = minF.g + step
|
|
|
|
|
currentNode.father = minF
|
|
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
|
"""
|
|
|
|
|
开始寻路
|
|
|
|
|
:return: None或Point列表(路径)
|
|
|
|
|
"""
|
|
|
|
|
# 判断寻路终点是否是障碍
|
|
|
|
|
if self.map2d[self.endPoint.x][self.endPoint.y] != self.passTag:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# 1.将起点放入开启列表
|
|
|
|
|
startNode = AStar.Node(self.startPoint, self.endPoint)
|
|
|
|
|
self.openList.append(startNode)
|
|
|
|
|
# 2.主循环逻辑
|
|
|
|
|
count = 0
|
|
|
|
|
while True:
|
|
|
|
|
count += 1
|
|
|
|
|
if count > 400:
|
|
|
|
|
return None
|
|
|
|
|
# 找到F值最小的点
|
|
|
|
|
minF = self.getMinNode()
|
|
|
|
|
# 把这个点加入closeList中,并且在openList中删除它
|
|
|
|
|
self.closeList.append(minF)
|
|
|
|
|
self.openList.remove(minF)
|
|
|
|
|
# 判断这个节点的上下左右节点,八方寻路
|
|
|
|
|
self.searchNear(minF, 0, -self.offset) # 上
|
|
|
|
|
self.searchNear(minF, 0, self.offset) # 下
|
|
|
|
|
self.searchNear(minF, -self.offset, 0) # 左
|
|
|
|
|
self.searchNear(minF, self.offset, 0) # 右
|
|
|
|
|
self.searchNear(minF, -self.offset, -self.offset) # 上左
|
|
|
|
|
self.searchNear(minF, self.offset, self.offset) # 下右
|
|
|
|
|
self.searchNear(minF, -self.offset, self.offset) # 下左
|
|
|
|
|
self.searchNear(minF, self.offset, -self.offset) # 上右
|
|
|
|
|
|
|
|
|
|
# 判断是否终止
|
|
|
|
|
point = self.endPointInCloseList()
|
|
|
|
|
if point: # 如果终点在关闭表中,就返回结果
|
|
|
|
|
# print("关闭表中")
|
|
|
|
|
cPoint = point
|
|
|
|
|
pathList = []
|
|
|
|
|
while True:
|
|
|
|
|
if cPoint.father:
|
|
|
|
|
pathList.append(cPoint.point)
|
|
|
|
|
cPoint = cPoint.father
|
|
|
|
|
else:
|
|
|
|
|
return list(reversed(pathList))
|
|
|
|
|
if len(self.openList) == 0:
|
|
|
|
|
return None
|