赖勇浩(恋花蝶)的博客

Life is short , you need python .

用户操作
[即时聊天] [发私信] [加为好友]
赖勇浩ID:lanphaday
324291次访问,排名162好友189人,关注者230
网络游戏程序员,略懂python、C++;不太懂网络编程、数据库编程;不懂C语言、3D。
lanphaday的文章
原创 61 篇
翻译 16 篇
转载 12 篇
评论 585 篇
赖勇浩的公告
Google
最近评论
肉包:这里的博文基本上每篇必看。
肉包:期待下一篇。
roy:不要迷信权威
sheep:豆瓣是值得尊敬的公司
sheep:豆瓣是值得尊敬的公司
文章分类
收藏
    相册
    MetaWeblog API
    编程语言
    TIOBE 流行度
    性能排名
    我创建的开源项目
    VisualPyTune
    友情链接
    CP(图形学)
    DarkSpy
    一刀
    丫头
    沈公,仙人也!
    超哥之奋起版
    风干的绿叶
    存档
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 基本A*算法python实现收藏

    新一篇: 较高人工智能的人机博弈程序实现(多个算法结合)含C++源码 | 旧一篇: 以讹传讹的小故事大道理

    本文由恋花蝶发表于http://blog.csdn.net/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.csdn.net/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-11-1, 0, 1)
            ys 
    = (-1,-1,-1,  0, 0,  111)
            
    #只能走上下左右四个方向
    #
            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 "path length is %d"%(len(path))
        
    print "searched squares count is %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()
     

    发表于 @ 2006年10月11日 11:50:00|评论(loading...)|编辑

    新一篇: 较高人工智能的人机博弈程序实现(多个算法结合)含C++源码 | 旧一篇: 以讹传讹的小故事大道理

    评论

    #alula 发表于2007-01-11 00:00:26  IP: 219.134.250.*
    最近闲着,通过此次的资料,学习了A*并用C++实现了一个封装。
    自觉还挺好。欢迎批评建议:)
    #lanphaday 发表于2007-01-11 09:47:06  IP:
    谢谢alula的支持啊~
    有空常来,我去了你的blog,你很勤奋啊~向你学习~
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © 赖勇浩