└── A星算法.py /A星算法.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | ''' 4 | 看到项目中有一个A*算法,跟图有关,挺有意思.在游戏中很常用. 5 | 6 | 7 | 所以看到网上一个python实现,现在来研究一下. 8 | 9 | 这个算法主要是跟dijikstra算法比较.他是启发型的但是保证找到最优解.速度比地理特斯拉算法要快很多!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 10 | 11 | ''' 12 | 13 | 14 | 15 | #需要进行抽象化的有:节点(属性有:x y坐标 父节点 g及h ) 地图(属性有高度 宽度 数据(数据中有可通行路径与障碍两种)) 16 | #A_star : 17 | # open_list (存放待测试的点,刚开始有start加入list,后面每一个current的相邻点(不能位于colse_list中且不是终点)要放到open_list中) 表示已经走过的点 18 | # close_list(存放已测试的点,已经当过current的点,就放到close_list中) 存放已经探测过的点,不必再进行探测 19 | # current 现在正在测试的点,要计算current周围的点的代价f 经过current后要放到close_list中 将openlist代价f最小的node当作下一个current 20 | # start_point end_point 21 | 22 | #初始化地图 openlist closelist node 23 | #将start点放入openlist中 24 | #while(未达到终点): 25 | #取出 openlist 中的点 将这个点设置为current 并放入closelist中 26 | #for node_near in(current的临近点) 27 | #if(current的临近点 node_near 不在closelist中且不为障碍): 28 | #计算 node_near 的f(f=g+h)大小 29 | # if( node_near 不在 openlist 中) #表示这个节点可以进行探索. 30 | # 将 node_near 放入 openlist,并将其父节点设置为current 然后将f值设置为计算出的f值 31 | # else if( node_near 在 openlist 中) 32 | # if(计算出的f大于在openlist中的f) 33 | # 不动作 34 | # else if(计算出的f小于等于在openlist中的f) 35 | # 将 openlist 中的 node_near 的f值更新为计算出的新的更小的f值 并将父节点设置为current 36 | #返回并继续循环 37 | 38 | 39 | 40 | # 之前的算法没有,在对角线和直线上加不同的权重,就导致最优解可能会走斜线来到达直线就能到达的目标. 41 | # 所以加上对角权重即可. 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | import sys 52 | #将地图中的点抽象化成类 53 | class Point: 54 | def __init__(self, x, y): 55 | self.x = x 56 | self.y = y 57 | 58 | 59 | 60 | 61 | # __eq__ 定义了类的等号(==)行为 62 | def __eq__(self, other): #函数重载 63 | if((self.x == other.x )and (self.y == other.y)): 64 | return 1 65 | else: 66 | return 0 67 | 68 | #通过列表实现的地图的建立 类c语言数组? 69 | class map_2d: 70 | def __init__(self,height,width): 71 | self.height = height 72 | self.width = width 73 | 74 | self.data = [[0 for i in range(width)] for j in range(height)] 75 | def map_show(self): 76 | for i in range(self.height): 77 | for j in range(self.width): 78 | print(self.data[i][j], end=' ') 79 | print("") 80 | 81 | # 设置障碍,也就是填入1. 82 | 83 | 84 | # 注意游戏地图一定要有边缘,所以最后的2b地图周围一圈必须都是1才行. 85 | # 这样obstacle 中也包含了是否超出边界的处理过程. 86 | def obstacle(self,obstacle_x,obstacle_y): 87 | self.data[obstacle_x][obstacle_y]=1 88 | 89 | 90 | # 字符6表示开始和结束点. 91 | def end_draw(self,point): 92 | self.data[point.x][point.y] = 6 93 | 94 | #A*算法的实现 95 | class A_star: 96 | # 设置node, 这些node作为地图中点的抽象,往list中放. 97 | class Node: 98 | def __init__(self, point, endpoint, g): 99 | self.point = point # 自己当前的坐标 100 | self.endpoint = endpoint # 目标结束点的坐标. 101 | self.father = None # 父节点 102 | self.g = g # g值,g值在用到的时候会重新算 103 | 104 | 105 | # 结束点到当前带点的海明距离*10 106 | self.h = (abs(endpoint.x - point.x) + abs(endpoint.y - point.y)) * 10 # 计算h值 107 | self.f = self.g + self.h # f值就是q值. 108 | 109 | #寻找临近点, 然后返回这个点抽象之后的node 110 | def search_near(self,ud,rl): # up down right left 111 | 112 | 113 | # ud,rl 都是-1 or 1表示周围的点. 114 | nearpoint = Point(self.point.x + rl, self.point.y + ud) 115 | 116 | # 下面为什么要加1 我们把权重写在这个地方. 就是后面+1,下面我修改这个为对角衰减类型 117 | # nearnode = A_star.Node(nearpoint, self.endpoint, self.g+1 ) 118 | 119 | #我修改的对角衰减类型. 120 | if 0 in [ud,rl]: 121 | 122 | nearnode = A_star.Node(nearpoint, self.endpoint, self.g+1 ) 123 | else: 124 | nearnode = A_star.Node(nearpoint, self.endpoint, self.g + 1.4) 125 | 126 | 127 | 128 | 129 | 130 | 131 | return nearnode 132 | 133 | 134 | 135 | 136 | def __init__(self,start_point,end_point,map):#需要传输到类中的,在此括号中写出 137 | self.path=[] 138 | self.close_list=[] #存放已经走过的点 139 | self.open_list=[] #存放需要尽心探索的点 140 | self.current = 0 #现在的node 141 | self.start_point=start_point 142 | self.end_point=end_point 143 | self.map = map #所在地图 144 | 145 | 146 | 147 | 148 | 149 | 150 | # 这个select 体现了贪婪,因为先计算最小的min,然后走 f值最小的那个节点. 151 | def select_current(self): 152 | min=10000000 153 | node_temp = 0 154 | 155 | # 找到open表中 f值最小的节点.吧他当作下一步应该走到的位置!并且就这么走 156 | # 在记录表中记录下这个一个步奏. 157 | for ele in self.open_list: 158 | if ele.f < min: 159 | min = ele.f 160 | node_temp = ele 161 | 162 | 163 | 164 | 165 | self.path.append(node_temp) 166 | self.open_list.remove(node_temp) 167 | self.close_list.append(node_temp) 168 | return node_temp 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | def isin_openlist(self,node): 178 | for opennode_temp in self.open_list: 179 | if opennode_temp.point == node.point: 180 | return opennode_temp 181 | return 0 182 | 183 | def isin_closelist(self,node): 184 | for closenode_temp in self.close_list: 185 | if closenode_temp.point == node.point: 186 | return 1 187 | return 0 188 | 189 | def is_obstacle(self,node): 190 | if self.map.data[node.point.x][node.point.y]==1 : 191 | return 1 192 | return 0 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | # 整个算法的核心函数就这个一个!!!!!!!!! 207 | def near_explore(self,node): 208 | 209 | 210 | 211 | # 对周围8个方块进行逐个判断,所以是8个类似的部分!!!!!!! 212 | ud = 1 213 | rl = 0 214 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 215 | if node_temp.point == end_point: 216 | return 1 # 返回1表示已经不用再搜索了,返回0表示还没到终点. 217 | elif self.isin_closelist(node_temp): # closelist表示搜索过的点. 218 | pass 219 | elif self.is_obstacle(node_temp): # 表示不能走的点,也pass掉. 220 | pass 221 | elif self.isin_openlist(node_temp) == 0: # 之前openlist 作更新. 222 | node_temp.father = node 223 | self.open_list.append(node_temp) 224 | else:# 下行是动态规划的公式核心. # 也就是node的f值更新而已. 225 | if node_temp.f < (self.isin_openlist(node_temp)).f: 226 | self.open_list.remove(self.isin_openlist(node_temp)) 227 | node_temp.father = node 228 | self.open_list.append(node_temp) 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | ud = -1 240 | rl = 0 241 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 242 | if node_temp.point == end_point: 243 | return 1 244 | elif self.isin_closelist(node_temp): 245 | pass 246 | elif self.is_obstacle(node_temp): 247 | pass 248 | elif self.isin_openlist(node_temp) == 0: 249 | node_temp.father = node 250 | self.open_list.append(node_temp) 251 | else: 252 | if node_temp.f < (self.isin_openlist(node_temp)).f: 253 | self.open_list.remove(self.isin_openlist(node_temp)) 254 | node_temp.father = node 255 | self.open_list.append(node_temp) 256 | 257 | ud = 0 258 | rl = 1 259 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 260 | if node_temp.point == end_point: 261 | return 1 262 | elif self.isin_closelist(node_temp): 263 | pass 264 | elif self.is_obstacle(node_temp): 265 | pass 266 | elif self.isin_openlist(node_temp) == 0: 267 | node_temp.father = node 268 | self.open_list.append(node_temp) 269 | else: 270 | if node_temp.f < (self.isin_openlist(node_temp)).f: 271 | self.open_list.remove(self.isin_openlist(node_temp)) 272 | node_temp.father = node 273 | self.open_list.append(node_temp) 274 | 275 | ud = 0 276 | rl = -1 277 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 278 | if node_temp.point == end_point: 279 | return 1 280 | elif self.isin_closelist(node_temp): 281 | pass 282 | elif self.is_obstacle(node_temp): 283 | pass 284 | elif self.isin_openlist(node_temp) == 0: 285 | node_temp.father = node 286 | self.open_list.append(node_temp) 287 | else: 288 | if node_temp.f < (self.isin_openlist(node_temp)).f: 289 | self.open_list.remove(self.isin_openlist(node_temp)) 290 | node_temp.father = node 291 | self.open_list.append(node_temp) 292 | 293 | ud = 1 294 | rl = 1 295 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 296 | if node_temp.point == end_point: 297 | return 1 298 | elif self.isin_closelist(node_temp): 299 | pass 300 | elif self.is_obstacle(node_temp): 301 | pass 302 | elif self.isin_openlist(node_temp) == 0: 303 | node_temp.father = node 304 | self.open_list.append(node_temp) 305 | else: 306 | if node_temp.f < (self.isin_openlist(node_temp)).f: 307 | self.open_list.remove(self.isin_openlist(node_temp)) 308 | node_temp.father = node 309 | self.open_list.append(node_temp) 310 | 311 | ud = 1 312 | rl = -1 313 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 314 | if node_temp.point == end_point: 315 | return 1 316 | elif self.isin_closelist(node_temp): 317 | pass 318 | elif self.is_obstacle(node_temp): 319 | pass 320 | elif self.isin_openlist(node_temp) == 0: 321 | node_temp.father = node 322 | self.open_list.append(node_temp) 323 | else: 324 | if node_temp.f < (self.isin_openlist(node_temp)).f: 325 | self.open_list.remove(self.isin_openlist(node_temp)) 326 | node_temp.father = node 327 | self.open_list.append(node_temp) 328 | 329 | ud = -1 330 | rl = 1 331 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 332 | if node_temp.point == end_point: 333 | return 1 334 | elif self.isin_closelist(node_temp): 335 | pass 336 | elif self.is_obstacle(node_temp): 337 | pass 338 | elif self.isin_openlist(node_temp) == 0: 339 | node_temp.father = node 340 | self.open_list.append(node_temp) 341 | else: 342 | if node_temp.f < (self.isin_openlist(node_temp)).f: 343 | self.open_list.remove(self.isin_openlist(node_temp)) 344 | node_temp.father = node 345 | self.open_list.append(node_temp) 346 | 347 | ud = -1 348 | rl = -1 349 | node_temp = node.search_near(ud,rl) #在调用另一个类的方法时(不论是子类还是在类外定义的类),都要进行实例化才能调用函数 350 | if node_temp.point == end_point: 351 | return 1 352 | elif self.isin_closelist(node_temp): 353 | pass 354 | elif self.is_obstacle(node_temp): 355 | pass 356 | elif self.isin_openlist(node_temp) == 0: 357 | node_temp.father = node 358 | self.open_list.append(node_temp) 359 | else: 360 | if node_temp.f < (self.isin_openlist(node_temp)).f: 361 | self.open_list.remove(self.isin_openlist(node_temp)) 362 | node_temp.father = node 363 | self.open_list.append(node_temp) 364 | 365 | return 0 366 | 367 | ##建图并设立障碍 368 | ss=map_2d(10,20) 369 | for i in range(10): 370 | ss.obstacle(4,i) 371 | for i in range(19): 372 | ss.obstacle(0,i+1) 373 | for i in range(9): 374 | ss.obstacle(i+1,0) 375 | for i in range(9): 376 | ss.obstacle(i+1,19) 377 | for i in range(19): 378 | ss.obstacle(9,i) 379 | ss.obstacle(8,6) 380 | ss.obstacle(6,8) 381 | ss.obstacle(6,15) 382 | ss.obstacle(9,10) 383 | start_point = Point(1,3) 384 | end_point = Point(9,2) 385 | ss.end_draw(end_point) 386 | ss.end_draw(start_point) 387 | import copy 388 | # ss2用于画最优解. 389 | ss2=copy.deepcopy(ss) # 注意这个地方因为是对象所以要重新开内存来赋值才避免冲突. 390 | #初始化设置A* 391 | a_star = A_star(start_point,end_point,ss) 392 | start_node = a_star.Node(start_point,end_point,0) 393 | a_star.open_list.append(start_node) 394 | 395 | flag=0 #到达终点的标志位 396 | m=0 #步数统计 397 | 398 | #进入循环 399 | while flag != 1 : 400 | a_star.current = a_star.select_current()#从openlist中选取一个node 401 | flag=a_star.near_explore(a_star.current)#对选中的node进行周边探索 402 | m=m+1 403 | print(m) 404 | 405 | #画出地图路径 406 | for node_path in a_star.path: 407 | ss.end_draw(node_path.point) 408 | ss.map_show() 409 | 410 | print("上面打印的地图里面的6只是表示所有探索过的位置,而不是真正最优解走的路径.") 411 | 412 | 413 | 414 | print("下面打印最优解:") 415 | 416 | 417 | 418 | endNode=a_star.open_list[-1] 419 | save=[] 420 | tmp=endNode 421 | while tmp.father!=None: 422 | ss2.end_draw(tmp.point) 423 | tmp=tmp.father 424 | ss2.map_show() --------------------------------------------------------------------------------