启发式搜索 A*算法的OC 实现
前两天重新学习了下A*算法,上次学习A*算法已经是5年前了,看到网上铺天盖地的A*算法都是C、C++等等其他语言的,就是没有OC 的,所以抽空写了一份。今天太晚了就不说明A*算法的细节了,大家如果想学习的话建议大家看一下这篇博客http://blog.csdn.net/b2b160/article/details/4057781 。下面我就把代码贴出来吧,喜欢的可以拿去。代码中有些地方用到了一些自定义的类或者属性,大家可以在阅读代码的基础上进行修改,我自己做了一个小游戏用到了这个类,所以有些地方有改动。不过整体思路没有改动。代码的一些关键地方已经加了注释。祝晚安~
// // Search.m // A*算法 // // Created by 邓竹立 on 15-5-8. // Copyright (c) 2015年 GiveMeFive. All rights reserved. // #import "Search.h" #import "NodeView.h" #define kStright 10 //走直线的话 花费为10 #define kSlant 14 // 走斜线 花费为14 (1.414) #define kMaxF 100000 //最大花费 @interface Search () @property(nonatomic,strong)NSMutableArray *colsedTable; @end @implementation Search +(instancetype)sharedSearch { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance=[[self alloc] init]; }); return instance; } #pragma mark 搜索 /** * 搜索的主方法 * * @param srcNode srcNode description * @param descNode descNode description */ -(void)searchPathFrom:(NodeView*)srcNode to:(NodeView*)descNode complete:(void(^)(NodeView*)) nextPathNode { NodeView *currentNode=srcNode; __block NodeView *nextNode; //这里定义为__block 是因为在下面的代码块中要使用 NSArray *array=[self nearNodes:currentNode]; //获取到当前节点的所有可访问节点(已访问过的和不可达的去除) __block NSInteger minF=kMaxF; for (NodeView *tmpNode in array) { NSInteger tmpGx=currentNode.g+[self gxFromNode:currentNode toNode:tmpNode]; //算出从当前节点到其邻接节点的gx if (tmpNode.isInOpentable) // 判断节点是否已经再open中 { if (tmpNode.g>tmpGx)// 如果再open 中其gx 比现在的gx大的话 说明从当前节点出发到达该节点更划算 { tmpNode.g=tmpGx; tmpNode.parentNode=currentNode; } }else//如果不在open中则加入open { tmpNode.g=tmpGx; [self.openTable addObject:tmpNode]; tmpNode.inOpenTable=YES; } tmpNode.h=[self ManHWithNode:tmpNode descNode:descNode]; tmpNode.f=tmpNode.g+tmpNode.h; if (tmpNode.f<minF && tmpNode.parentNode==currentNode)//如果该节点的f 在当前节点的所有邻接节点中是最小的,并且该节点的父节点是当前节点 则把该节点当做下一个要访问的节点 { minF=tmpNode.f; nextNode=tmpNode; } } [self.openTable removeObject:currentNode]; currentNode.inOpenTable=NO; [self.colsedTable addObject:currentNode]; currentNode.inClosedTable=YES; if (nextNode==nil)//在某些情况下,以当前节点作为父节点的邻接节点都不适合作为下一个要访问的节点。 { [self.openTable enumerateObjectsUsingBlock:^(NodeView * obj, NSUInteger idx, BOOL *stop) { if (obj.f<minF) { minF=obj.f; nextNode=obj;//找到open 表中f值最小的节点作为下一个访问的节点 } }]; } if ([self.colsedTable containsObject:descNode])//抵达目标接地啊 { NodeView *node=descNode; while (node.parentNode.parentNode) { //这里的每个节点都是路径上的点 node=node.parentNode; } if (node) { nextPathNode(node); } self.openTable=nil; self.colsedTable=nil; return; }else if(self.openTable.count==0) { self.openTable=nil; self.colsedTable=nil; NSLog(@"不存在路径"); }else { currentNode=nextNode; [self searchPathFrom:currentNode to:descNode complete:^(NodeView *node) { nextPathNode(node); }];//递归搜索 } } /** * 计算两个相邻节点的gx * * @param node0 node0 description * @param node1 node1 description * * @return return value description */ -(NSInteger)gxFromNode:(NodeView*)node0 toNode:(NodeView*)node1 { NSAssert( node0 && node1, @"node 不能为空"); NSAssert([self isnNodes:node0 nearsNode:node1], @"两个节点不相邻 能计算gx"); BOOL isSlant=NO; if ((ABS( node0.indexX-node1.indexX) + ABS(node0.indexY-node1.indexY))==2) { isSlant=YES; } if (isSlant) { return kSlant; } return kStright; } /** * 求出该节点的邻接节点 去除不可达的 和已经加入到close 表的 * * @param node node description */ -(NSArray*)nearNodes:(NodeView*)node { NSArray *array=[self findNearNode:node]; NSMutableArray *mutable=[NSMutableArray array]; for (NodeView *tmpNode in array) { if (tmpNode.isAccess && !tmpNode.isInClosedTable) { if (!tmpNode.isInOpentable) { tmpNode.parentNode=node; } [mutable addObject:tmpNode]; } } return mutable.copy; } /** * 判断两个节点是否相邻 * * @param node0 node0 description * @param node1 node1 description * * @return return value description */ -(BOOL)isnNodes:(NodeView*)node0 nearsNode:(NodeView*)node1 { return [[self findNearNode:node0] containsObject:node1]; } /** * 返回某个节点周围存在的节点,不论是否已添加或者是不可达 * * @param node node description * * @return */ -(NSArray *)findNearNode:(NodeView *)node { NSMutableArray *mutable=[NSMutableArray array]; for (int i=-1; i<2; i++) { for (int j=-1; j<2; j++) { NSInteger indexX=node.indexX+i; NSInteger indexY=node.indexY+j; if (indexX<0 || indexY<0 || indexY>self.colCount-1 || indexX>self.rowCount-1 || (i==0 &&j==0)) { continue; } NodeView *nearNode=self.nodes[indexX][indexY]; [mutable addObject:nearNode]; } } return mutable.copy; } /** * 曼哈顿估算函数 * * @param node node description * @param descNode descNode description * * @return return value description */ -(NSInteger)ManHWithNode:(NodeView*)node descNode:(NodeView*)descNode { NSInteger x= descNode.indexX- node.indexX; NSInteger y= descNode.indexY- node.indexY; return (ABS(x*kStright)+ABS(y*kStright)); } -(NSMutableArray*)openTable { if (_openTable==nil) { _openTable=[NSMutableArray array]; } return _openTable; } -(NSMutableArray*)colsedTable { if (_colsedTable==nil) { _colsedTable=[NSMutableArray array]; } return _colsedTable; } @end
// // Search.h // A*算法 // // Created by 邓竹立 on 15-5-8. // Copyright (c) 2015年 GiveMeFive. All rights reserved. // #import <Foundation/Foundation.h> @class NodeView; @interface Search : NSObject @property(nonatomic,assign)NSInteger rowCount; @property(nonatomic,assign)NSInteger colCount; @property(nonatomic,strong)NSArray *nodes; @property(nonatomic,strong)NodeView *currentNode; @property(nonatomic,strong)NodeView* descNode; @property(nonatomic,strong)NSMutableArray * openTable; -(void)searchPathFrom:(NodeView*)srcNode to:(NodeView*)descNode complete:(void(^)(NodeView*)) nextPathNode; +(instancetype)sharedSearch; @end