基本A*算法python实现

本文介绍了一种使用Python实现A*算法的方法,不仅探讨了算法原理,还提供了详细注释的代码示例。通过该示例,读者可以了解如何利用A*算法进行路径寻找。

本文由恋花蝶发表于http://blog.youkuaiyun.com/lanphaday,可以在保证全文完整的前提下任意形式自由传播,但必须保留本声明,违反必究。

最近因为一个任务要用到A*算法,就用C++实现了一份。不过只是用A*来检测从A点到B点有无通路,不必输出路径,后来想把代码贴出来,但又觉得不如实现一个简单的寻路应用好一些,就用python写了一个版本贴上来。
A*算法不仅仅可以用来寻路,寻路也不仅仅使用A*算法。这是使用学习和使用A*算法最要谨记的一点吧~
A*算法用以寻路实现算不得是人工智能,他本质上是一种启发式的试探回溯算法,不过业界似乎喜欢把它称为游戏人工智能(GameAI)的一个组成部分,听起来就“豪华”得多了。A*算法需要很大的内存(相对于深度优先搜索),需要很实现比较复杂的逻辑,容易出错。
A*过程:
1.将开始节点放入开放列表(开始节点的F和G值都视为0);
2.重复一下步骤:
i.在开放列表中查找具有最小F值的节点,并把查找到的节点作为当前节点;
ii.把当前节点从开放列表删除, 加入到封闭列表;
iii.对当前节点相邻的每一个节点依次执行以下步骤:
1.如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点;
2.如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;
3.如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值.
iv.循环结束条件:
当终点节点被加入到开放列表作为待检验节点时, 表示路径被找到,此时应终止循环;
或者当开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环;
3.从终点节点开始沿父节点遍历, 并保存整个遍历到的节点坐标,遍历所得的节点就是最后得到的路径;

好了,废话不多说,看代码吧,带详尽注释,但可能存在bug~,另:本示例程序未作优化。

参考资料:
http://www.gamedev.net/reference/programming/features/astar/default.asp

http://blog.youkuaiyun.com/win32asn/archive/2006/03/17/627098.aspx

# -*-coding:utf-8-*-
import math

# 地图
tm = [
' ############################################################ ' ,
' #..........................................................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.......S.....................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #.............................#............................# ' ,
' #######.#######################################............# ' ,
' #....#........#............................................# ' ,
' #....#........#............................................# ' ,
' #....##########............................................# ' ,
' #..........................................................# ' ,
' #..........................................................# ' ,
' #..........................................................# ' ,
' #..........................................................# ' ,
' #..........................................................# ' ,
' #...............................##############.............# ' ,
' #...............................#........E...#.............# ' ,
' #...............................#............#.............# ' ,
' #...............................#............#.............# ' ,
' #...............................#............#.............# ' ,
' #...............................###########..#.............# ' ,
' #..........................................................# ' ,
' #..........................................................# ' ,
' ############################################################ ' ]

# 因为python里string不能直接改变某一元素,所以用test_map来存储搜索时的地图
test_map = []

# ########################################################
class Node_Elem:
"""
开放列表和关闭列表的元素类型,parent用来在成功的时候回溯路径
"""
def __init__ (self,parent,x,y,dist):
self.parent
= parent
self.x
= x
self.y
= y
self.dist
= dist

class A_Star:
"""
A星算法实现类
"""
# 注意w,h两个参数,如果你修改了地图,需要传入一个正确值或者修改这里的默认参数
def __init__ (self,s_x,s_y,e_x,e_y,w = 60 ,h = 30 ):
self.s_x
= s_x
self.s_y
= s_y
self.e_x
= e_x
self.e_y
= e_y

self.width
= w
self.height
= h

self.open
= []
self.close
= []
self.path
= []

# 查找路径的入口函数
def find_path(self):
# 构建开始节点
p = Node_Elem(None,self.s_x,self.s_y, 0.0 )
while True:
# 扩展F值最小的节点
self.extend_round(p)
# 如果开放列表为空,则不存在路径,返回
if not self.open:
return
# 获取F值最小的节点
idx,p = self.get_best()
# 找到路径,生成路径,返回
if self.is_target(p):
self.make_path(p)
return
# 把此节点压入关闭列表,并从开放列表里删除
self.close.append(p)
del self.open[idx]

