You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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