def make_path(self,p):
# 从结束点回溯到开始点,开始点的parent==None
while p:
self.path.append((p.x,p.y))
p
= p.parent

def is_target(self,i):
return i.x == self.e_x and i.y == self.e_y

def get_best(self):
best
= None
bv
= 1000000 # 如果你修改的地图很大,可能需要修改这个值
bi = - 1
for idx,i in enumerate(self.open):
value
= self.get_dist(i) # 获取F值
if value < bv: # 比以前的更好,即F值更小
best = i
bv
= value
bi
= idx
return bi,best

def get_dist(self,i):
# F=G+H
# G为已经走过的路径长度,H为估计还要走多远
# 这个公式就是A*算法的精华了。
return i.dist + math.sqrt(
(self.e_x
- i.x) * (self.e_x - i.x)
+ (self.e_y - i.y) * (self.e_y - i.y)) * 1.2

def extend_round(self,p):
# 可以从8个方向走
xs = ( - 1 ,0, 1 , - 1 , 1 , - 1 ,0, 1 )
ys
= ( - 1 , - 1 , - 1 ,0,0, 1 , 1 , 1 )
# 只能走上下左右四个方向
#
xs=(0,-1,1,0)
#
ys=(-1,0,0,1)
for x,y in zip(xs,ys):
new_x,new_y
= x + p.x,y + p.y
# 无效或者不可行走区域,则勿略
if not self.is_valid_coord(new_x,new_y):
continue
# 构造新的节点
node = Node_Elem(p,new_x,new_y,p.dist + self.get_cost(
p.x,p.y,new_x,new_y))
# 新节点在关闭列表,则忽略
if self.node_in_close(node):
continue
i
= self.node_in_open(node)
if i != - 1 :
# 新节点在开放列表
if self.open[i].dist > node.dist:
# 现在的路径到比以前到这个节点的路径更好~
# 则使用现在的路径
self.open[i].parent = p
self.open[i].dist
= node.dist
continue
self.open.append(node)

def get_cost(self,x1,y1,x2,y2):
"""
上下左右直走,代价为1.0,斜走,代价为1.4
"""
if x1 == x2 or y1 == y2:
return 1.0
return 1.4

def node_in_close(self,node):
for i in self.close:
if node.x == i.x and node.y == i.y:
return True
return False

def node_in_open(self,node):
for i,n in enumerate(self.open):
if node.x == n.x and node.y == n.y:
return i
return - 1

def is_valid_coord(self,x,y):
if x < 0 or x >= self.width or y < 0 or y >= self.height:
return False
return test_map[y][x] != ' # '

def get_searched(self):
l
= []
for i in self.open:
l.append((i.x,i.y))
for i in self.close:
l.append((i.x,i.y))
return l

# ########################################################
def print_test_map():
"""
打印搜索后的地图
"""
for line in test_map:
print '' .join(line)

def get_start_XY():
return get_symbol_XY( ' S ' )

def get_end_XY():
return get_symbol_XY( ' E ' )

def get_symbol_XY(s):
for y,line in enumerate(test_map):
try :
x
= line.index(s)
except :
continue
else :
break
return x,y

# ########################################################
def mark_path(l):
mark_symbol(l,
' * ' )

def mark_searched(l):
mark_symbol(l,
' ' )

def mark_symbol(l,s):
for x,y in l:
test_map[y][x]
= s

def mark_start_end(s_x,s_y,e_x,e_y):
test_map[s_y][s_x]
= ' S '
test_map[e_y][e_x]
= ' E '

def tm_to_test_map():
for line in tm:
test_map.append(list(line))

def find_path():
s_x,s_y
= get_start_XY()
e_x,e_y
= get_end_XY()
a_star
= A_Star(s_x,s_y,e_x,e_y)
a_star.find_path()
searched
= a_star.get_searched()
path
= a_star.path
# 标记已搜索区域
mark_searched(searched)
# 标记路径
mark_path(path)
print " pathlengthis%d " % (len(path))
print " searchedsquarescountis%d " % (len(searched))
# 标记开始、结束点
mark_start_end(s_x,s_y,e_x,e_y)

if __name__ == " __main__ " :
# 把字符串转成列表
tm_to_test_map()
find_path()
print_test_map()
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值