├── .gitignore ├── LICENSE ├── PathFindingForObjC-Example ├── Classes │ ├── PathFinding-Prefix.pch │ ├── Scene │ │ ├── GameScene.h │ │ └── GameScene.m │ ├── Sprite │ │ ├── PFGridNode.h │ │ └── PFGridNode.m │ └── Utilities │ │ ├── BFPhysicsShape.h │ │ ├── BFPhysicsShape.m │ │ ├── SKNode+WBKit.h │ │ ├── SKNode+WBKit.m │ │ ├── WBArrowShapeNode │ │ ├── WBArrowShapeNode.h │ │ └── WBArrowShapeNode.m │ │ ├── WBGameUtilities.h │ │ ├── WBGameUtilities.m │ │ ├── WBSpriteButton.h │ │ └── WBSpriteButton.m ├── OSX │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Info-mac.plist │ ├── NoHitLabel.h │ ├── NoHitLabel.m │ ├── SplitWindow.h │ ├── SplitWindow.m │ ├── StackCellViewController.h │ ├── StackCellViewController.m │ ├── StackCellViewController.xib │ └── main.m ├── PathFindingForObjC-Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcbaselines │ │ └── A53546A919FFC908005C629B.xcbaseline │ │ ├── 721B40FB-E133-44FE-B8DD-F647C48F176A.plist │ │ └── Info.plist ├── PathFindingForObjC-Example.xcworkspace │ └── contents.xcworkspacedata ├── Podfile ├── Podfile.lock ├── Resources │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Spaceship.imageset │ │ │ ├── Contents.json │ │ │ └── Spaceship.png │ ├── PathFindingData.json │ ├── arrow.png │ ├── cell_menu_bg.png │ ├── grid.png │ └── radio-res-ios │ │ ├── checked.png │ │ ├── checked@2x.png │ │ ├── unchecked.png │ │ └── unchecked@2x.png └── iOS │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── ContentViewController.h │ ├── ContentViewController.m │ ├── GameViewController.h │ ├── GameViewController.m │ ├── Info-ios.plist │ ├── LeftViewController.h │ ├── LeftViewController.m │ ├── StackTableViewCell.h │ ├── StackTableViewCell.m │ └── main.m ├── PathFindingForObjC.podspec ├── PathFindingForObjC ├── PFTypes.h ├── PathFinding.h ├── PathFinding.m ├── core │ ├── Heuristic.h │ ├── Heuristic.m │ ├── PFGrid.h │ ├── PFGrid.m │ ├── PFNode.h │ ├── PFNode.m │ ├── PFUtil.h │ └── PFUtil.m ├── fieldGrid │ ├── VectorFieldGrid.h │ └── VectorFieldGrid.m └── finders │ ├── AStarFinder.h │ ├── AStarFinder.m │ ├── BaseFinder.h │ ├── BaseFinder.m │ ├── BestFirstFinder.h │ ├── BestFirstFinder.m │ ├── BiAStarFinder.h │ ├── BiAStarFinder.m │ ├── BiBestFirstFinder.h │ ├── BiBestFirstFinder.m │ ├── BiBreadthFirstFinder.h │ ├── BiBreadthFirstFinder.m │ ├── BiDijkstraFinder.h │ ├── BiDijkstraFinder.m │ ├── BreadthFirstFinder.h │ ├── BreadthFirstFinder.m │ ├── DijkstraFinder.h │ ├── DijkstraFinder.m │ ├── IDAStarFinder.h │ ├── IDAStarFinder.m │ ├── JPFAlwaysMoveDiagonally.h │ ├── JPFAlwaysMoveDiagonally.m │ ├── JPFMoveDiagonallyIfAtMostOneObstacle.h │ ├── JPFMoveDiagonallyIfAtMostOneObstacle.m │ ├── JPFMoveDiagonallyIfNoObstacles.h │ ├── JPFMoveDiagonallyIfNoObstacles.m │ ├── JPFNeverMoveDiagonally.h │ ├── JPFNeverMoveDiagonally.m │ ├── JumpPointFinderBase.h │ └── JumpPointFinderBase.m ├── README.md └── demo ├── PathFinding-Mac.zip ├── PathFinding_ScreenShot.png ├── PathFinding_ScreenShot_iOS.png └── Screenshot_01.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 JasioWoo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/PathFinding-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PathFinding-Prefix.pch 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #ifndef PathFindingForObjC_Example_PathFinding_Prefix_pch 10 | #define PathFindingForObjC_Example_PathFinding_Prefix_pch 11 | 12 | #import 13 | 14 | #ifndef __IPHONE_8_0 15 | #warning "This project uses features only available in iOS SDK 8.0 and later." 16 | #endif 17 | 18 | #ifndef __MAC_10_10 19 | #warning "This project uses features only available in OSX 10.10 and later." 20 | #endif 21 | 22 | 23 | #ifdef __OBJC__ 24 | #import 25 | #import "WBGameUtilities.h" 26 | //设备尺寸 27 | #define APP_SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 28 | #define APP_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 29 | //debug下显示log,在release下自动屏蔽log输出 30 | #ifdef DEBUG 31 | #define NSLog(...) NSLog(__VA_ARGS__) 32 | #define DLog(...) NSLog(__VA_ARGS__) 33 | #define debugMethod() NSLog(@"%s", __func__) 34 | #else 35 | #define NSLog(...) {} 36 | #define DLog(...) /* */ 37 | #define debugMethod() /* */ 38 | #endif 39 | #endif 40 | 41 | 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Scene/GameScene.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.h 3 | // PathFindingForObjC-Example 4 | // 5 | 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @protocol PathFindingActionDelegate 13 | -(void)startAction:(id)sender withPFInfo:(NSDictionary *)info; 14 | -(void)pauseAction:(id)sender; 15 | -(void)clearAction:(id)sender; 16 | @end 17 | 18 | typedef enum { 19 | PFState_Ide = 0, 20 | PFState_finding, 21 | PFState_pause, 22 | PFState_finish 23 | } PFState; 24 | 25 | 26 | 27 | extern NSString *const PathFinding_NC_Start; 28 | extern NSString *const PathFinding_NC_Finish; 29 | extern NSString *const PathFinding_NC_Result; 30 | 31 | @interface GameScene : SKScene 32 | 33 | @property (nonatomic, assign) CGSize gridSize; 34 | @property (nonatomic, assign) PFState pfState; 35 | @property (nonatomic, assign) NSUInteger trackSpeed; 36 | 37 | 38 | - (void)startFindingPath:(NSDictionary *)pfInfo; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Sprite/PFGridNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFGridNode.h 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/29. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | kGState_None = 0, 13 | kGState_Walkable, 14 | kGState_Start, 15 | kGState_End, 16 | kGState_Block, 17 | kGState_Open, 18 | kGState_Close, 19 | kGState_Tested 20 | } GridNodeState; 21 | 22 | @interface PFGridNode : SKSpriteNode 23 | 24 | @property (nonatomic) BOOL showWeightValue; 25 | @property (nonatomic) GridNodeState editState; // ( kGState_Walkable / kGState_Start / kGState_End / kGState_Block ) 26 | @property (nonatomic) GridNodeState searchState; // ( kGState_None / kGState_Open / kGState_Close / kGState_Tested ) 27 | @property (nonatomic) CGFloat colorFactor; 28 | 29 | @property (nonatomic) CGFloat fValue; 30 | @property (nonatomic) CGFloat gValue; 31 | @property (nonatomic) CGFloat hValue; 32 | @property (nonatomic) CGFloat costValue; 33 | @property (nonatomic) int direction; 34 | 35 | @property (nonatomic) int x; 36 | @property (nonatomic) int y; 37 | 38 | @property (nonatomic, strong) SKColor *walkableColor; 39 | @property (nonatomic, strong) SKColor *startColor; 40 | @property (nonatomic, strong) SKColor *endColor; 41 | @property (nonatomic, strong) SKColor *blockColor; 42 | @property (nonatomic, strong) SKColor *openColor; 43 | @property (nonatomic, strong) SKColor *closeColor; 44 | @property (nonatomic, strong) SKColor *testedColor; 45 | 46 | - (BOOL)setupEditGridState:(GridNodeState)editState runAnimate:(BOOL)animate; 47 | 48 | - (void)setDirection:(int)direction vector:(CGVector)vec; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Sprite/PFGridNode.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFGridNode.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/29. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "PFGridNode.h" 10 | 11 | #define OFFSET (self.size.width*0.05) 12 | 13 | @interface PFGridNode() 14 | @property (nonatomic, retain) SKLabelNode *fLab; 15 | @property (nonatomic, retain) SKLabelNode *gLab; 16 | @property (nonatomic, retain) SKLabelNode *hLab; 17 | @property (nonatomic, retain) SKSpriteNode *arrow; 18 | 19 | @end 20 | 21 | @implementation PFGridNode { 22 | 23 | } 24 | 25 | - (instancetype)initWithTexture:(SKTexture *)texture { 26 | self = [super initWithTexture:texture]; 27 | if (self) { 28 | _direction = 0; 29 | self.searchState = kGState_None; 30 | self.editState = kGState_Walkable; 31 | 32 | self.walkableColor = [SKColor colorWithRed:1 green:1 blue:1 alpha:0]; 33 | self.startColor = [SKColor colorWithRed:25/255.0 green:117/255.0 blue:248/255.0 alpha:1]; 34 | self.endColor = [SKColor colorWithRed:226/255.0 green:43/255.0 blue:0 alpha:1]; 35 | self.blockColor = [SKColor colorWithRed:50/255.0 green:50/255.0 blue:50/255.0 alpha:1]; 36 | self.openColor = [SKColor colorWithRed:145/255.0 green:254/255.0 blue:129/255.0 alpha:1]; 37 | self.closeColor = [SKColor colorWithRed:165/255.0 green:235/255.0 blue:234/255.0 alpha:1]; 38 | self.testedColor = [SKColor colorWithRed:190/255.0 green:190/255.0 blue:190/255.0 alpha:1]; 39 | } 40 | return self; 41 | } 42 | 43 | //static int COLOR_MAX = 222235247; 44 | //static int COLOR_MIN = 49130189; 45 | //static int COLOR_DIF = COLOR_MAX-COLOR_MIN; 46 | //- (SKColor*)getColorBrewerRange:(CGFloat)rate { 47 | // 48 | // 49 | //} 50 | 51 | - (SKSpriteNode *)arrow { 52 | if (!_arrow) { 53 | _arrow = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:@"arrow"] size:self.size]; 54 | _arrow.texture.filteringMode = SKTextureFilteringNearest; 55 | _arrow.userInteractionEnabled = NO; 56 | [self addChild:_arrow]; 57 | } 58 | return _arrow; 59 | } 60 | 61 | 62 | -(SKLabelNode *)fLab { 63 | if (!_fLab) { 64 | SKLabelNode *lab = [SKLabelNode labelNodeWithFontNamed:@"Avenir-LightOblique"]; 65 | lab.text = @"0"; 66 | lab.fontColor = [SKColor blackColor]; 67 | lab.fontSize = self.size.width*0.3; 68 | lab.hidden = YES; 69 | lab.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; 70 | lab.position = CGPointMake(OFFSET-self.size.width/2.0, self.size.height-CGRectGetHeight(lab.frame)-OFFSET-self.size.height/2.0); 71 | [self addChild:lab]; 72 | _fLab = lab; 73 | } 74 | return _fLab; 75 | } 76 | 77 | - (SKLabelNode *)gLab { 78 | if (!_gLab) { 79 | SKLabelNode *lab = [SKLabelNode labelNodeWithFontNamed:@"Avenir-LightOblique"]; 80 | lab.text = @"0"; 81 | lab.fontColor = [SKColor blackColor]; 82 | lab.fontSize = self.size.width*0.3; 83 | lab.hidden = YES; 84 | lab.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; 85 | lab.position = CGPointMake(OFFSET-self.size.width/2.0, CGRectGetHeight(lab.frame)+OFFSET-self.size.height/2.0); 86 | [self addChild:lab]; 87 | _gLab = lab; 88 | } 89 | return _gLab; 90 | } 91 | 92 | -(SKLabelNode *)hLab { 93 | if (!_hLab) { 94 | SKLabelNode *lab = [SKLabelNode labelNodeWithFontNamed:@"Avenir-LightOblique"]; 95 | lab.text = @"0"; 96 | lab.fontColor = [SKColor blackColor]; 97 | lab.fontSize = self.size.width*0.3; 98 | lab.hidden = YES; 99 | lab.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; 100 | lab.position = CGPointMake(OFFSET-self.size.width/2.0, OFFSET-self.size.height/2.0); 101 | [self addChild:lab]; 102 | _hLab = lab; 103 | } 104 | return _hLab; 105 | } 106 | 107 | - (void)setShowWeightValue:(BOOL)showWeightValue { 108 | if (_showWeightValue != showWeightValue) { 109 | _showWeightValue = showWeightValue; 110 | if (showWeightValue) { 111 | self.fLab.text = [NSString stringWithFormat:@"%d", (int)(_fValue*10)]; 112 | self.gLab.text = [NSString stringWithFormat:@"%d", (int)(_gValue*10)]; 113 | self.hLab.text = [NSString stringWithFormat:@"%d", (int)(_hValue*10)]; 114 | } 115 | 116 | if (_searchState==kGState_Close || _searchState==kGState_Open) { 117 | self.fLab.hidden = !showWeightValue; 118 | self.gLab.hidden = !showWeightValue; 119 | self.hLab.hidden = !showWeightValue; 120 | } else { 121 | self.fLab.hidden = YES; 122 | self.gLab.hidden = YES; 123 | self.hLab.hidden = YES; 124 | } 125 | } 126 | } 127 | 128 | - (void)setFValue:(CGFloat)fValue { 129 | _fValue = fValue; 130 | self.fLab.text = [NSString stringWithFormat:@"%d", (int)(_fValue*10)]; 131 | } 132 | - (void)setGValue:(CGFloat)gValue { 133 | _gValue = gValue; 134 | self.gLab.text = [NSString stringWithFormat:@"%d", (int)(_gValue*10)]; 135 | } 136 | - (void)setHValue:(CGFloat)hValue { 137 | _hValue = hValue; 138 | self.hLab.text = [NSString stringWithFormat:@"%d", (int)(_hValue*10)]; 139 | } 140 | 141 | -(void)setCostValue:(CGFloat)costValue { 142 | _costValue = costValue; 143 | if (costValue==0) { 144 | return; 145 | } 146 | _searchState = kGState_Close; 147 | self.fLab.text = [NSString stringWithFormat:@"%d", (int)costValue]; 148 | self.fLab.hidden = !_showWeightValue; 149 | 150 | [self removeAllActions]; 151 | costValue = MIN(costValue*20, 255); 152 | SKColor *color = [SKColor colorWithRed:costValue/255.0 green:117/255.0 blue:248/255.0 alpha:1];; 153 | 154 | SKAction *colorAct = [SKAction colorizeWithColor:color colorBlendFactor:1 duration:0.1]; 155 | SKAction *scaleAct = [SKAction sequence:@[[SKAction scaleTo:1.1 duration:0.1], [SKAction scaleTo:1 duration:0.1]]]; 156 | [self runAction:[SKAction group:@[colorAct, scaleAct]]]; 157 | 158 | 159 | } 160 | 161 | - (void)setColorFactor:(CGFloat)colorFactor { 162 | _colorFactor = colorFactor; 163 | self.colorBlendFactor = colorFactor; 164 | } 165 | 166 | 167 | - (void)updateGridColor:(GridNodeState)state runAnimate:(BOOL)animate { 168 | BOOL showWeightValue = NO; 169 | if (_searchState==kGState_Close || _searchState==kGState_Open) { 170 | showWeightValue = _showWeightValue; 171 | } 172 | 173 | SKColor *color; 174 | if (state==kGState_Walkable) { 175 | color = self.walkableColor; 176 | 177 | } else if (state==kGState_Start) { 178 | color = self.startColor; 179 | 180 | } else if (state==kGState_End) { 181 | color = self.endColor; 182 | 183 | } else if (state==kGState_Block) { 184 | showWeightValue = NO; 185 | color = self.blockColor; 186 | 187 | } else if (state==kGState_Open) { 188 | color = self.openColor; 189 | 190 | } else if (state==kGState_Close) { 191 | color = self.closeColor; 192 | 193 | } else if (state==kGState_Tested) { 194 | color = self.testedColor; 195 | 196 | } else { 197 | color = self.walkableColor; 198 | } 199 | 200 | self.fLab.hidden = !showWeightValue; 201 | self.gLab.hidden = !showWeightValue; 202 | self.hLab.hidden = !showWeightValue; 203 | [self removeAllActions]; 204 | if (animate) { 205 | SKAction *colorAct = [SKAction colorizeWithColor:color colorBlendFactor:self.colorFactor duration:0.1]; 206 | SKAction *scaleAct = [SKAction sequence:@[[SKAction scaleTo:1.1 duration:0.1], [SKAction scaleTo:1 duration:0.1]]]; 207 | [self runAction:[SKAction group:@[colorAct, scaleAct]]]; 208 | } else { 209 | self.color = color; 210 | self.colorBlendFactor = self.colorFactor; 211 | [self runAction:[SKAction scaleTo:1 duration:0]]; 212 | } 213 | } 214 | 215 | - (void)setSearchState:(GridNodeState)searchState{ 216 | if (_searchState!=searchState) { 217 | _searchState = searchState; 218 | if (_editState==kGState_Walkable) { 219 | [self updateGridColor:searchState runAnimate:YES]; 220 | } 221 | } 222 | } 223 | 224 | 225 | - (BOOL)setupEditGridState:(GridNodeState)editState runAnimate:(BOOL)animate { 226 | if (_editState != editState) { 227 | if (_editState!=kGState_Walkable) { 228 | if (_editState==kGState_Block && editState==kGState_Walkable) { 229 | 230 | } else { 231 | return NO; 232 | } 233 | } 234 | 235 | _editState = editState; 236 | if (editState==kGState_Walkable) { 237 | [self updateGridColor:_searchState runAnimate:animate]; 238 | 239 | } else if (editState==kGState_Start) { 240 | [self updateGridColor:editState runAnimate:animate]; 241 | 242 | } else if (editState==kGState_End) { 243 | [self updateGridColor:editState runAnimate:animate]; 244 | 245 | } else if (editState==kGState_Block) { 246 | [self updateGridColor:editState runAnimate:animate]; 247 | } 248 | return YES; 249 | } 250 | return NO; 251 | } 252 | 253 | 254 | 255 | - (void)setDirection:(int)direction vector:(CGVector)vec { 256 | 257 | 258 | if (vec.dx==0 && vec.dy==0) { 259 | self.arrow.hidden = YES; 260 | } else { 261 | CGFloat ang = WB_POLAR_ADJUST(WB_RadiansBetweenPoints(CGPointMake(vec.dx, vec.dy), CGPointMake(0, 0))); 262 | self.arrow.hidden = NO; 263 | self.arrow.zRotation = ang; 264 | } 265 | self.gLab.text = [NSString stringWithFormat:@"%d", (int)vec.dx]; 266 | self.gLab.hidden = !_showWeightValue; 267 | self.hLab.text = [NSString stringWithFormat:@"%d", (int)vec.dy]; 268 | self.hLab.hidden = !_showWeightValue; 269 | 270 | 271 | // _direction = direction; 272 | // if (direction==0) { 273 | // self.arrow.hidden = YES; 274 | // } else if (direction==2) { 275 | // self.arrow.hidden = NO; 276 | // self.arrow.zRotation = M_PI; 277 | // } else if (direction==4) { 278 | // self.arrow.hidden = NO; 279 | // self.arrow.zRotation = M_PI/2; 280 | // } else if (direction==6) { 281 | // self.arrow.hidden = NO; 282 | // self.arrow.zRotation = 0; 283 | // } else if (direction==8) { 284 | // self.arrow.hidden = NO; 285 | // self.arrow.zRotation = -M_PI/2; 286 | // } 287 | 288 | 289 | 290 | } 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | @end 316 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/BFPhysicsShape.h: -------------------------------------------------------------------------------- 1 | // 2 | // BFPhysicsShape.h 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/10/18. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BFPhysicsShape : NSObject 12 | 13 | + (instancetype)sharedInstance; 14 | 15 | - (SKTexture *)getPhysicsShapeTXAtIndex:(int)index; 16 | - (SKPhysicsBody *)getPhysicsBodyAtIndex:(int)index; 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/BFPhysicsShape.m: -------------------------------------------------------------------------------- 1 | // 2 | // BFPhysicsShape.m 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/10/18. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "BFPhysicsShape.h" 10 | #import "WBGameUtilities.h" 11 | 12 | #define kPhysicsShapeFrames 2 13 | 14 | @interface BFPhysicsShape () 15 | 16 | @property (nonatomic, strong) NSArray *physicsShapeFrames; 17 | 18 | @end 19 | 20 | 21 | @implementation BFPhysicsShape 22 | 23 | + (BFPhysicsShape *)sharedInstance { 24 | static BFPhysicsShape *sharedInstanceObj = nil; 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | if (sharedInstanceObj == nil) 28 | sharedInstanceObj = [[self alloc] init]; 29 | }); 30 | 31 | return sharedInstanceObj; 32 | } 33 | 34 | - (id)init { 35 | if ((self = [super init])) { 36 | // init something 37 | (void)self.physicsShapeFrames; 38 | 39 | } 40 | return self; 41 | } 42 | 43 | 44 | - (NSArray *)physicsShapeFrames { 45 | if (!_physicsShapeFrames) { 46 | _physicsShapeFrames = WB_LoadFramesFromAtlas(@"PhysicsTX", @"shape", kPhysicsShapeFrames); 47 | } 48 | return _physicsShapeFrames; 49 | } 50 | 51 | 52 | 53 | - (SKPhysicsBody *)getPhysicsBodyAtIndex:(int)index { 54 | SKTexture *texture = [self getPhysicsShapeTXAtIndex:index]; 55 | return [SKPhysicsBody bodyWithTexture:texture size:texture.size]; 56 | } 57 | 58 | - (SKTexture *)getPhysicsShapeTXAtIndex:(int)index { 59 | SKTexture *texture = self.physicsShapeFrames[index]; 60 | return texture; 61 | } 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/SKNode+WBKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // SKNode+WBKit.h 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/10/15. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SKNode (WBKit) 12 | 13 | - (void)runAction:(SKAction *)action withKey:(NSString *)key completion:(dispatch_block_t)block; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/SKNode+WBKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // SKNode+WBKit.m 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/10/15. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "SKNode+WBKit.h" 10 | 11 | @implementation SKNode (WBKit) 12 | 13 | - (void)runAction:(SKAction *)action withKey:(NSString *)key completion:(dispatch_block_t)block { 14 | SKAction *completionAct = [SKAction runBlock:block]; 15 | [self runAction:[SKAction sequence:@[action, completionAct]] withKey:key]; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/WBArrowShapeNode/WBArrowShapeNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBArrowShapeNode.h 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/9/19. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface WBArrowShapeNode : SKShapeNode 13 | 14 | + (id)arrowNodeWithStartPoint:(CGPoint)startPoint 15 | endPoint:(CGPoint)endPoint 16 | headWidth:(CGFloat)headWidth 17 | headLength:(CGFloat)headLength 18 | tailWidth:(CGFloat)tailWidth 19 | color:(SKColor *)color; 20 | 21 | - (id)initWithStartPoint:(CGPoint)startPoint 22 | endPoint:(CGPoint)endPoint 23 | headWidth:(CGFloat)headWidth 24 | headLength:(CGFloat)headLength 25 | tailWidth:(CGFloat)tailWidth 26 | color:(SKColor *)color; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/WBArrowShapeNode/WBArrowShapeNode.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBArrowShapeNode.m 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/9/19. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "WBArrowShapeNode.h" 10 | 11 | @implementation WBArrowShapeNode 12 | 13 | #pragma mark - init 14 | + (id)arrowNodeWithStartPoint:(CGPoint)startPoint 15 | endPoint:(CGPoint)endPoint 16 | headWidth:(CGFloat)headWidth 17 | headLength:(CGFloat)headLength 18 | tailWidth:(CGFloat)tailWidth 19 | color:(SKColor *)color 20 | { 21 | return [[WBArrowShapeNode alloc] initWithStartPoint:startPoint endPoint:endPoint headWidth:headWidth headLength:headLength tailWidth:tailWidth color:color]; 22 | } 23 | 24 | 25 | - (id)initWithStartPoint:(CGPoint)startPoint 26 | endPoint:(CGPoint)endPoint 27 | headWidth:(CGFloat)headWidth 28 | headLength:(CGFloat)headLength 29 | tailWidth:(CGFloat)tailWidth 30 | color:(SKColor *)color 31 | { 32 | self = [super init]; 33 | if (self) { 34 | // NSLog(@"startPoint=%@", NSStringFromCGPoint(startPoint)); 35 | // NSLog(@"endPoint=%@", NSStringFromCGPoint(endPoint)); 36 | 37 | CGPathRef pathRef = WBCreateArrowShapePath(startPoint, endPoint, 38 | headWidth, headLength, 39 | tailWidth); 40 | self.fillColor = color; 41 | self.path = pathRef; 42 | CGPathRelease(pathRef); 43 | } 44 | return self; 45 | } 46 | 47 | 48 | 49 | #pragma mark - C Method 50 | void WBAxisAlignedArrowPoints(CGPoint *points, 51 | CGFloat length, 52 | CGFloat headWidth, 53 | CGFloat headLength, 54 | CGFloat tailWidth) { 55 | headWidth *= 0.5; 56 | tailWidth *= 0.5; 57 | points[0] = CGPointMake(0.0, -tailWidth); 58 | points[1] = CGPointMake(length - headLength, -tailWidth); 59 | points[2] = CGPointMake(length - headLength, -headWidth); 60 | points[3] = CGPointMake(length, 0.0); 61 | points[4] = CGPointMake(length - headLength, headWidth); 62 | points[5] = CGPointMake(length - headLength, tailWidth); 63 | points[6] = CGPointMake(0.0, tailWidth); 64 | } 65 | 66 | CGPathRef WBCreateArrowShapePath(CGPoint startPoint, 67 | CGPoint endPoint, 68 | CGFloat headWidth, 69 | CGFloat headLength, 70 | CGFloat tailWidth) { 71 | CGFloat length = hypot(endPoint.x - startPoint.x, endPoint.y - startPoint.y); 72 | CGPoint points[7]; 73 | WBAxisAlignedArrowPoints(points, length, headWidth, headLength, tailWidth); 74 | 75 | CGFloat angle = atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x); 76 | CGAffineTransform transform = CGAffineTransformMakeRotation(angle); 77 | 78 | // CGFloat cosine = (endPoint.x - startPoint.x) / length; 79 | // CGFloat sine = (endPoint.y - startPoint.y) / length; 80 | // CGAffineTransform transform = (CGAffineTransform){ cosine, sine, -sine, cosine, startPoint.x, startPoint.y }; 81 | // CGAffineTransform transform = (CGAffineTransform){ cosine, sine, -sine, cosine, 0, 0 }; 82 | 83 | CGMutablePathRef pathRef = CGPathCreateMutable(); 84 | CGPathAddLines(pathRef, &transform, points, sizeof(points)/sizeof(*points)); 85 | CGPathCloseSubpath(pathRef); 86 | return pathRef; 87 | 88 | } 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/WBGameUtilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBGameUtilities.h 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/9/23. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | 10 | #import "SKNode+WBKit.h" 11 | 12 | #if TARGET_OS_IPHONE 13 | 14 | #else 15 | //iOS SDK NSStringFromCG To OSX SDK 16 | #define NSStringFromCGPoint(x) NSStringFromPoint(NSPointFromCGPoint(x)) 17 | #define NSStringFromCGSize(x) NSStringFromSize(NSSizeFromCGSize(x)) 18 | #define NSStringFromCGRect(x) NSStringFromRect(NSRectFromCGRect(x)) 19 | #endif 20 | 21 | 22 | 23 | 24 | /* The assets are all facing Y down, so offset by pi half to get into X right facing. */ 25 | #define WB_POLAR_ADJUST(x) x+(M_PI*0.5f) 26 | 27 | /// 两点间的距离 28 | CGFloat WB_DistanceBetweenPoints(CGPoint first, CGPoint second); 29 | /// 两点直线相对的弧度 30 | CGFloat WB_RadiansBetweenPoints(CGPoint first, CGPoint second); 31 | /// 两点相加 32 | CGPoint WB_PointByAddingCGPoints(CGPoint first, CGPoint second); 33 | 34 | /// YES: x和y有相同的符号 NO: x,y有相反的符号 35 | BOOL WB_ISSameSign(NSInteger x, NSInteger y); 36 | 37 | /* Load the named frames in a texture atlas into an array of frames. */ 38 | NSArray *WB_LoadFramesFromAtlas(NSString *atlasName, NSString *baseFileName, int numberOfFrames); 39 | 40 | 41 | /* Category on SKEmitterNode to make it easy to load an emitter from a node file created by Xcode. */ 42 | @interface SKEmitterNode (WBBalloonFightAdditions) 43 | + (instancetype)wbbf_emitterNodeWithEmitterNamed:(NSString *)emitterFileName; 44 | @end 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/WBGameUtilities.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBGameUtilities.m 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/9/23. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | 10 | #pragma mark - Point Calculations 11 | /// 两点间的距离 12 | CGFloat WB_DistanceBetweenPoints(CGPoint first, CGPoint second) { 13 | return hypotf(second.x - first.x, second.y - first.y); 14 | } 15 | /// 两点直线相对的弧度 16 | CGFloat WB_RadiansBetweenPoints(CGPoint first, CGPoint second) { 17 | CGFloat deltaX = second.x - first.x; 18 | CGFloat deltaY = second.y - first.y; 19 | return atan2f(deltaY, deltaX); 20 | } 21 | /// 两点相加 22 | CGPoint WB_PointByAddingCGPoints(CGPoint first, CGPoint second) { 23 | return CGPointMake(first.x + second.x, first.y + second.y); 24 | } 25 | 26 | /// YES: x和y有相同的符号 NO: x,y有相反的符号 27 | BOOL WB_ISSameSign(NSInteger x, NSInteger y) { 28 | // 有0的情况例外 29 | return (x ^ y) >= 0; 30 | } 31 | 32 | 33 | #pragma mark - Loading from a Texture Atlas 34 | NSArray *WB_LoadFramesFromAtlas(NSString *atlasName, NSString *baseFileName, int numberOfFrames) { 35 | NSMutableArray *frames = [NSMutableArray arrayWithCapacity:numberOfFrames]; 36 | 37 | SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:atlasName]; 38 | for (int i = 1; i <= numberOfFrames; i++) { 39 | NSString *fileName = [NSString stringWithFormat:@"%@%04d.png", baseFileName, i]; 40 | SKTexture *texture = [atlas textureNamed:fileName]; 41 | [frames addObject:texture]; 42 | } 43 | return frames; 44 | } 45 | 46 | 47 | 48 | #pragma mark - SKEmitterNode Category 49 | @implementation SKEmitterNode (WBBalloonFightAdditions) 50 | + (instancetype)wbbf_emitterNodeWithEmitterNamed:(NSString *)emitterFileName { 51 | return [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:emitterFileName ofType:@"sks"]]; 52 | } 53 | @end 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/WBSpriteButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBSpriteButton.h 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/10/9. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_OPTIONS(NSUInteger, WBButtonControlEvent) { 12 | WBButtonControlEventNone = 0, 13 | WBButtonControlEventTouchDown = 1 << 0, 14 | WBButtonControlEventTouchUp = 1 << 1, 15 | WBButtonControlEventTouchMoved = 1 << 2, 16 | WBButtonControlEventTouchUpInside = 1 << 3, 17 | WBButtonControlEventTouchUpOutside = 1 << 4, 18 | WBButtonControlEventTouchCancel = 1 << 5, 19 | WBButtonControlEventAllEvents = 0xFFFFFFFF 20 | }; 21 | 22 | 23 | @interface WBSpriteButton : SKSpriteNode 24 | 25 | @property (nonatomic) BOOL isEnabled; 26 | @property (nonatomic) BOOL isSelected; 27 | @property (nonatomic) CGPoint touchPoint; 28 | @property (nonatomic) WBButtonControlEvent controlEvent; 29 | @property (nonatomic, readonly, strong) SKLabelNode *title; 30 | @property (nonatomic, strong) SKTexture *normalButtonTexture; 31 | @property (nonatomic, strong) SKTexture *selectedButtonTexture; 32 | @property (nonatomic, strong) SKTexture *disabledButtonTexture; 33 | 34 | - (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected; 35 | - (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected disabled:(SKTexture *)disabled; 36 | 37 | - (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected; 38 | - (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected disabled:(NSString *)disabled; 39 | 40 | 41 | - (void)addTarget:(id)target action:(SEL)action forControlEvents:(WBButtonControlEvent)controlEvents; 42 | 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Classes/Utilities/WBSpriteButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBSpriteButton.m 3 | // BalloonFight 4 | // 5 | // Created by JasioWoo on 14/10/9. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "WBSpriteButton.h" 10 | 11 | #define SuppressPerformSelectorLeakWarning(Stuff) \ 12 | do { \ 13 | _Pragma("clang diagnostic push") \ 14 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ 15 | Stuff; \ 16 | _Pragma("clang diagnostic pop") \ 17 | } while (0) 18 | 19 | 20 | @interface WBSpriteButton () 21 | 22 | // NSValue(SEL) - Target 23 | @property (nonatomic, retain) NSMapTable *eventTouchDownMap; 24 | @property (nonatomic, retain) NSMapTable *eventTouchUpMap; 25 | @property (nonatomic, retain) NSMapTable *eventTouchMovedMap; 26 | @property (nonatomic, retain) NSMapTable *eventTouchUpInsideMap; 27 | @property (nonatomic, retain) NSMapTable *eventTouchUpOutsideMap; 28 | @property (nonatomic, retain) NSMapTable *eventTouchCancelMap; 29 | @property (nonatomic, retain) NSMapTable *eventTouchAllMap; 30 | 31 | 32 | @end 33 | 34 | 35 | @implementation WBSpriteButton 36 | 37 | #pragma mark - setter&getter 38 | -(NSMapTable *)eventTouchDownMap { 39 | if (!_eventTouchDownMap) { 40 | _eventTouchDownMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 41 | valueOptions:NSMapTableWeakMemory]; 42 | } 43 | return _eventTouchDownMap; 44 | } 45 | -(NSMapTable *)eventTouchUpMap { 46 | if (!_eventTouchUpMap) { 47 | _eventTouchUpMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 48 | valueOptions:NSMapTableWeakMemory]; 49 | } 50 | return _eventTouchUpMap; 51 | } 52 | -(NSMapTable *)eventTouchMovedMap { 53 | if (!_eventTouchMovedMap) { 54 | _eventTouchMovedMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 55 | valueOptions:NSMapTableWeakMemory]; 56 | } 57 | return _eventTouchMovedMap; 58 | } 59 | -(NSMapTable *)eventTouchUpInsideMap { 60 | if (!_eventTouchUpInsideMap) { 61 | _eventTouchUpInsideMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 62 | valueOptions:NSMapTableWeakMemory]; 63 | } 64 | return _eventTouchUpInsideMap; 65 | } 66 | -(NSMapTable *)eventTouchUpOutsideMap { 67 | if (!_eventTouchUpOutsideMap) { 68 | _eventTouchUpOutsideMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 69 | valueOptions:NSMapTableWeakMemory]; 70 | } 71 | return _eventTouchUpOutsideMap; 72 | } 73 | -(NSMapTable *)eventTouchCancelMap { 74 | if (!_eventTouchCancelMap) { 75 | _eventTouchCancelMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 76 | valueOptions:NSMapTableWeakMemory]; 77 | } 78 | return _eventTouchCancelMap; 79 | } 80 | -(NSMapTable *)eventTouchAllMap { 81 | if (!_eventTouchAllMap) { 82 | _eventTouchAllMap = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 83 | valueOptions:NSMapTableWeakMemory]; 84 | } 85 | return _eventTouchAllMap; 86 | } 87 | 88 | - (void)setIsEnabled:(BOOL)isEnabled { 89 | _isEnabled = isEnabled; 90 | if (self.disabledButtonTexture) { 91 | if (!_isEnabled) { 92 | [self setTexture:_disabledButtonTexture]; 93 | } else { 94 | [self setTexture:_normalButtonTexture]; 95 | } 96 | } 97 | } 98 | 99 | - (void)setIsSelected:(BOOL)isSelected { 100 | _isSelected = isSelected; 101 | if (self.selectedButtonTexture && self.isEnabled) { 102 | if (_isSelected) { 103 | [self setTexture:_selectedButtonTexture]; 104 | } else { 105 | [self setTexture:_normalButtonTexture]; 106 | } 107 | } 108 | } 109 | 110 | #pragma mark - Texture Init 111 | - (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected { 112 | return [self initWithTextureNormal:normal selected:selected disabled:nil]; 113 | } 114 | 115 | - (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected disabled:(SKTexture *)disabled { 116 | self = [super initWithTexture:normal]; 117 | if (self) { 118 | self.controlEvent = WBButtonControlEventNone; 119 | [self setNormalButtonTexture:normal]; 120 | [self setSelectedButtonTexture:selected]; 121 | [self setDisabledButtonTexture:disabled]; 122 | [self setIsEnabled:YES]; 123 | [self setIsSelected:NO]; 124 | 125 | _title = [SKLabelNode labelNodeWithFontNamed:@"Arial"]; 126 | _title.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter; 127 | _title.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter; 128 | [self addChild:_title]; 129 | 130 | self.userInteractionEnabled = YES; 131 | } 132 | return self; 133 | } 134 | 135 | #pragma mark - Image Init 136 | - (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected { 137 | return [self initWithImageNamedNormal:normal selected:selected disabled:nil]; 138 | } 139 | 140 | - (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected disabled:(NSString *)disabled { 141 | SKTexture *textureNormal = nil; 142 | if (normal) { 143 | textureNormal = [SKTexture textureWithImageNamed:normal]; 144 | } 145 | 146 | SKTexture *textureSelected = nil; 147 | if (selected) { 148 | textureSelected = [SKTexture textureWithImageNamed:selected]; 149 | } 150 | 151 | SKTexture *textureDisabled = nil; 152 | if (disabled) { 153 | textureDisabled = [SKTexture textureWithImageNamed:disabled]; 154 | } 155 | 156 | return [self initWithTextureNormal:textureNormal selected:textureSelected disabled:textureDisabled]; 157 | } 158 | 159 | 160 | #pragma mark - 161 | - (void)dealloc { 162 | if (_eventTouchDownMap) { 163 | [_eventTouchDownMap removeAllObjects]; 164 | } 165 | if (_eventTouchUpMap) { 166 | [_eventTouchUpMap removeAllObjects]; 167 | } 168 | if (_eventTouchMovedMap) { 169 | [_eventTouchMovedMap removeAllObjects]; 170 | } 171 | if (_eventTouchUpInsideMap) { 172 | [_eventTouchUpInsideMap removeAllObjects]; 173 | } 174 | if (_eventTouchUpOutsideMap) { 175 | [_eventTouchUpOutsideMap removeAllObjects]; 176 | } 177 | if (_eventTouchCancelMap) { 178 | [_eventTouchCancelMap removeAllObjects]; 179 | } 180 | if (_eventTouchAllMap) { 181 | [_eventTouchAllMap removeAllObjects]; 182 | } 183 | } 184 | 185 | #pragma mark - Setting Target-Action 186 | - (void)addTarget:(id)target action:(SEL)action forControlEvents:(WBButtonControlEvent)controlEvents { 187 | switch (controlEvents) { 188 | case WBButtonControlEventTouchDown: 189 | [self.eventTouchDownMap setObject:target forKey:[NSValue valueWithPointer:action]]; 190 | break; 191 | case WBButtonControlEventTouchUp: 192 | [self.eventTouchUpMap setObject:target forKey:[NSValue valueWithPointer:action]]; 193 | break; 194 | case WBButtonControlEventTouchMoved: 195 | [self.eventTouchMovedMap setObject:target forKey:[NSValue valueWithPointer:action]]; 196 | break; 197 | case WBButtonControlEventTouchUpInside: 198 | [self.eventTouchUpInsideMap setObject:target forKey:[NSValue valueWithPointer:action]]; 199 | break; 200 | case WBButtonControlEventTouchUpOutside: 201 | [self.eventTouchUpOutsideMap setObject:target forKey:[NSValue valueWithPointer:action]]; 202 | break; 203 | case WBButtonControlEventTouchCancel: 204 | [self.eventTouchCancelMap setObject:target forKey:[NSValue valueWithPointer:action]]; 205 | break; 206 | case WBButtonControlEventAllEvents: 207 | [self.eventTouchAllMap setObject:target forKey:[NSValue valueWithPointer:action]]; 208 | break; 209 | 210 | default: 211 | return; 212 | } 213 | } 214 | 215 | -(void)controlEventOccured:(WBButtonControlEvent)controlEvent { 216 | NSMapTable *mapTable = nil; 217 | switch (controlEvent) { 218 | case WBButtonControlEventTouchDown: 219 | mapTable = _eventTouchDownMap; 220 | break; 221 | case WBButtonControlEventTouchUp: 222 | mapTable = _eventTouchUpMap; 223 | break; 224 | case WBButtonControlEventTouchMoved: 225 | mapTable = _eventTouchMovedMap; 226 | break; 227 | case WBButtonControlEventTouchUpInside: 228 | mapTable = _eventTouchUpInsideMap; 229 | break; 230 | case WBButtonControlEventTouchUpOutside: 231 | mapTable = _eventTouchUpOutsideMap; 232 | break; 233 | case WBButtonControlEventTouchCancel: 234 | mapTable = _eventTouchCancelMap; 235 | break; 236 | case WBButtonControlEventAllEvents: 237 | mapTable = _eventTouchAllMap; 238 | break; 239 | 240 | default: 241 | return; 242 | } 243 | if (mapTable) { 244 | NSEnumerator *enumerator = mapTable.keyEnumerator; 245 | NSValue *keyValue = nil;; 246 | while (keyValue = [enumerator nextObject]) { 247 | id target = [mapTable objectForKey:keyValue]; 248 | if (target) { 249 | SuppressPerformSelectorLeakWarning([target performSelector:[keyValue pointerValue] withObject:self]); 250 | } 251 | } 252 | } 253 | 254 | } 255 | 256 | 257 | #if TARGET_OS_IPHONE 258 | #pragma mark - Event Handling - iOS 259 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 260 | if ([self isEnabled]) { 261 | UITouch *touch = [touches anyObject]; 262 | CGPoint touchPoint = [touch locationInNode:self.parent]; 263 | self.touchPoint = touchPoint; 264 | self.controlEvent = WBButtonControlEventTouchDown; 265 | [self setIsSelected:YES]; 266 | [self controlEventOccured:WBButtonControlEventTouchDown]; 267 | [self controlEventOccured:WBButtonControlEventAllEvents]; 268 | } 269 | } 270 | 271 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 272 | if ([self isEnabled]) { 273 | self.controlEvent = WBButtonControlEventTouchMoved; 274 | UITouch *touch = [touches anyObject]; 275 | CGPoint touchPoint = [touch locationInNode:self.parent]; 276 | self.touchPoint = touchPoint; 277 | if (CGRectContainsPoint(self.frame, touchPoint)) { 278 | [self setIsSelected:YES]; 279 | } else { 280 | [self setIsSelected:NO]; 281 | } 282 | [self controlEventOccured:WBButtonControlEventTouchMoved]; 283 | [self controlEventOccured:WBButtonControlEventAllEvents]; 284 | } 285 | } 286 | 287 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 288 | if ([self isEnabled]) { 289 | UITouch *touch = [touches anyObject]; 290 | CGPoint touchPoint = [touch locationInNode:self.parent]; 291 | [self setIsSelected:NO]; 292 | if (CGRectContainsPoint(self.frame, touchPoint)) { 293 | self.controlEvent = WBButtonControlEventTouchUp | WBButtonControlEventTouchUpInside; 294 | [self controlEventOccured:WBButtonControlEventTouchUpInside]; 295 | } else { 296 | self.controlEvent = WBButtonControlEventTouchUp | WBButtonControlEventTouchUpOutside; 297 | [self controlEventOccured:WBButtonControlEventTouchUpOutside]; 298 | } 299 | [self controlEventOccured:WBButtonControlEventTouchUp]; 300 | [self controlEventOccured:WBButtonControlEventAllEvents]; 301 | } 302 | } 303 | 304 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 305 | if ([self isEnabled]) { 306 | [self setIsSelected:NO]; 307 | self.controlEvent = WBButtonControlEventTouchCancel; 308 | [self controlEventOccured:WBButtonControlEventTouchCancel]; 309 | [self controlEventOccured:WBButtonControlEventAllEvents]; 310 | } 311 | } 312 | #else 313 | 314 | #pragma mark - Event Handling - OS X 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | #endif 326 | 327 | 328 | 329 | 330 | 331 | @end 332 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PathFinding-Mac 4 | // 5 | 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SplitWindow.h" 12 | 13 | @interface AppDelegate : NSObject 14 | 15 | @property (assign) IBOutlet SplitWindow *window; 16 | 17 | @property (nonatomic, weak)IBOutlet NSStackView *stackView; 18 | @property (nonatomic, weak)IBOutlet SKView *skView; 19 | 20 | 21 | 22 | - (IBAction)showHelp:(id)sender; 23 | 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PathFinding-Mac 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "GameScene.h" 11 | #import "StackCellViewController.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @property (weak)IBOutlet NSView *headerView; 16 | 17 | @end 18 | 19 | 20 | @implementation AppDelegate 21 | 22 | @synthesize window = _window; 23 | 24 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 25 | 26 | CGSize size = self.skView.frame.size; 27 | NSLog(@"SKView size = %@", NSStringFromSize(size)); 28 | GameScene *scene = [[GameScene alloc] initWithSize:size]; 29 | scene.scaleMode = SKSceneScaleModeResizeFill; 30 | [self.skView presentScene:scene]; 31 | self.skView.ignoresSiblingOrder = YES; 32 | // self.skView.showsFields = YES; 33 | // self.skView.showsPhysics = YES; 34 | self.skView.showsFPS = YES; 35 | // self.skView.showsDrawCount = YES; 36 | // self.skView.showsNodeCount = YES; 37 | 38 | [self.window.rightView setWantsLayer:YES]; 39 | self.window.pf_delegate = scene; 40 | 41 | // load data 42 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"PathFindingData" ofType:@"json"]; 43 | NSData *data = [NSData dataWithContentsOfFile:filePath]; 44 | NSArray *pfDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; 45 | 46 | [self.stackView addView:self.headerView inGravity:NSStackViewGravityLeading]; 47 | self.window.stackCells = [NSMutableArray array]; 48 | int i = 0; 49 | for (NSDictionary *data in pfDic) { 50 | StackCellViewController *cellVC = [[StackCellViewController alloc] initWithNibName:@"StackCellViewController" bundle:nil]; 51 | cellVC.delegate = self; 52 | [cellVC loadingData:data]; 53 | [self.stackView addView:cellVC.view inGravity:NSStackViewGravityLeading]; 54 | [self.window.stackCells addObject:cellVC]; 55 | if (i==0) { 56 | [cellVC changeDisclosureViewState:NO]; 57 | } 58 | i++; 59 | } 60 | 61 | } 62 | 63 | 64 | 65 | 66 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { 67 | return YES; 68 | } 69 | 70 | 71 | - (IBAction)showHelp:(id)sender { 72 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/wbcyclist/PathFindingForObjC"]]; 73 | } 74 | 75 | 76 | 77 | #pragma mark - StackCellViewDelegate 78 | - (void)stackCellVC:(StackCellViewController *)stackCellVC disclosureDidChange:(BOOL)disclosureIsClosed { 79 | for (StackCellViewController *cellVC in self.window.stackCells) { 80 | if (stackCellVC != cellVC) { 81 | [cellVC changeDisclosureViewState:YES]; 82 | } 83 | } 84 | } 85 | 86 | 87 | 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/Info-mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.jasio.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2014年 JasioWoo. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/NoHitLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // NoHitLabel.h 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/3/27. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NoHitLabel : NSTextField 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/NoHitLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // NoHitLabel.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/3/27. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "NoHitLabel.h" 10 | 11 | @implementation NoHitLabel 12 | 13 | 14 | - (NSView *)hitTest:(NSPoint)aPoint { 15 | return nil; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/SplitWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // SplitWindow.h 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/3/20. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GameScene.h" 11 | 12 | 13 | 14 | @interface SplitWindow : NSWindow 15 | 16 | @property (assign)IBOutlet NSSplitView *splitView; 17 | @property (nonatomic, weak)IBOutlet NSView *leftView; 18 | @property (nonatomic, weak)IBOutlet NSView *rightView; 19 | 20 | @property (nonatomic, weak)IBOutlet NSSlider *speedSlider; 21 | @property (nonatomic, weak)IBOutlet NSButton *weightCheckBtn; 22 | @property (nonatomic, strong)NSMutableArray *stackCells; 23 | 24 | @property (nonatomic, weak)IBOutlet NSTextField *timeLab; 25 | @property (nonatomic, weak)IBOutlet NSTextField *lengthLab; 26 | @property (nonatomic, weak)IBOutlet NSButton *startBtn; 27 | @property (nonatomic, weak)IBOutlet NSButton *pauseBtn; 28 | @property (nonatomic, weak)IBOutlet NSButton *clearBtn; 29 | 30 | @property (nonatomic, weak)id pf_delegate; 31 | 32 | 33 | - (IBAction)startBtnAction:(NSButton *)sender; 34 | - (IBAction)pauseBtnAction:(NSButton *)sender; 35 | - (IBAction)clearBtnAction:(NSButton *)sender; 36 | 37 | 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/SplitWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // SplitWindow.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/3/20. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "SplitWindow.h" 10 | #import "StackCellViewController.h" 11 | 12 | 13 | @interface SplitWindow() 14 | @property (nonatomic, weak)GameScene *gameScene; 15 | @end 16 | 17 | @implementation SplitWindow 18 | 19 | - (void)setRightView:(NSView *)rightView { 20 | if (_rightView != rightView) { 21 | _rightView = rightView; 22 | 23 | [[NSNotificationCenter defaultCenter] addObserver:self 24 | selector:@selector(startFindingPath:) 25 | name:PathFinding_NC_Start object:nil]; 26 | [[NSNotificationCenter defaultCenter] addObserver:self 27 | selector:@selector(finishFindingPath:) 28 | name:PathFinding_NC_Finish object:nil]; 29 | [[NSNotificationCenter defaultCenter] addObserver:self 30 | selector:@selector(resultFindingPath:) 31 | name:PathFinding_NC_Result object:nil]; 32 | } 33 | } 34 | 35 | - (void)dealloc { 36 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 37 | } 38 | 39 | #define LEFT_VIEW_MIN_WIDTH 250.0 40 | #define LEFT_VIEW_MAX_WIDTH 350.0 41 | 42 | - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview { 43 | return self.leftView == subview; 44 | } 45 | 46 | - (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex { 47 | return self.leftView == subview; 48 | } 49 | 50 | - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex { 51 | return LEFT_VIEW_MIN_WIDTH; 52 | } 53 | 54 | - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex { 55 | return LEFT_VIEW_MAX_WIDTH; 56 | } 57 | 58 | //- (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize { 59 | // if (NSEqualSizes(splitView.frame.size, oldSize)) { 60 | // return; 61 | // } 62 | // NSSize newSize = splitView.frame.size; 63 | // NSPoint rightOrigin = NSMakePoint(splitView.dividerThickness, 0); 64 | // 65 | // if (![splitView isSubviewCollapsed:self.leftView]) { 66 | // rightOrigin = NSMakePoint(self.leftView.frame.size.width + splitView.dividerThickness, 0); 67 | // 68 | // if (newSize.height != oldSize.height) { 69 | // [self.leftView setFrameSize:NSMakeSize(self.leftView.frame.size.width, newSize.height)]; 70 | // } 71 | // } 72 | // 73 | // NSRect rightFrame = { 74 | // rightOrigin, 75 | // newSize.width - rightOrigin.x, 76 | // newSize.height 77 | // }; 78 | // [self.rightView setFrame:rightFrame]; 79 | //} 80 | 81 | 82 | - (void)setPf_delegate:(id)pf_delegate { 83 | if (_pf_delegate != pf_delegate) { 84 | _pf_delegate = pf_delegate; 85 | if ([_pf_delegate isKindOfClass:[GameScene class]]) { 86 | self.gameScene = (GameScene *)_pf_delegate; 87 | } else { 88 | self.gameScene = nil; 89 | } 90 | } 91 | } 92 | 93 | static NSString *TIME_LAB_PREFIX = @"Time: "; 94 | static NSString *LENGTH_LAB_PREFIX = @"Length: "; 95 | 96 | - (IBAction)startBtnAction:(NSButton *)sender { 97 | NSMutableDictionary *pfInfo; 98 | if (self.gameScene.pfState!=PFState_pause) { 99 | pfInfo = [NSMutableDictionary dictionary]; 100 | pfInfo[@"isShowWeight"] = @(self.weightCheckBtn.state); 101 | pfInfo[@"trackSpeed"] = @(abs(self.speedSlider.intValue-(int)self.speedSlider.maxValue-1)); 102 | for (StackCellViewController *cellVC in self.stackCells) { 103 | if (!cellVC.disclosureIsClosed) { 104 | pfInfo[@"algType"] = @(cellVC.algType); 105 | pfInfo[@"heuristicType"] = @(cellVC.heuristicType); 106 | pfInfo[@"movementType"] = @(cellVC.movementType); 107 | pfInfo[@"isBidirectional"] = @(cellVC.isBidirectional); 108 | pfInfo[@"weight"] = @(cellVC.weight); 109 | break; 110 | } 111 | } 112 | } 113 | [self.gameScene startAction:self withPFInfo:pfInfo]; 114 | 115 | self.startBtn.title = @"Restart\nSearch"; 116 | self.pauseBtn.title = @"Pause\nSearch"; 117 | } 118 | - (IBAction)pauseBtnAction:(NSButton *)sender { 119 | if (self.gameScene.pfState==PFState_Ide) { 120 | self.pauseBtn.title = @"Pause\nSearch"; 121 | return; 122 | } else if (self.gameScene.pfState==PFState_finding) { 123 | self.startBtn.title = @"Resume\nSearch"; 124 | self.pauseBtn.title = @"Cancel\nSearch"; 125 | } else if (self.gameScene.pfState==PFState_pause || self.gameScene.pfState==PFState_finish) { 126 | self.startBtn.title = @"Start\nSearch"; 127 | self.pauseBtn.title = @"Pause\nSearch"; 128 | } 129 | [self.gameScene pauseAction:self]; 130 | } 131 | - (IBAction)clearBtnAction:(NSButton *)sender { 132 | [self.gameScene clearAction:self]; 133 | self.startBtn.title = @"Start\nSearch"; 134 | self.pauseBtn.title = @"Pause\nSearch"; 135 | } 136 | 137 | 138 | -(IBAction)adjustTrackSpeed:(NSSlider *)sender { 139 | self.gameScene.trackSpeed = abs(sender.intValue-(int)sender.maxValue-1); 140 | } 141 | 142 | #pragma mark - GameScene Notification 143 | - (void)startFindingPath:(NSNotification *)notification { 144 | 145 | } 146 | - (void)finishFindingPath:(NSNotification *)notification { 147 | self.startBtn.title = @"Restart\nSearch"; 148 | self.pauseBtn.title = @"Clear\nPath"; 149 | } 150 | - (void)resultFindingPath:(NSNotification *)notification { 151 | NSNumber *costTime = notification.userInfo[@"costTime"]; 152 | NSNumber *length = notification.userInfo[@"length"]; 153 | 154 | self.timeLab.stringValue = [NSString stringWithFormat:@"%@%fms", TIME_LAB_PREFIX, costTime.floatValue]; 155 | self.lengthLab.stringValue = [NSString stringWithFormat:@"%@%d", LENGTH_LAB_PREFIX, length.intValue]; 156 | } 157 | 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/StackCellViewController.h: -------------------------------------------------------------------------------- 1 | // Created by JasioWoo on 15/3/22. 2 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | @class StackCellViewController; 8 | 9 | @protocol StackCellViewDelegate 10 | @optional 11 | - (void)stackCellVC:(StackCellViewController *)stackCellVC disclosureDidChange:(BOOL)disclosureIsClosed; 12 | @end 13 | 14 | 15 | @interface StackCellViewController : NSViewController 16 | 17 | @property (nonatomic, weak) iddelegate; 18 | 19 | @property (weak)IBOutlet NSView *headerView; 20 | @property (nonatomic, assign, readonly)BOOL disclosureIsClosed; 21 | @property (nonatomic, strong)NSDictionary *cellData; 22 | 23 | @property (nonatomic, assign)int algType; 24 | @property (nonatomic, assign)int heuristicType; 25 | @property (nonatomic, assign)int movementType; 26 | @property (nonatomic, assign)BOOL isBidirectional; 27 | @property (nonatomic, assign)int weight; 28 | 29 | 30 | - (IBAction)toggleDisclosure:(id)sender; 31 | 32 | 33 | - (void)loadingData:(NSDictionary*)data; 34 | - (void)changeDisclosureViewState:(BOOL)disclosureIsClosed; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/StackCellViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/OSX/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PathFinding-Mac 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/PathFindingForObjC-Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/PathFindingForObjC-Example.xcodeproj/xcshareddata/xcbaselines/A53546A919FFC908005C629B.xcbaseline/721B40FB-E133-44FE-B8DD-F647C48F176A.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | PathFinding_iOSTests 8 | 9 | testPerformanceExample 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/PathFindingForObjC-Example.xcodeproj/xcshareddata/xcbaselines/A53546A919FFC908005C629B.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 721B40FB-E133-44FE-B8DD-F647C48F176A 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 2600 19 | logicalCPUCoresPerPackage 20 | 8 21 | modelCode 22 | MacBookPro10,1 23 | physicalCPUCoresPerPackage 24 | 4 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | targetDevice 31 | 32 | modelCode 33 | iPhone6,1 34 | platformIdentifier 35 | com.apple.platform.iphonesimulator 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/PathFindingForObjC-Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Podfile: -------------------------------------------------------------------------------- 1 | inhibit_all_warnings! 2 | 3 | def import_ios_pods 4 | pod 'ECSlidingViewController' 5 | pod 'RadioButton' 6 | end 7 | 8 | target :'PathFinding-iOS', :exclusive => true do 9 | platform :ios, '8.0' 10 | import_ios_pods 11 | end 12 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - ECSlidingViewController (2.0.3) 3 | - RadioButton (1.0) 4 | 5 | DEPENDENCIES: 6 | - ECSlidingViewController 7 | - RadioButton 8 | 9 | SPEC CHECKSUMS: 10 | ECSlidingViewController: 6485c14f23b4487e9946535421ca8f111f86cc53 11 | RadioButton: 7ef053baf6ff0e7f9e777016c28e9ba4e330edba 12 | 13 | COCOAPODS: 0.36.3 14 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "mac", 65 | "size" : "16x16", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "mac", 70 | "size" : "16x16", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "mac", 75 | "size" : "32x32", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "mac", 80 | "size" : "32x32", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "mac", 85 | "size" : "128x128", 86 | "scale" : "1x" 87 | }, 88 | { 89 | "idiom" : "mac", 90 | "size" : "128x128", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "idiom" : "mac", 95 | "size" : "256x256", 96 | "scale" : "1x" 97 | }, 98 | { 99 | "idiom" : "mac", 100 | "size" : "256x256", 101 | "scale" : "2x" 102 | }, 103 | { 104 | "idiom" : "mac", 105 | "size" : "512x512", 106 | "scale" : "1x" 107 | }, 108 | { 109 | "idiom" : "mac", 110 | "size" : "512x512", 111 | "scale" : "2x" 112 | } 113 | ], 114 | "info" : { 115 | "version" : 1, 116 | "author" : "xcode" 117 | }, 118 | "properties" : { 119 | "pre-rendered" : true 120 | } 121 | } -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/Images.xcassets/Spaceship.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Spaceship.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/Images.xcassets/Spaceship.imageset/Spaceship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/Images.xcassets/Spaceship.imageset/Spaceship.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/PathFindingData.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Algorithm": "AStar", 4 | "algType": 0, 5 | "Heuristic": ["Manhattan", "Euclidean", "Octile", "Chebyshev"], 6 | "DiagonalMovement": ["Always", "Never", "IfAtMostOneObstacle", "OnlyWhenNoObstacles"], 7 | "Options": {"Bi-directional": "true", "Weight": "true"} 8 | }, 9 | { 10 | "Algorithm": "BestFirstSearch", 11 | "algType": 1, 12 | "Heuristic": ["Manhattan", "Euclidean", "Octile", "Chebyshev"], 13 | "DiagonalMovement": ["Always", "Never", "IfAtMostOneObstacle", "OnlyWhenNoObstacles"], 14 | "Options": {"Bi-directional": "true"} 15 | }, 16 | { 17 | "Algorithm": "BreadthFirstSearch", 18 | "algType": 4, 19 | "DiagonalMovement": ["Always", "Never", "IfAtMostOneObstacle", "OnlyWhenNoObstacles"], 20 | "Options": {"Bi-directional": "true"} 21 | }, 22 | { 23 | "Algorithm": "Dijkstra", 24 | "algType": 2, 25 | "DiagonalMovement": ["Always", "Never", "IfAtMostOneObstacle", "OnlyWhenNoObstacles"], 26 | "Options": {"Bi-directional": "true"} 27 | }, 28 | { 29 | "Algorithm": "JumpPointSearch", 30 | "algType": 3, 31 | "Heuristic": ["Manhattan", "Euclidean", "Octile", "Chebyshev"], 32 | "DiagonalMovement": ["Always", "Never", "IfAtMostOneObstacle", "OnlyWhenNoObstacles"] 33 | } 34 | ] -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/arrow.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/cell_menu_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/cell_menu_bg.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/grid.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/radio-res-ios/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/radio-res-ios/checked.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/radio-res-ios/checked@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/radio-res-ios/checked@2x.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/radio-res-ios/unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/radio-res-ios/unchecked.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/Resources/radio-res-ios/unchecked@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/PathFindingForObjC-Example/Resources/radio-res-ios/unchecked@2x.png -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/ContentViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.h 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/4/4. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GameScene.h" 11 | 12 | @interface ContentViewController : UIViewController 13 | 14 | @property (nonatomic, weak)IBOutlet SKView *skView; 15 | 16 | @property (nonatomic, weak)id pf_delegate; 17 | 18 | 19 | - (void)adjustTrackSpeed:(UISlider *)sender; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/ContentViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/4/4. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "ContentViewController.h" 10 | #import "LeftViewController.h" 11 | #import "UIViewController+ECSlidingViewController.h" 12 | 13 | @interface ContentViewController () 14 | @property (nonatomic, weak)GameScene *gameScene; 15 | 16 | @property (nonatomic, weak)IBOutlet UIButton *startBtn; 17 | @property (nonatomic, weak)IBOutlet UIButton *pauseBtn; 18 | @property (nonatomic, weak)IBOutlet UIButton *clearBtn; 19 | @property (nonatomic, weak)IBOutlet UILabel *timeLab; 20 | @property (nonatomic, weak)IBOutlet UILabel *lengthLab; 21 | 22 | 23 | 24 | @end 25 | 26 | @implementation ContentViewController 27 | 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | 31 | // Configure the view. 32 | self.skView.showsFPS = YES; 33 | // self.skView.showsDrawCount = YES; 34 | // self.skView.showsNodeCount = YES; 35 | self.skView.ignoresSiblingOrder = YES; 36 | 37 | // Create and configure the scene. 38 | CGSize viewSize = self.view.bounds.size; 39 | viewSize.width *= 2; 40 | viewSize.height *= 2; 41 | GameScene *scene = [[GameScene alloc] initWithSize:viewSize]; 42 | scene.scaleMode = SKSceneScaleModeFill; 43 | // Present the scene. 44 | [self.skView presentScene:scene]; 45 | self.pf_delegate = scene; 46 | 47 | [[NSNotificationCenter defaultCenter] addObserver:self 48 | selector:@selector(startFindingPath:) 49 | name:PathFinding_NC_Start object:nil]; 50 | [[NSNotificationCenter defaultCenter] addObserver:self 51 | selector:@selector(finishFindingPath:) 52 | name:PathFinding_NC_Finish object:nil]; 53 | [[NSNotificationCenter defaultCenter] addObserver:self 54 | selector:@selector(resultFindingPath:) 55 | name:PathFinding_NC_Result object:nil]; 56 | } 57 | 58 | - (void)viewWillAppear:(BOOL)animated { 59 | [super viewWillAppear:animated]; 60 | // self.slidingViewController.anchorRightPeekAmount = 50; 61 | // [self.view addGestureRecognizer:self.slidingViewController.panGesture]; 62 | } 63 | 64 | - (void)dealloc { 65 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 66 | } 67 | 68 | - (void)setPf_delegate:(id)pf_delegate { 69 | if (_pf_delegate != pf_delegate) { 70 | _pf_delegate = pf_delegate; 71 | if ([_pf_delegate isKindOfClass:[GameScene class]]) { 72 | self.gameScene = (GameScene *)_pf_delegate; 73 | } else { 74 | self.gameScene = nil; 75 | } 76 | } 77 | } 78 | 79 | 80 | #pragma mark - fun 81 | - (IBAction)menuButtonTapped:(UIButton *)sender { 82 | if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionCentered) { 83 | [self.slidingViewController anchorTopViewToRightAnimated:YES]; 84 | [sender setTitle:@"Close" forState:UIControlStateNormal]; 85 | } else { 86 | [self.slidingViewController resetTopViewAnimated:YES]; 87 | [sender setTitle:@"Menu" forState:UIControlStateNormal]; 88 | } 89 | } 90 | 91 | - (void)adjustTrackSpeed:(UISlider *)sender { 92 | // NSLog(@"track speed = %d", (int)fabsf(sender.value-sender.maximumValue-1)); 93 | self.gameScene.trackSpeed = fabsf(sender.value-sender.maximumValue-1); 94 | } 95 | 96 | 97 | static NSString *TIME_LAB_PREFIX = @"Time: "; 98 | static NSString *LENGTH_LAB_PREFIX = @"Length: "; 99 | 100 | - (IBAction)startBtnAction:(UIButton *)sender { 101 | NSDictionary *pfInfo = nil; 102 | if (self.gameScene.pfState!=PFState_pause) { 103 | LeftViewController *leftVC = (LeftViewController*)self.slidingViewController.underLeftViewController; 104 | pfInfo = [leftVC fetchPFInfo]; 105 | if (!pfInfo) { 106 | return; 107 | } 108 | } 109 | [self.gameScene startAction:self withPFInfo:pfInfo]; 110 | 111 | [self.startBtn setTitle:@"Restart\nSearch" forState:UIControlStateNormal]; 112 | [self.pauseBtn setTitle:@"Pause\nSearch" forState:UIControlStateNormal]; 113 | } 114 | - (IBAction)pauseBtnAction:(UIButton *)sender { 115 | if (self.gameScene.pfState==PFState_Ide) { 116 | [self.pauseBtn setTitle:@"Pause\nSearch" forState:UIControlStateNormal]; 117 | return; 118 | } else if (self.gameScene.pfState==PFState_finding) { 119 | [self.startBtn setTitle:@"Resume\nSearch" forState:UIControlStateNormal]; 120 | [self.pauseBtn setTitle:@"Cancel\nSearch" forState:UIControlStateNormal]; 121 | } else if (self.gameScene.pfState==PFState_pause || self.gameScene.pfState==PFState_finish) { 122 | [self.startBtn setTitle:@"Start\nSearch" forState:UIControlStateNormal]; 123 | [self.pauseBtn setTitle:@"Pause\nSearch" forState:UIControlStateNormal]; 124 | } 125 | [self.gameScene pauseAction:self]; 126 | } 127 | - (IBAction)clearBtnAction:(UIButton *)sender { 128 | [self.gameScene clearAction:self]; 129 | [self.startBtn setTitle:@"Start\nSearch" forState:UIControlStateNormal]; 130 | [self.pauseBtn setTitle:@"Pause\nSearch" forState:UIControlStateNormal]; 131 | } 132 | 133 | 134 | #pragma mark - GameScene Notification 135 | - (void)startFindingPath:(NSNotification *)notification { 136 | 137 | } 138 | - (void)finishFindingPath:(NSNotification *)notification { 139 | [self.startBtn setTitle:@"Restart\nSearch" forState:UIControlStateNormal]; 140 | [self.pauseBtn setTitle:@"Clear\nPath" forState:UIControlStateNormal]; 141 | } 142 | - (void)resultFindingPath:(NSNotification *)notification { 143 | NSNumber *costTime = notification.userInfo[@"costTime"]; 144 | NSNumber *length = notification.userInfo[@"length"]; 145 | 146 | self.timeLab.text = [NSString stringWithFormat:@"%@%fms", TIME_LAB_PREFIX, costTime.floatValue]; 147 | self.lengthLab.text = [NSString stringWithFormat:@"%@%d", LENGTH_LAB_PREFIX, length.intValue]; 148 | } 149 | 150 | 151 | 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/GameViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.h 3 | // PathFindingForObjC-Example 4 | // 5 | 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface GameViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/GameViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "GameViewController.h" 10 | #import "GameScene.h" 11 | 12 | @implementation GameViewController 13 | 14 | - (void)viewDidLoad 15 | { 16 | [super viewDidLoad]; 17 | 18 | // Configure the view. 19 | SKView * skView = (SKView *)self.view; 20 | skView.showsFPS = YES; 21 | skView.showsDrawCount = YES; 22 | skView.showsNodeCount = YES; 23 | /* Sprite Kit applies additional optimizations to improve rendering performance */ 24 | skView.ignoresSiblingOrder = YES; 25 | 26 | // Create and configure the scene. 27 | CGSize viewSize = self.view.bounds.size; 28 | viewSize.width *= 2; 29 | viewSize.height *= 2; 30 | GameScene *scene = [[GameScene alloc] initWithSize:viewSize]; 31 | scene.scaleMode = SKSceneScaleModeFill; 32 | 33 | // Present the scene. 34 | [skView presentScene:scene]; 35 | } 36 | 37 | 38 | 39 | 40 | - (BOOL)prefersStatusBarHidden { 41 | return YES; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/Info-ios.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.jasio.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/LeftViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LeftViewController.h 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/4/4. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LeftViewController : UIViewController 12 | 13 | @property (nonatomic, weak)IBOutlet UITableView *stackTableView; 14 | 15 | 16 | - (NSDictionary *)fetchPFInfo; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/LeftViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LeftViewController.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/4/4. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "LeftViewController.h" 10 | #import "UIViewController+ECSlidingViewController.h" 11 | #import "StackTableViewCell.h" 12 | #import "ContentViewController.h" 13 | 14 | static NSString *CellIdentifier = @"StackTableCell"; 15 | 16 | @interface LeftViewController () 17 | 18 | @property (nonatomic, weak)IBOutlet UISlider *speedSlider; 19 | @property (nonatomic, weak)IBOutlet UISwitch *weightSwitch; 20 | 21 | @end 22 | 23 | @implementation LeftViewController { 24 | int currentRow; 25 | NSMutableArray *cells; 26 | } 27 | 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | 31 | cells = [NSMutableArray array]; 32 | 33 | // load data 34 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"PathFindingData" ofType:@"json"]; 35 | NSData *data = [NSData dataWithContentsOfFile:filePath]; 36 | NSArray *pfDics = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; 37 | 38 | currentRow = 0; 39 | for (int i=0; i 10 | 11 | @interface StackTableViewCell : UITableViewCell 12 | 13 | @property (nonatomic, strong) UIView *openView; 14 | 15 | @property (nonatomic, assign)BOOL isOpened; 16 | @property (nonatomic, assign)CGFloat cellHeight; 17 | @property (nonatomic, strong)NSDictionary *cellData; 18 | 19 | @property (nonatomic, assign)int algType; 20 | @property (nonatomic, assign)int heuristicType; 21 | @property (nonatomic, assign)int movementType; 22 | @property (nonatomic, assign)BOOL isBidirectional; 23 | @property (nonatomic, assign)int weight; 24 | 25 | 26 | 27 | 28 | - (void)loadingData:(NSDictionary*)data; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/StackTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // StackTableViewCell.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 15/4/5. 6 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import "StackTableViewCell.h" 10 | #import "RadioButton.h" 11 | 12 | @interface StackTableViewCell () 13 | 14 | @property (nonatomic, strong) UILabel *titleLab; 15 | 16 | @end 17 | 18 | @implementation StackTableViewCell 19 | 20 | 21 | //- (void)setCellBG:(UIImageView *)cellBG { 22 | // if (_cellBG != cellBG) { 23 | // _cellBG = cellBG; 24 | // UIImage *image = [[UIImage imageNamed:@"cell_menu_bg.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(4,0,4,0)]; 25 | // [cellBG setImage:image]; 26 | // } 27 | //} 28 | 29 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 30 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 31 | if (self) { 32 | self.weight = 1; 33 | self.cellHeight = 44; 34 | self.titleLab = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 180, 44)]; 35 | self.titleLab.font = [UIFont boldSystemFontOfSize:14]; 36 | [self.contentView addSubview:self.titleLab]; 37 | 38 | self.openView = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.titleLab.frame), 400, 0)]; 39 | self.openView.backgroundColor = [UIColor lightGrayColor]; 40 | self.openView.clipsToBounds = YES; 41 | self.openView.hidden = YES; 42 | [self.contentView addSubview:self.openView]; 43 | 44 | } 45 | return self; 46 | } 47 | 48 | //- (void)layoutSubviews { 49 | // debugMethod(); 50 | // [super layoutSubviews]; 51 | // 52 | //// self.bounds = CGRectMake(0, 0, CGRectGetWidth(self.bounds), 60); 53 | // NSLog(@"cell tag=%d frame = %@", self.tag, NSStringFromCGRect(self.frame)); 54 | // NSLog(@"cell content frame = %@", NSStringFromCGRect(self.contentView.frame)); 55 | //} 56 | 57 | 58 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 59 | [super setSelected:selected animated:NO]; 60 | } 61 | 62 | 63 | - (void)setIsOpened:(BOOL)isOpened { 64 | if (_isOpened != isOpened) { 65 | _isOpened = isOpened; 66 | self.openView.hidden = !isOpened; 67 | if (isOpened) { 68 | self.cellHeight = CGRectGetMaxY(self.openView.frame); 69 | } else { 70 | self.cellHeight = CGRectGetMinY(self.openView.frame); 71 | } 72 | } 73 | } 74 | 75 | - (void)loadingData:(NSDictionary*)data { 76 | if (data==self.cellData) { 77 | return; 78 | } 79 | self.openView.frame = CGRectMake(0, CGRectGetMaxY(self.titleLab.frame), 400, 0); 80 | self.cellHeight = CGRectGetMinY(self.openView.frame); 81 | [self.openView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 82 | if (!data) { 83 | return; 84 | } 85 | CGFloat height = 0; 86 | 87 | self.cellData = data; 88 | self.titleLab.text = self.cellData[@"Algorithm"]; 89 | self.algType = [self.cellData[@"algType"] intValue]; 90 | 91 | int index = 0; 92 | for (NSString *key in @[@"Heuristic", @"DiagonalMovement"]) { 93 | NSArray *value = self.cellData[key]; 94 | if (value) { 95 | UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(0, height, 250, 17)]; 96 | lab.font = [UIFont boldSystemFontOfSize:14]; 97 | lab.textColor = [UIColor whiteColor]; 98 | lab.text = key; 99 | [self.openView addSubview:lab]; 100 | height = CGRectGetMaxY(lab.frame); 101 | 102 | NSMutableArray* buttons = [NSMutableArray arrayWithCapacity:4]; 103 | CGRect btnRect = CGRectMake(10, height, 250, 30); 104 | for (int i=0; i < value.count; i++) { 105 | NSString *optionTitle = value[i]; 106 | RadioButton* btn = [[RadioButton alloc] initWithFrame:btnRect]; 107 | [btn addTarget:self action:@selector(onRadioButtonValueChanged:) forControlEvents:UIControlEventValueChanged]; 108 | btnRect.origin.y += 25; 109 | btn.tag = i + index; 110 | [btn setTitle:optionTitle forState:UIControlStateNormal]; 111 | [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 112 | btn.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 113 | [btn setImage:[UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal]; 114 | [btn setImage:[UIImage imageNamed:@"checked.png"] forState:UIControlStateSelected]; 115 | btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; 116 | btn.titleEdgeInsets = UIEdgeInsetsMake(0, 6, 0, 0); 117 | [buttons addObject:btn]; 118 | 119 | [self.openView addSubview:btn]; 120 | height = CGRectGetMaxY(btn.frame); 121 | } 122 | [buttons[0] setGroupButtons:buttons]; // Setting buttons into the group 123 | [buttons[0] setSelected:YES]; 124 | } 125 | index += 10; 126 | } 127 | 128 | NSDictionary *options = self.cellData[@"Options"]; 129 | BOOL hasBI = [options[@"Bi-directional"] boolValue]; 130 | BOOL hasWeight = [options[@"Weight"] boolValue]; 131 | 132 | if (hasBI || hasWeight) { 133 | UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(0, height, 200, 17)]; 134 | lab.font = [UIFont boldSystemFontOfSize:14]; 135 | lab.textColor = [UIColor whiteColor]; 136 | lab.text = @"Options"; 137 | [self.openView addSubview:lab]; 138 | height = CGRectGetMaxY(lab.frame); 139 | 140 | if (hasBI) { 141 | UILabel *lab2 = [[UILabel alloc] initWithFrame:CGRectMake(10, height+5, 100, 17)]; 142 | lab2.font = [UIFont boldSystemFontOfSize:14]; 143 | lab2.textColor = [UIColor whiteColor]; 144 | lab2.text = @"Bi-directional:"; 145 | [self.openView addSubview:lab2]; 146 | 147 | UISwitch *biSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(CGRectGetMaxX(lab2.frame), height-3, 0, 0)]; 148 | biSwitch.on = NO; 149 | biSwitch.tag = 0; 150 | [biSwitch addTarget:self action:@selector(onSwitchValueChanged:) forControlEvents:UIControlEventValueChanged]; 151 | [self.openView addSubview:biSwitch]; 152 | height = CGRectGetMaxY(biSwitch.frame); 153 | } 154 | 155 | // if (hasWeight) { 156 | // UILabel *lab3 = [[UILabel alloc] initWithFrame:CGRectMake(10, height+10, 55, 17)]; 157 | // lab3.font = [UIFont boldSystemFontOfSize:14]; 158 | // lab3.textColor = [UIColor whiteColor]; 159 | // lab3.text = @"Weight:"; 160 | // [self.openView addSubview:lab3]; 161 | // 162 | // UITextField *field = [[UITextField alloc] initWithFrame:CGRectMake(CGRectGetMaxX(lab3.frame), height, 98, 27)]; 163 | // field.text = @"1"; 164 | // field.returnKeyType = UIReturnKeyDone; 165 | // field.enablesReturnKeyAutomatically = YES; 166 | // field.keyboardType = UIKeyboardTypeNumberPad; 167 | // field.textAlignment = NSTextAlignmentRight; 168 | // field.borderStyle = UITextBorderStyleRoundedRect; 169 | // 170 | // [self.openView addSubview:field]; 171 | // height = CGRectGetMaxY(field.frame); 172 | // } 173 | } 174 | height +=5; 175 | self.openView.frame = (CGRect){ 176 | .origin = self.openView.frame.origin, 177 | .size.width = CGRectGetWidth(self.openView.frame), 178 | .size.height = height 179 | }; 180 | if (self.isOpened) { 181 | self.cellHeight = CGRectGetMaxY(self.openView.frame); 182 | } else { 183 | self.cellHeight = CGRectGetMinY(self.openView.frame); 184 | } 185 | } 186 | 187 | 188 | -(void)onRadioButtonValueChanged:(RadioButton*)sender { 189 | if(sender.selected) { 190 | NSLog(@"Selected: %@, tag=%d", sender.titleLabel.text, (int)sender.tag); 191 | if (sender.tag<10) { 192 | self.heuristicType = (int)sender.tag; 193 | } else { 194 | self.movementType = (int)sender.tag-10; 195 | } 196 | } 197 | } 198 | 199 | -(void)onSwitchValueChanged:(UISwitch*)sender { 200 | NSLog(@"UISwitch tag:%d, state:%@", (int)sender.tag, sender.on?@"YES":@"NO"); 201 | if (sender.tag==0) { 202 | self.isBidirectional = sender.isOn; 203 | } 204 | } 205 | 206 | 207 | 208 | 209 | 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /PathFindingForObjC-Example/iOS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PathFindingForObjC-Example 4 | // 5 | // Created by JasioWoo on 14/10/28. 6 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PathFindingForObjC.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'PathFindingForObjC' 3 | s.version = '1.0.0' 4 | s.license = 'MIT' 5 | s.homepage = 'https://github.com/wbcyclist/PathFindingForObjC' 6 | s.author = { 'Jasio Woo' => 'wbcyclist@gmail.com' } 7 | s.summary = 'A Comprehensive PathFinding Library for Objective-C' 8 | s.screenshots = [ "https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/master/demo/PathFinding_ScreenShot.png" ] 9 | s.source = { :git => 'https://github.com/wbcyclist/PathFindingForObjC.git', :tag => s.version } 10 | s.requires_arc = true 11 | s.ios.deployment_target = '7.0' 12 | s.osx.deployment_target = '10.9' 13 | s.ios.frameworks = 'UIKit' 14 | s.osx.frameworks = 'AppKit' 15 | 16 | s.source_files = 'PathFindingForObjC/**/*.{h,m}' 17 | 18 | end 19 | -------------------------------------------------------------------------------- /PathFindingForObjC/PFTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFTypes.h 3 | // 4 | // Created by JasioWoo on 15/3/13. 5 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #ifndef PathFindingForObjC_PFTypes_h 9 | #define PathFindingForObjC_PFTypes_h 10 | 11 | #import "TargetConditionals.h" 12 | 13 | #if TARGET_OS_IPHONE 14 | #import 15 | #define PF_CGPointToNSValue(p) [NSValue valueWithCGPoint:p] 16 | #define PF_NSValueToCGPoint(v) v.CGPointValue 17 | #else 18 | #import 19 | #define PF_CGPointToNSValue(p) [NSValue valueWithPoint:p] 20 | #define PF_NSValueToCGPoint(v) v.pointValue 21 | #endif 22 | 23 | #define PF_ConvertToMatrixPoint(p, t, o) do{ p.x = (int)((p.x+o.x)/t.width); p.y = (int)((p.y+o.y)/t.height);}while(0) 24 | #define PF_ConvertToOriginPoint(p, t, o) do{ p.x = p.x*t.width - o.x + t.width/2.0; p.y = p.y*t.height - o.y + t.height/2.0;}while(0) 25 | 26 | #ifndef PF_DEBUG 27 | #ifdef DEBUG 28 | #define PF_DEBUG 1 29 | #else 30 | #define PF_DEBUG 0 31 | #endif 32 | #endif 33 | 34 | /** 35 | * http://qiao.github.io/PathFinding.js/visual/ 36 | */ 37 | typedef enum { 38 | PathfindingAlgorithm_AStar = 0, // default 39 | PathfindingAlgorithm_BestFirstSearch, // 40 | PathfindingAlgorithm_Dijkstra, // 41 | PathfindingAlgorithm_JumpPointSearch, // 42 | PathfindingAlgorithm_BreadthFirstSearch, // queue 43 | // PathfindingAlgorithm_DepthFirstSearch, // stack 44 | 45 | PathfindingAlgorithm_BiAStar, // 46 | PathfindingAlgorithm_BiBestFirst, // 47 | PathfindingAlgorithm_BiDijkstra, // 48 | PathfindingAlgorithm_BiBreadthFirst, // 49 | 50 | PathfindingAlgorithm_IDAStar // 51 | } PathfindingAlgorithm; 52 | 53 | 54 | typedef enum { 55 | /** 56 | * On a square grid that allows 4 directions of movement, use Manhattan distance (L1). 57 | * On a square grid that allows 8 directions of movement, use Diagonal distance (L∞). 58 | * On a square grid that allows any direction of movement, you might or might not want Euclidean distance (L2). 59 | If A* is finding paths on the grid but you are allowing movement not on the grid, you may want to consider other representations of the map. 60 | * On a hexagon grid that allows 6 directions of movement, use Manhattan distance adapted to hexagonal grids. 61 | * 62 | * See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#heuristics-for-grid-maps for more details. 63 | */ 64 | HeuristicTypeManhattan = 0, // default 65 | HeuristicTypeEuclidean, 66 | HeuristicTypeOctile, 67 | HeuristicTypeChebyshev 68 | } HeuristicType; 69 | 70 | 71 | typedef enum { 72 | DiagonalMovement_Always = 0, 73 | DiagonalMovement_Never, 74 | DiagonalMovement_IfAtMostOneObstacle, 75 | DiagonalMovement_OnlyWhenNoObstacles 76 | } DiagonalMovement; 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /PathFindingForObjC/PathFinding.h: -------------------------------------------------------------------------------- 1 | // 2 | // PathFinding.h 3 | // 4 | // Created by JasioWoo on 14/10/26. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PFTypes.h" 9 | #import "PFNode.h" 10 | #import "PFGrid.h" 11 | 12 | @interface PathFinding : NSObject 13 | 14 | @property (nonatomic) HeuristicType heuristicType; 15 | @property (nonatomic) DiagonalMovement movementType; 16 | 17 | @property (nonatomic) int weight; 18 | @property (nonatomic) CGSize mapSize; 19 | @property (nonatomic) CGSize tileSize; 20 | @property (nonatomic) CGPoint orginPoint; 21 | @property (nonatomic) CGPoint startPoint; 22 | @property (nonatomic) CGPoint endPoint; 23 | 24 | - (instancetype)initWithMapSize:(CGSize)mapSize tileSize:(CGSize)tileSize coordsOrgin:(CGPoint)orginPoint; 25 | 26 | /** 27 | * position of the immobile obstacle, like a wall. 28 | */ 29 | - (void)addBlockTilePosition:(CGPoint)point; 30 | /// @param points = @[[NSValue valueWithCGPoint:CGPointMake(x, y)]] 31 | - (void)addBlockTilePositions:(NSArray*)points; 32 | 33 | /** 34 | * position of the mobile obstacle, like a moving car. (! Clear DynamicBlock after each invoke findPathing:IsConvertToOriginCoords: ) 35 | */ 36 | - (void)addDynamicBlockTilePosition:(CGPoint)point; 37 | 38 | 39 | - (void)clearBlockTiles; 40 | 41 | /// @return An PFNode Array. 42 | - (NSArray *)findPathing:(PathfindingAlgorithm)alg IsConvertToOriginCoords:(BOOL)isConvert; 43 | - (NSArray *)findPathing:(PathfindingAlgorithm)alg IsConvertToOriginCoords:(BOOL)isConvert trackFinding:(NSMutableArray**)trackArrForTest; 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /PathFindingForObjC/PathFinding.m: -------------------------------------------------------------------------------- 1 | // 2 | // PathFinding.m 3 | // 4 | // Created by JasioWoo on 14/10/26. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PathFinding.h" 9 | 10 | #import "AStarFinder.h" 11 | #import "BestFirstFinder.h" 12 | #import "DijkstraFinder.h" 13 | #import "BreadthFirstFinder.h" 14 | #import "JumpPointFinderBase.h" 15 | 16 | #import "BiAStarFinder.h" 17 | #import "BiBestFirstFinder.h" 18 | #import "BiDijkstraFinder.h" 19 | #import "BiBreadthFirstFinder.h" 20 | 21 | 22 | 23 | @interface PathFinding () 24 | @property (nonatomic, strong) NSMutableArray *blockTiles; 25 | @property (nonatomic, strong) NSMutableArray *dynamicBlockTiles; 26 | @end 27 | 28 | @implementation PathFinding { 29 | 30 | } 31 | 32 | #pragma mark - 33 | 34 | - (instancetype)initWithMapSize:(CGSize)mapSize tileSize:(CGSize)tileSize coordsOrgin:(CGPoint)orginPoint { 35 | self = [super init]; 36 | if (self) { 37 | self.movementType = DiagonalMovement_Always; 38 | self.heuristicType = HeuristicTypeManhattan; 39 | 40 | self.weight = 1; 41 | self.mapSize = mapSize; 42 | self.tileSize = tileSize; 43 | self.orginPoint = orginPoint; 44 | 45 | } 46 | return self; 47 | } 48 | 49 | - (void)dealloc { 50 | // debugMethod(); 51 | } 52 | 53 | 54 | #pragma mark - 55 | - (NSMutableArray *)blockTiles { 56 | if (!_blockTiles) { 57 | _blockTiles = [NSMutableArray array]; 58 | } 59 | return _blockTiles; 60 | } 61 | - (NSMutableArray *)dynamicBlockTiles { 62 | if (!_dynamicBlockTiles) { 63 | _dynamicBlockTiles = [NSMutableArray array]; 64 | } 65 | return _dynamicBlockTiles; 66 | } 67 | 68 | - (void)addBlockTilePosition:(CGPoint)point { 69 | [self.blockTiles addObject:PF_CGPointToNSValue(point)]; 70 | } 71 | - (void)addDynamicBlockTilePosition:(CGPoint)point { 72 | [self.dynamicBlockTiles addObject:PF_CGPointToNSValue(point)]; 73 | } 74 | 75 | - (void)addBlockTilePositions:(NSArray *)points { 76 | [self.blockTiles addObjectsFromArray:points]; 77 | } 78 | 79 | - (void)clearBlockTiles { 80 | if (_blockTiles) { 81 | [_blockTiles removeAllObjects]; 82 | } 83 | if (_dynamicBlockTiles) { 84 | [_dynamicBlockTiles removeAllObjects]; 85 | } 86 | } 87 | 88 | 89 | - (NSArray *)findPathing:(PathfindingAlgorithm)alg IsConvertToOriginCoords:(BOOL)isConvert { 90 | return [self findPathing:alg IsConvertToOriginCoords:isConvert trackFinding:nil]; 91 | } 92 | 93 | - (NSArray *)findPathing:(PathfindingAlgorithm)alg IsConvertToOriginCoords:(BOOL)isConvert trackFinding:(NSMutableArray *__autoreleasing *)trackArrForTest { 94 | unsigned int column = self.mapSize.width/self.tileSize.width; 95 | unsigned int row = self.mapSize.height/self.tileSize.height; 96 | 97 | NSMutableArray *blockPoints = [NSMutableArray arrayWithCapacity:self.blockTiles.count]; 98 | for (NSValue *value in self.blockTiles) { 99 | CGPoint point = PF_NSValueToCGPoint(value); 100 | PF_ConvertToMatrixPoint(point, self.tileSize, self.orginPoint); 101 | [blockPoints addObject:PF_CGPointToNSValue(point)]; 102 | } 103 | NSArray *dyArr = self.dynamicBlockTiles; 104 | self.dynamicBlockTiles = nil; 105 | for (NSValue *value in dyArr) { 106 | CGPoint point = PF_NSValueToCGPoint(value); 107 | PF_ConvertToMatrixPoint(point, self.tileSize, self.orginPoint); 108 | [blockPoints addObject:PF_CGPointToNSValue(point)]; 109 | } 110 | 111 | 112 | CGPoint sPoint = self.startPoint; 113 | // sPoint.x = (int)((sPoint.x+self.orginPoint.x)/self.tileSize.width); 114 | // sPoint.y = (int)((sPoint.y+self.orginPoint.y)/self.tileSize.height); 115 | PF_ConvertToMatrixPoint(sPoint, self.tileSize, self.orginPoint); 116 | 117 | CGPoint ePoint = self.endPoint; 118 | PF_ConvertToMatrixPoint(ePoint, self.tileSize, self.orginPoint); 119 | 120 | PFGrid *grid = [[PFGrid alloc] initWithColumn:column andRow:row andBlockPoints:blockPoints]; 121 | PFNode *startNode = [grid getNodeAtX:sPoint.x andY:sPoint.y]; 122 | PFNode *endNode = [grid getNodeAtX:ePoint.x andY:ePoint.y]; 123 | if (!startNode || !endNode) { 124 | // NSLog(@"not found startPoint(%@) or endPoint(%@)", NSStringFromCGPoint(self.startPoint), NSStringFromCGPoint(self.endPoint)); 125 | // NSLog(@"mapSize=%@, tileSize=%@, orginPoint=%@", NSStringFromCGSize(self.mapSize), NSStringFromCGSize(self.tileSize), NSStringFromCGPoint(self.orginPoint)); 126 | return nil; 127 | } else if (startNode.x==endNode.x && startNode.y==endNode.y) { 128 | return @[PF_CGPointToNSValue(self.startPoint), PF_CGPointToNSValue(self.endPoint)]; 129 | } 130 | 131 | BaseFinder *finder = [self getFinder:alg]; 132 | finder.heuristicType = self.heuristicType; 133 | finder.movementType = self.movementType; 134 | 135 | finder.weight = self.weight; 136 | NSMutableArray *trackArr = trackArrForTest?*trackArrForTest:nil; 137 | NSArray *result = [finder findPathInStartNode:startNode toEndNode:endNode withGrid:grid trackFinding:&trackArr]; 138 | 139 | // convert to origin coords 140 | if (isConvert) { 141 | // convert track operation 142 | for (NSObject *obj in trackArr) { 143 | if ([obj isKindOfClass:[PFNode class]]) { 144 | PFNode *node = (PFNode *)obj; 145 | CGPoint originPoint = CGPointMake(node.x, node.y); 146 | PF_ConvertToOriginPoint(originPoint, self.tileSize, self.orginPoint); 147 | node.originPoint = originPoint; 148 | } else if ([obj isKindOfClass:[NSMutableArray class]]) { 149 | NSArray *arr = (NSArray*)obj; 150 | for (PFNode *node in arr) { 151 | CGPoint originPoint = CGPointMake(node.x, node.y); 152 | PF_ConvertToOriginPoint(originPoint, self.tileSize, self.orginPoint); 153 | node.originPoint = originPoint; 154 | } 155 | } 156 | } 157 | // convert found path 158 | if (result) { 159 | PFNode *firstNode = [result firstObject]; 160 | PFNode *lastNode = [result lastObject]; 161 | firstNode.originPoint = self.startPoint; 162 | lastNode.originPoint = self.endPoint; 163 | for (int i=1; i 9 | 10 | /** 11 | * @description A collection of heuristic functions. 12 | */ 13 | @interface Heuristic : NSObject 14 | - (float)performAlgorithmWithX:(float)dx andY:(float)dy; 15 | @end 16 | 17 | /** 18 | * Manhattan distance. 19 | * @param {number} dx - Difference in x. 20 | * @param {number} dy - Difference in y. 21 | * @return {number} dx + dy 22 | */ 23 | @interface Manhattan : Heuristic 24 | @end 25 | 26 | /** 27 | * Euclidean distance. 28 | * @param {number} dx - Difference in x. 29 | * @param {number} dy - Difference in y. 30 | * @return {number} sqrt(dx * dx + dy * dy) 31 | */ 32 | @interface Euclidean : Heuristic 33 | @end 34 | 35 | /** 36 | * Octile distance. 37 | * @param {number} dx - Difference in x. 38 | * @param {number} dy - Difference in y. 39 | * @return {number} sqrt(dx * dx + dy * dy) for grids 40 | */ 41 | @interface Octile : Heuristic 42 | @end 43 | 44 | /** 45 | * Chebyshev distance. 46 | * @param {number} dx - Difference in x. 47 | * @param {number} dy - Difference in y. 48 | * @return {number} max(dx, dy) 49 | */ 50 | @interface Chebyshev : Heuristic 51 | @end 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/Heuristic.m: -------------------------------------------------------------------------------- 1 | // 2 | // Heuristic.m 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "Heuristic.h" 9 | 10 | static float F = 1.4-1;//M_SQRT2 - 1; 11 | 12 | @implementation Heuristic 13 | - (float)performAlgorithmWithX:(float)dx andY:(float)dy { 14 | // Override 15 | return 0; 16 | } 17 | @end 18 | 19 | @implementation Manhattan 20 | - (float)performAlgorithmWithX:(float)dx andY:(float)dy { 21 | return dx + dy; 22 | } 23 | @end 24 | 25 | @implementation Euclidean 26 | - (float)performAlgorithmWithX:(float)dx andY:(float)dy { 27 | return sqrtf(dx * dx + dy * dy); 28 | } 29 | @end 30 | 31 | @implementation Octile 32 | - (float)performAlgorithmWithX:(float)dx andY:(float)dy { 33 | return (dx < dy) ? F * dx + dy : F * dy + dx; 34 | } 35 | @end 36 | 37 | @implementation Chebyshev 38 | - (float)performAlgorithmWithX:(float)dx andY:(float)dy { 39 | return MAX(dx, dy); 40 | } 41 | @end 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/PFGrid.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGrid.h 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PFTypes.h" 9 | 10 | @class PFNode; 11 | 12 | 13 | /** 14 | * The Grid class, which serves as the encapsulation of the layout of the nodes. 15 | */ 16 | @interface PFGrid : NSObject 17 | 18 | /// The number of columns of the grid. 19 | @property (nonatomic) unsigned int column; 20 | /// The number of rows of the grid. 21 | @property (nonatomic) unsigned int row; 22 | 23 | /// A 2D array of int. 0-1(representing the walkable status of the nodes(0 for walkable)) 24 | @property (nonatomic) int **matrix; 25 | /// A 2D array of nodes. 26 | @property (nonatomic, retain, readonly) NSArray *nodes; 27 | 28 | 29 | - (instancetype) initWithColumn:(unsigned int)col andRow:(unsigned int)row andBlockPoints:(NSArray*)blockPoints; 30 | 31 | 32 | - (PFNode *)getNodeAtX:(int)x andY:(int)y; 33 | 34 | /** 35 | * Determine whether the node at the given position is walkable. 36 | * (Also returns false if the position is outside the grid.) 37 | * @param {number} x - The x coordinate of the node. 38 | * @param {number} y - The y coordinate of the node. 39 | * @return {boolean} - The walkability of the node. 40 | */ 41 | - (BOOL)isWalkableAtX:(int)x andY:(int)y; 42 | 43 | /** 44 | * Set whether the node on the given position is walkable. 45 | * NOTE: throws exception if the coordinate is not inside the grid. 46 | * @param {number} x - The x coordinate of the node. 47 | * @param {number} y - The y coordinate of the node. 48 | * @param {boolean} walkable - Whether the position is walkable. 49 | */ 50 | - (void)setWalkableAtX:(int)x andY:(int)y andWalkable:(BOOL)walkable; 51 | 52 | /** 53 | * Get the neighbors of the given node. 54 | * 55 | * offsets diagonalOffsets: 56 | * +---+---+---+ +---+---+---+ 57 | * | | 0 | | | 0 | | 1 | 58 | * +---+---+---+ +---+---+---+ 59 | * | 3 | | 1 | | | | | 60 | * +---+---+---+ +---+---+---+ 61 | * | | 2 | | | 3 | | 2 | 62 | * +---+---+---+ +---+---+---+ 63 | * 64 | * When allowDiagonal is true, if offsets[i] is valid, then 65 | * diagonalOffsets[i] and 66 | * diagonalOffsets[(i + 1) % 4] is valid. 67 | * @param {Node} node 68 | * @param {DiagonalMovement} diagonalMovement 69 | */ 70 | - (NSArray *)getNeighborsWith:(PFNode*)node diagonalMovement:(DiagonalMovement)movementType; 71 | 72 | 73 | /// test 74 | - (void)printFoundPath:(NSArray *)path; 75 | 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/PFGrid.m: -------------------------------------------------------------------------------- 1 | // 2 | // FGrid.m 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PFGrid.h" 9 | 10 | #import "PFNode.h" 11 | //#import 12 | 13 | @interface PFGrid () 14 | 15 | 16 | @end 17 | 18 | @implementation PFGrid { 19 | NSMutableArray *_nodes; 20 | 21 | } 22 | 23 | - (instancetype)initWithColumn:(unsigned int)col andRow:(unsigned int)row andBlockPoints:(NSArray*)blockPoints{ 24 | self = [super init]; 25 | if (self) { 26 | if (col==0 || row==0) { 27 | // NSLog(@"Grid Size Must Be > 0. (col=%d, row=%d)", col, row); 28 | return nil; 29 | } 30 | _column = col; 31 | _row = row; 32 | _matrix = (int**)malloc(_row * sizeof(int*)); 33 | NSMutableArray *nodeArr = [NSMutableArray arrayWithCapacity:_row]; 34 | 35 | PFNode *node = nil;; 36 | for (int i=0; i<_row; i++) { 37 | _matrix[i] = (int*)malloc(_column * sizeof(int)); 38 | memset(_matrix[i], 0, _column * sizeof(int)); 39 | 40 | nodeArr[i] = [NSMutableArray arrayWithCapacity:_column]; 41 | 42 | for (int j=0; j<_column; j++) { 43 | node = [[PFNode alloc] init]; 44 | node.x = j; 45 | node.y = i; 46 | node.f = 0; 47 | node.g = 0; 48 | node.h = 0; 49 | node.walkable = YES; 50 | node.parent = nil; 51 | nodeArr[i][j] = node; 52 | } 53 | } 54 | _nodes = nodeArr; 55 | 56 | // setup block zone 57 | CGPoint blockPoint; 58 | for (NSValue *value in blockPoints) { 59 | blockPoint = PF_NSValueToCGPoint(value); 60 | node = [self getNodeAtX:blockPoint.x andY:blockPoint.y]; 61 | if (node) { 62 | node.walkable = NO; 63 | _matrix[node.y][node.x] = 1; 64 | } 65 | } 66 | 67 | // NSLog(@"PFNode size(memory): %zd", class_getInstanceSize([PFNode class])); 68 | // NSLog(@"PFGrid size(memory): %zd", class_getInstanceSize([PFGrid class])); 69 | // [self printMatrix]; 70 | } 71 | return self; 72 | } 73 | 74 | - (void)printMatrix { 75 | for (int i=_row-1; i>=0; i--) { 76 | for(int j=0; j<_column; j++) { 77 | printf(" %d", _matrix[i][j]); 78 | } 79 | printf("\n"); 80 | } 81 | } 82 | 83 | - (void)printFoundPath:(NSArray *)path { 84 | NSUInteger l = path.count; 85 | for (int i=0; i=0; i--) { 101 | for(int j=0; j<_column; j++) { 102 | if (_matrix[i][j]==1) { 103 | // block 104 | printf(" \U0001F51E"); 105 | } else if (_matrix[i][j]==2) { 106 | // start 107 | printf(" \U0001F64F"); 108 | } else if (_matrix[i][j]==3) { 109 | // path 110 | printf(" \U0001F497"); 111 | } else if (_matrix[i][j]==4) { 112 | // end 113 | printf(" \U0001F645"); 114 | } else { 115 | printf(" \U0001F50E"); 116 | } 117 | // printf(" %d", _matrix[i][j]); 118 | } 119 | printf("\n"); 120 | } 121 | } 122 | 123 | 124 | - (void)dealloc { 125 | // debugMethod(); 126 | [_nodes removeAllObjects]; 127 | _nodes = nil; 128 | 129 | // release matrix 130 | for(int i=0; i<_row; i++) { 131 | memset(_matrix[i], 0, sizeof(int)); 132 | free(_matrix[i]); 133 | } 134 | free(_matrix); 135 | _matrix = NULL; 136 | } 137 | 138 | 139 | - (PFNode *)getNodeAtX:(int)x andY:(int)y { 140 | if (x<0 || x>self.column-1 141 | || y<0 || y>self.row-1) { 142 | return nil; 143 | } 144 | return self.nodes[y][x]; 145 | } 146 | 147 | - (BOOL)isWalkableAtX:(int)x andY:(int)y { 148 | return [self getNodeAtX:x andY:y].walkable; 149 | } 150 | 151 | - (void)setWalkableAtX:(int)x andY:(int)y andWalkable:(BOOL)walkable { 152 | [self getNodeAtX:x andY:y].walkable = walkable; 153 | } 154 | 155 | - (NSArray *)getNeighborsWith:(PFNode*)node diagonalMovement:(DiagonalMovement)movementType { 156 | NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity:4]; 157 | int x = node.x; 158 | int y = node.y; 159 | BOOL s0 = NO; 160 | BOOL s1 = NO; 161 | BOOL s2 = NO; 162 | BOOL s3 = NO; 163 | BOOL d0 = NO; 164 | BOOL d1 = NO; 165 | BOOL d2 = NO; 166 | BOOL d3 = NO; 167 | 168 | // ↑ 169 | if ([self isWalkableAtX:x andY:y-1]) { 170 | [neighbors addObject:self.nodes[y-1][x]]; 171 | s0 = YES; 172 | } 173 | // → 174 | if ([self isWalkableAtX:x+1 andY:y]) { 175 | [neighbors addObject:self.nodes[y][x+1]]; 176 | s1 = YES; 177 | } 178 | // ↓ 179 | if ([self isWalkableAtX:x andY:y+1]) { 180 | [neighbors addObject:self.nodes[y+1][x]]; 181 | s2 = YES; 182 | } 183 | // ← 184 | if ([self isWalkableAtX:x-1 andY:y]) { 185 | [neighbors addObject:self.nodes[y][x-1]]; 186 | s3 = YES; 187 | } 188 | 189 | switch (movementType) { 190 | case DiagonalMovement_Always: 191 | d0 = d1 = d2 = d3 = YES; 192 | break; 193 | 194 | case DiagonalMovement_Never: 195 | return neighbors; 196 | 197 | case DiagonalMovement_OnlyWhenNoObstacles: 198 | d0 = s3 && s0; 199 | d1 = s0 && s1; 200 | d2 = s1 && s2; 201 | d3 = s2 && s3; 202 | break; 203 | 204 | case DiagonalMovement_IfAtMostOneObstacle: 205 | d0 = s3 || s0; 206 | d1 = s0 || s1; 207 | d2 = s1 || s2; 208 | d3 = s2 || s3; 209 | break; 210 | 211 | default: 212 | NSAssert(NO, @"Incorrect value of diagonalMovement"); 213 | break; 214 | } 215 | 216 | // ↖ 217 | if (d0 && [self isWalkableAtX:x-1 andY:y-1]) { 218 | [neighbors addObject:self.nodes[y-1][x-1]]; 219 | } 220 | // ↗ 221 | if (d1 && [self isWalkableAtX:x+1 andY:y-1]) { 222 | [neighbors addObject:self.nodes[y-1][x+1]]; 223 | } 224 | // ↘ 225 | if (d2 && [self isWalkableAtX:x+1 andY:y+1]) { 226 | [neighbors addObject:self.nodes[y+1][x+1]]; 227 | } 228 | // ↙ 229 | if (d3 && [self isWalkableAtX:x-1 andY:y+1]) { 230 | [neighbors addObject:self.nodes[y+1][x-1]]; 231 | } 232 | 233 | return neighbors; 234 | } 235 | 236 | 237 | 238 | 239 | 240 | @end 241 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/PFNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // FNode.h 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | /** 12 | * A node in grid. 13 | * This class holds some basic information about a node and custom 14 | * attributes may be added, depending on the algorithms' needs. 15 | */ 16 | @interface PFNode : NSObject 17 | 18 | @property (nonatomic) float f; 19 | @property (nonatomic) float g; 20 | @property (nonatomic) float h; 21 | 22 | /// special costWeight of pass through. 23 | @property (nonatomic) float cost; 24 | 25 | /// The x coordinate of the node on the grid. (X-axis In Matrix Coords) 26 | @property (nonatomic) int x; 27 | /// The y coordinate of the node on the grid. (Y-axis In Matrix Coords) 28 | @property (nonatomic) int y; 29 | /// Whether this node can be walked through. 30 | @property (nonatomic) BOOL walkable; 31 | /** 32 | * @param 0 is out of the OpenList 33 | * @param 1 is in the startOpenList 34 | * @param 2 is in the endOpenList 35 | */ 36 | @property (nonatomic) uint8_t opened; 37 | @property (nonatomic) BOOL closed; 38 | @property (nonatomic) BOOL tested; 39 | 40 | /** 41 | * direction for vectorFieldGrid 42 | * 43 | * +---+---+---+ 44 | * | 1 | 2 | 3 | 45 | * +---+---+---+ 46 | * | 8 | 0 | 4 | 47 | * +---+---+---+ 48 | * | 7 | 6 | 5 | 49 | * +---+---+---+ 50 | * 51 | */ 52 | @property (nonatomic) int direction; 53 | /** 54 | * vector for vectorFieldGrid 55 | */ 56 | @property (nonatomic, assign) CGVector vector; 57 | 58 | /// Point In Origin Coords 59 | @property (nonatomic) CGPoint originPoint; 60 | 61 | @property (nonatomic, weak) PFNode *parent; 62 | 63 | - (instancetype)initWithX:(int)x andY:(int)y; 64 | - (NSComparisonResult)descFWeightSort:(PFNode *)anObject; 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/PFNode.m: -------------------------------------------------------------------------------- 1 | // 2 | // FNode.m 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PFNode.h" 9 | 10 | @implementation PFNode 11 | 12 | - (instancetype)init { 13 | self = [super init]; 14 | if (self) { 15 | self.f = 0; 16 | self.g = 0; 17 | self.h = 0; 18 | self.cost = 0; 19 | self.walkable = YES; 20 | self.opened = 0; 21 | self.closed = NO; 22 | self.tested = NO; 23 | self.direction = 0; 24 | } 25 | return self; 26 | } 27 | 28 | - (instancetype)initWithX:(int)x andY:(int)y { 29 | self = [self init]; 30 | if (self) { 31 | self.x = x; 32 | self.y = y; 33 | } 34 | return self; 35 | } 36 | 37 | // 用于寻路时跟踪节点的状态 38 | - (id)copyWithZone:(NSZone *)zone { 39 | PFNode *copy = [[PFNode alloc] init]; 40 | copy.f = self.f; 41 | copy.g = self.g; 42 | copy.h = self.h; 43 | copy.x = self.x; 44 | copy.y = self.y; 45 | copy.cost = self.cost; 46 | copy.walkable = self.walkable; 47 | copy.opened = self.opened; 48 | copy.closed = self.closed; 49 | copy.tested = self.tested; 50 | copy.originPoint = self.originPoint; 51 | copy.direction = self.direction; 52 | copy.vector = self.vector; 53 | return copy; 54 | } 55 | 56 | - (NSComparisonResult)descFWeightSort:(PFNode *)anObject { 57 | if (self.f > anObject.f) { 58 | return NSOrderedAscending; 59 | } else if (self.f < anObject.f) { 60 | return NSOrderedDescending; 61 | } else { 62 | return NSOrderedSame; 63 | } 64 | } 65 | 66 | 67 | //- (void)dealloc { 68 | // debugMethod(); 69 | //} 70 | 71 | - (NSString *)description { 72 | NSString *score = [NSString stringWithFormat:@"F:%.1f G:%.1f H:%.1f Opened:%d Closed:%@", self.f, self.g, self.h, self.opened, self.closed?@"YES":@"NO"]; 73 | return [NSString stringWithFormat:@"{%d, %d} %@", self.x, self.y, score]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/PFUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFUtil.h 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | 11 | @class PFGrid; 12 | @class PFNode; 13 | 14 | 15 | @interface PFUtil : NSObject 16 | 17 | /** 18 | * Backtrace according to the parent records and return the path. 19 | * (including both start and end nodes) 20 | * @param {Node} node End node 21 | * @return Array the path 22 | */ 23 | + (NSArray *)backtrace:(PFNode*)node; 24 | 25 | /** 26 | * Backtrace from start and end node, and return the path. 27 | * (including both start and end nodes) 28 | * @param {Node} 29 | * @param {Node} 30 | * @return Array the path 31 | */ 32 | + (NSArray *)biBacktraceWithNodeA:(PFNode*)nodeA andNodeB:(PFNode*)nodeB; 33 | 34 | /** 35 | * Compute the length of the path. 36 | * @param Array path The path 37 | * @return {number} The length of the path 38 | */ 39 | + (float)pathLength:(NSArray*)path; 40 | 41 | /** 42 | * Given the start and end coordinates, return all the coordinates lying 43 | * on the line formed by these coordinates, based on Bresenham's algorithm. 44 | * http://en.wikipedia.org/wiki/Bresenham's_line_algorithm#Simplification 45 | * @param {number} x0 Start x coordinate 46 | * @param {number} y0 Start y coordinate 47 | * @param {number} x1 End x coordinate 48 | * @param {number} y1 End y coordinate 49 | * @return Array The coordinates on the line 50 | */ 51 | + (NSArray *)interpolate:(int)x0 :(int)y0 :(int)x1 :(int)y1; 52 | 53 | /** 54 | * Given a compressed path, return a new path that has all the segments 55 | * in it interpolated. 56 | * @param Array path The path 57 | * @return Array expanded path 58 | */ 59 | + (NSArray *)expandPath:(NSArray*)path; 60 | 61 | /** 62 | * Smoothen the give path. 63 | * The original path will not be modified; a new path will be returned. 64 | * @param {PF.Grid} grid 65 | * @param Array path The path 66 | */ 67 | + (NSArray *)smoothenPathWithGrid:(PFGrid*)grid andPath:(NSArray*)path; 68 | 69 | /** 70 | * Compress a path, remove redundant nodes without altering the shape 71 | * The original path is not modified 72 | * @param Array path The path 73 | * @return Array The compressed path 74 | */ 75 | + (NSArray *)compressPath:(NSArray*)path; 76 | 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /PathFindingForObjC/core/PFUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFUtil.m 3 | // 4 | // Created by JasioWoo on 14/10/27. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PFUtil.h" 9 | #import "PFNode.h" 10 | #import "PFGrid.h" 11 | 12 | @implementation PFUtil 13 | 14 | + (NSArray *)backtrace:(PFNode *)node { 15 | NSMutableArray *path = [NSMutableArray array]; 16 | [path addObject:node]; 17 | while (node.parent) { 18 | node = node.parent; 19 | [path addObject:node]; 20 | } 21 | 22 | return [[path reverseObjectEnumerator] allObjects]; 23 | } 24 | 25 | + (NSArray *)biBacktraceWithNodeA:(PFNode *)nodeA andNodeB:(PFNode *)nodeB { 26 | NSArray *pathA = [PFUtil backtrace:nodeA]; 27 | NSArray *pathB = [PFUtil backtrace:nodeB]; 28 | 29 | NSMutableArray *mergePath = [NSMutableArray arrayWithArray:pathA]; 30 | [mergePath addObjectsFromArray:[pathB reverseObjectEnumerator].allObjects]; 31 | return mergePath; 32 | } 33 | 34 | + (float)pathLength:(NSArray*)path { 35 | float sum = 0; 36 | int i, dx, dy; 37 | PFNode *aNode, *bNode; 38 | for (i=1; i -dy) { 69 | err = err - dy; 70 | x0 = x0 + sx; 71 | } 72 | if (e2 < dx) { 73 | err = err + dx; 74 | y0 = y0 + sy; 75 | } 76 | } 77 | 78 | return line; 79 | } 80 | 81 | + (NSArray *)expandPath:(NSArray*)path { 82 | NSMutableArray *expanded = [NSMutableArray array]; 83 | NSUInteger len = [path count]; 84 | if (len < 2) { 85 | return expanded; 86 | } 87 | 88 | PFNode *aNode, *bNode; 89 | for (int i = 0; i < len - 1; ++i) { 90 | aNode = path[i]; 91 | bNode = path[i+1]; 92 | 93 | NSArray *interpolated = [PFUtil interpolate:aNode.x :aNode.y :bNode.x :bNode.y]; 94 | if (interpolated.count >0) { 95 | [expanded addObjectsFromArray:interpolated]; 96 | } 97 | } 98 | [expanded addObject:path[len - 1]]; 99 | return expanded; 100 | } 101 | 102 | + (NSArray *)smoothenPathWithGrid:(PFGrid*)grid andPath:(NSArray*)path { 103 | 104 | NSUInteger len = [path count]; 105 | if (len<2) { 106 | return path; 107 | } 108 | 109 | PFNode *currentNode = path[0]; 110 | PFNode *nextNode = nil; 111 | PFNode *lastNode = nil; 112 | 113 | NSArray *line = nil; 114 | NSMutableArray *newPath = [NSMutableArray array]; 115 | [newPath addObject:currentNode]; 116 | 117 | for (int i = 2; i < len; ++i) { 118 | nextNode = path[i]; 119 | line = [PFUtil interpolate:currentNode.x :currentNode.y :nextNode.x :nextNode.y]; 120 | 121 | BOOL blocked = NO; 122 | for (int j = 1; j < [line count]; ++j) { 123 | PFNode *testNode = line[j]; 124 | 125 | if (![grid isWalkableAtX:testNode.x andY:testNode.y]) { 126 | blocked = YES; 127 | break; 128 | } 129 | } 130 | 131 | if (blocked) { 132 | lastNode = path[i - 1]; 133 | [newPath addObject:lastNode]; 134 | 135 | currentNode = lastNode; 136 | } 137 | } 138 | [newPath addObject:[path lastObject]]; 139 | 140 | return newPath; 141 | 142 | } 143 | 144 | + (NSArray *)compressPath:(NSArray*)path { 145 | 146 | // nothing to compress 147 | if([path count] < 3) { 148 | return path; 149 | } 150 | NSMutableArray *compressed = [NSMutableArray array]; 151 | 152 | PFNode *lastNode = path[0]; 153 | PFNode *nextNode = path[1]; 154 | 155 | int dx = nextNode.x - lastNode.x; // direction between the two points 156 | int dy = nextNode.y - lastNode.y; // direction between the two points 157 | int ldx, ldy; 158 | // normalize the direction 159 | int sq = sqrt(dx*dx + dy*dy); 160 | dx /= sq; 161 | dy /= sq; 162 | 163 | // start the new path 164 | [compressed addObject:lastNode]; 165 | 166 | for(int i = 2; i < [path count]; i++) { 167 | // store the last point 168 | lastNode = nextNode; 169 | 170 | // store the last direction 171 | ldx = dx; 172 | ldy = dy; 173 | 174 | // next point 175 | nextNode = path[i]; 176 | 177 | // next direction 178 | dx = nextNode.x - lastNode.x; 179 | dy = nextNode.y - lastNode.y; 180 | 181 | // normalize 182 | sq = sqrt(dx*dx + dy*dy); 183 | dx /= sq; 184 | dy /= sq; 185 | 186 | // if the direction has changed, store the point 187 | if ( dx != ldx || dy != ldy ) { 188 | [compressed addObject:lastNode]; 189 | } 190 | } 191 | 192 | // store the last point 193 | [compressed addObject:nextNode]; 194 | 195 | return compressed; 196 | } 197 | 198 | 199 | @end 200 | -------------------------------------------------------------------------------- /PathFindingForObjC/fieldGrid/VectorFieldGrid.h: -------------------------------------------------------------------------------- 1 | // 2 | // VectorFieldGrid.h 3 | // 4 | // Created by JasioWoo on 14/11/4. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "PFTypes.h" 10 | #import "PFNode.h" 11 | 12 | 13 | @interface VectorFieldGrid : NSObject 14 | /// The number of columns of the grid. 15 | @property (nonatomic) unsigned int column; 16 | /// The number of rows of the grid. 17 | @property (nonatomic) unsigned int row; 18 | /// A 2D array of nodes. 19 | @property (nonatomic, retain, readonly) NSArray *nodes; 20 | 21 | @property (nonatomic) CGSize mapSize; 22 | @property (nonatomic) CGSize tileSize; 23 | @property (nonatomic) CGPoint orginPoint; 24 | 25 | @property (nonatomic, assign) CGPoint targetPoint; 26 | 27 | - (instancetype)initWithMapSize:(CGSize)mapSize tileSize:(CGSize)tileSize coordsOrgin:(CGPoint)orginPoint; 28 | 29 | - (BOOL)isWalkableAtX:(int)x andY:(int)y; 30 | - (PFNode *)getNodeAtX:(int)x andY:(int)y; 31 | - (void)setWalkableAtX:(int)x andY:(int)y andWalkable:(BOOL)walkable; 32 | 33 | - (void)clearBlockTiles; 34 | - (void)addBlockTilePosition:(CGPoint)point; 35 | 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /PathFindingForObjC/fieldGrid/VectorFieldGrid.m: -------------------------------------------------------------------------------- 1 | // 2 | // VectorFieldGrid.m 3 | // 4 | // Created by JasioWoo on 14/11/4. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "VectorFieldGrid.h" 9 | 10 | typedef enum { 11 | Algorithm_Dijkstra, 12 | Algorithm_BreadthFirstSearch 13 | } AlgorithmType; 14 | 15 | 16 | @implementation VectorFieldGrid { 17 | NSMutableArray *_nodes; 18 | } 19 | 20 | - (instancetype)initWithMapSize:(CGSize)mapSize tileSize:(CGSize)tileSize coordsOrgin:(CGPoint)orginPoint { 21 | self = [super init]; 22 | if (self) { 23 | _mapSize = mapSize; 24 | _tileSize = tileSize; 25 | _orginPoint = orginPoint; 26 | 27 | unsigned int column = mapSize.width/tileSize.width; 28 | unsigned int row = mapSize.height/tileSize.height; 29 | 30 | _column = column; 31 | _row = row; 32 | _nodes = [NSMutableArray arrayWithCapacity:_row]; 33 | 34 | PFNode *node = nil;; 35 | for (int i=0; i<_row; i++) { 36 | _nodes[i] = [NSMutableArray arrayWithCapacity:_column]; 37 | 38 | for (int j=0; j<_column; j++) { 39 | node = [[PFNode alloc] init]; 40 | node.x = j; 41 | node.y = i; 42 | node.f = 0; 43 | node.walkable = YES; 44 | node.parent = nil; 45 | _nodes[i][j] = node; 46 | } 47 | } 48 | } 49 | return self; 50 | } 51 | 52 | - (void)setMapSize:(CGSize)mapSize { 53 | if ((int)_mapSize.width != (int)mapSize.width || (int)_mapSize.height != (int)mapSize.height) { 54 | _mapSize = mapSize; 55 | self.column = _mapSize.width/_tileSize.width; 56 | self.row = _mapSize.height/_tileSize.height; 57 | } 58 | } 59 | 60 | - (void)setTileSize:(CGSize)tileSize { 61 | if ((int)_tileSize.width != (int)tileSize.width || (int)_tileSize.height != (int)tileSize.height) { 62 | _tileSize = tileSize; 63 | self.column = _mapSize.width/_tileSize.width; 64 | self.row = _mapSize.height/_tileSize.height; 65 | } 66 | } 67 | 68 | 69 | - (void)setColumn:(unsigned int)column { 70 | if (_column != column) { 71 | if (_column > column) { 72 | NSMutableArray *rowArr; 73 | for (int i=0; i<_row; i++) { 74 | rowArr = _nodes[i]; 75 | for (int j=column; j<_column; j++) { 76 | [rowArr removeObjectAtIndex:j]; 77 | } 78 | } 79 | 80 | } else { 81 | PFNode *node; 82 | NSMutableArray *rowArr; 83 | for (int i=0; i<_row; i++) { 84 | rowArr = _nodes[i]; 85 | for (int j=_column; j row) { 103 | for (int i=row; i<_row; i++) { 104 | [_nodes removeObjectAtIndex:i]; 105 | } 106 | } else { 107 | PFNode *node; 108 | for (int i=_row; iself.column-1 133 | || y<0 || y>self.row-1) { 134 | return nil; 135 | } 136 | return self.nodes[y][x]; 137 | } 138 | 139 | - (BOOL)isWalkableAtX:(int)x andY:(int)y { 140 | return [self getNodeAtX:x andY:y].walkable; 141 | } 142 | 143 | - (void)setWalkableAtX:(int)x andY:(int)y andWalkable:(BOOL)walkable { 144 | [self getNodeAtX:x andY:y].walkable = walkable; 145 | } 146 | 147 | - (void)clearBlockTiles { 148 | for (int i=0; i0) { 280 | if (type==Algorithm_BreadthFirstSearch) { 281 | node = [openList firstObject]; 282 | [openList removeObject:node]; 283 | } else { 284 | [openList sortUsingSelector:@selector(descFWeightSort:)]; 285 | node = [openList lastObject]; 286 | [openList removeLastObject]; 287 | } 288 | 289 | node.closed = YES; 290 | 291 | int leftCost, rightCost, upCost, downCost; 292 | 293 | // get neigbours of the current node 294 | // ↑ 295 | x = node.x; 296 | y = node.y + 1; 297 | neighbor = [self getNodeAtX:x andY:y]; 298 | if (neighbor.walkable) { 299 | if ([self updateNeighborNode:neighbor withCenterNode:node andOpenList:openList option:type]) { 300 | neighbor.direction = 2; 301 | } 302 | } 303 | upCost = neighbor.walkable ? neighbor.cost : node.cost; 304 | 305 | // ← 306 | x = node.x - 1; 307 | y = node.y; 308 | neighbor = [self getNodeAtX:x andY:y]; 309 | if (neighbor.walkable) { 310 | if ([self updateNeighborNode:neighbor withCenterNode:node andOpenList:openList option:type]) { 311 | neighbor.direction = 8; 312 | } 313 | } 314 | leftCost = neighbor.walkable ? neighbor.cost : node.cost; 315 | 316 | // ↓ 317 | x = node.x; 318 | y = node.y - 1; 319 | neighbor = [self getNodeAtX:x andY:y]; 320 | if (neighbor.walkable) { 321 | if ([self updateNeighborNode:neighbor withCenterNode:node andOpenList:openList option:type]) { 322 | neighbor.direction = 6; 323 | } 324 | } 325 | downCost = neighbor.walkable ? neighbor.cost : node.cost; 326 | 327 | // → 328 | x = node.x + 1; 329 | y = node.y; 330 | neighbor = [self getNodeAtX:x andY:y]; 331 | if (neighbor.walkable) { 332 | if ([self updateNeighborNode:neighbor withCenterNode:node andOpenList:openList option:type]) { 333 | neighbor.direction = 4; 334 | } 335 | } 336 | rightCost = neighbor.walkable ? neighbor.cost : node.cost; 337 | 338 | 339 | // recalculate the vector field 340 | node.vector = CGVectorMake(leftCost-rightCost, (downCost-upCost)); 341 | if (node.vector.dx==0) { 342 | if (node.direction==8) { 343 | node.vector = CGVectorMake(-1, node.vector.dy); 344 | } else if (node.direction==4) { 345 | node.vector = CGVectorMake(1, node.vector.dy); 346 | } 347 | } 348 | if (node.vector.dy==0) { 349 | if (node.direction==2) { 350 | node.vector = CGVectorMake(node.vector.dx, 1); 351 | }else if (node.direction==6) { 352 | node.vector = CGVectorMake(node.vector.dx, -1); 353 | } 354 | } 355 | } 356 | 357 | } 358 | 359 | 360 | 361 | - (BOOL)updateNeighborNode:(PFNode*)neighbor withCenterNode:(PFNode*)cNode andOpenList:(NSMutableArray*)openList option:(AlgorithmType)type { 362 | if (neighbor.closed) { 363 | return NO; 364 | } 365 | 366 | if (type==Algorithm_BreadthFirstSearch) { 367 | if (neighbor.opened==0) { 368 | neighbor.cost = cNode.cost + 1; 369 | neighbor.opened = 1; 370 | [openList addObject:neighbor]; 371 | return YES; 372 | } 373 | 374 | } else if (type==Algorithm_Dijkstra) { 375 | float ng = cNode.g + ((neighbor.x-cNode.x == 0 || neighbor.y-cNode.y == 0) ? 1 : 1.4); 376 | if (neighbor.opened==0 || ng < neighbor.g) { 377 | neighbor.cost = cNode.cost + 1; 378 | neighbor.g = ng; 379 | neighbor.f = neighbor.g; 380 | 381 | if (neighbor.opened==0) { 382 | neighbor.opened = 1; 383 | [openList addObject:neighbor]; 384 | } 385 | return YES; 386 | } 387 | } 388 | 389 | return NO; 390 | } 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | @end 401 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/AStarFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // AStarFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/28. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BaseFinder.h" 9 | 10 | /** 11 | * A* path-finder. 12 | * based upon https://github.com/bgrins/javascript-astar 13 | * @constructor 14 | * @param {object} opt 15 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 16 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 17 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 18 | * @param {function} opt.heuristic Heuristic function to estimate the distance 19 | * (defaults to manhattan). 20 | * @param {integer} opt.weight Weight to apply to the heuristic to allow for suboptimal paths, 21 | * in order to speed up the search. 22 | */ 23 | @interface AStarFinder : BaseFinder 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/AStarFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // AStarFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/28. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "AStarFinder.h" 9 | 10 | #import "PFUtil.h" 11 | 12 | 13 | @implementation AStarFinder 14 | 15 | 16 | - (NSArray *)findPathInStartNode:(PFNode *)startNode toEndNode:(PFNode *)endNode withGrid:(PFGrid *)grid trackFinding:(NSMutableArray *__autoreleasing *)trackArrForTest { 17 | 18 | NSMutableArray *openList = [NSMutableArray array]; 19 | PFNode *node = nil, *neighbor = nil; 20 | NSArray *neighbors = nil; 21 | NSUInteger l=0; 22 | int i=0, x=0, y=0, endX=endNode.x, endY=endNode.y; 23 | float ng = 0; 24 | 25 | // set the `g` and `f` value of the start node to be 0 26 | startNode.g = 0; 27 | startNode.f = 0; 28 | 29 | // push the start node into the open list 30 | [openList addObject:startNode]; 31 | startNode.opened = 1; 32 | 33 | // track 34 | if (trackArrForTest) {[(*trackArrForTest) addObject:[startNode copy]];} 35 | 36 | // while the open list is not empty 37 | while (openList.count>0) { 38 | // pop the position of node which has the minimum `f` value. 39 | [openList sortUsingSelector:@selector(descFWeightSort:)]; 40 | node = [openList lastObject]; 41 | [openList removeLastObject]; 42 | node.closed = YES; 43 | 44 | // track 45 | NSMutableArray *trackArr = nil; 46 | if (trackArrForTest) { 47 | [(*trackArrForTest) addObject:[node copy]]; 48 | trackArr = [NSMutableArray array]; 49 | } 50 | 51 | // if reached the end position, construct the path and return it 52 | if (node == endNode) { 53 | return [PFUtil backtrace:endNode]; 54 | } 55 | 56 | // get neigbours of the current node 57 | neighbors = [grid getNeighborsWith:node diagonalMovement:self.movementType]; 58 | for (i = 0, l = neighbors.count; i < l; ++i) { 59 | neighbor = neighbors[i]; 60 | 61 | if (neighbor.closed) { 62 | continue; 63 | } 64 | 65 | x = neighbor.x; 66 | y = neighbor.y; 67 | 68 | // get the distance between current node and the neighbor 69 | // and calculate the next g score 70 | // ng = node.g + ((x-node.x == 0 || y-node.y == 0) ? 1 : M_SQRT2); 71 | ng = node.g + ((x-node.x == 0 || y-node.y == 0) ? 1 : 1.4); 72 | 73 | // check if the neighbor has not been inspected yet, or 74 | // can be reached with smaller cost from the current node 75 | if (neighbor.opened==0 || ng < neighbor.g) { 76 | neighbor.g = ng; 77 | neighbor.h = neighbor.h==0 ? self.weight * [self calculateHeuristicValueWithX:abs(x - endX) andY:abs(y - endY)] : neighbor.h; 78 | neighbor.f = neighbor.g + neighbor.h; 79 | neighbor.parent = node; 80 | 81 | if (neighbor.opened==0) { 82 | [openList addObject:neighbor]; 83 | neighbor.opened = 1; 84 | } 85 | 86 | // track 87 | if (trackArrForTest) { [trackArr addObject:[neighbor copy]]; } 88 | } 89 | } // end for each neighbor 90 | 91 | // track 92 | if (trackArrForTest && trackArr.count>0) { [(*trackArrForTest) addObject:trackArr]; } 93 | } // end while not open list empty 94 | 95 | // fail to find the path 96 | return nil; 97 | } 98 | 99 | 100 | 101 | 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BaseFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/28. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "PFTypes.h" 9 | 10 | #import "PFNode.h" 11 | #import "PFGrid.h" 12 | #import "Heuristic.h" 13 | 14 | 15 | @interface BaseFinder : NSObject 16 | 17 | @property (nonatomic) HeuristicType heuristicType; 18 | @property (nonatomic) DiagonalMovement movementType; 19 | @property (nonatomic) Heuristic *heuristic; 20 | @property (nonatomic) int weight; 21 | 22 | @property (nonatomic, readonly) NSArray *resultPath; 23 | 24 | - (NSArray *)findPathInStartNode:(PFNode*)startNode toEndNode:(PFNode*)endNode withGrid:(PFGrid*)grid trackFinding:(NSMutableArray**)trackArrForTest; 25 | 26 | - (float)calculateHeuristicValueWithX:(float)dx andY:(float)dy; 27 | 28 | - (Heuristic *)createHeuristicWithType:(HeuristicType)type; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BaseFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/28. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BaseFinder.h" 9 | 10 | 11 | @implementation BaseFinder 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | if (self) { 16 | self.movementType = DiagonalMovement_Always; 17 | self.heuristicType = HeuristicTypeManhattan; 18 | self.heuristic = [self createHeuristicWithType:self.heuristicType]; 19 | 20 | self.weight = 1; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)setWeight:(int)weight { 26 | if (weight==0) { 27 | _weight = 1; 28 | } else { 29 | _weight = weight; 30 | } 31 | } 32 | 33 | - (void)setHeuristicType:(HeuristicType)heuristicType { 34 | if (heuristicType != _heuristicType) { 35 | _heuristicType = heuristicType; 36 | self.heuristic = [self createHeuristicWithType:heuristicType]; 37 | } 38 | } 39 | 40 | - (NSArray *)findPathInStartNode:(PFNode *)startNode toEndNode:(PFNode *)endNode withGrid:(PFGrid *)grid trackFinding:(NSMutableArray *__autoreleasing *)trackArrForTest { 41 | return nil; 42 | } 43 | 44 | - (Heuristic *)createHeuristicWithType:(HeuristicType)type { 45 | Heuristic *result = nil; 46 | switch (type) { 47 | case HeuristicTypeManhattan: 48 | result = [[Manhattan alloc] init]; 49 | break; 50 | case HeuristicTypeEuclidean: 51 | result = [[Euclidean alloc] init]; 52 | break; 53 | case HeuristicTypeOctile: 54 | result = [[Octile alloc] init]; 55 | break; 56 | case HeuristicTypeChebyshev: 57 | result = [[Chebyshev alloc] init]; 58 | break; 59 | default: 60 | result = [[Manhattan alloc] init]; 61 | break; 62 | } 63 | return result; 64 | } 65 | 66 | - (float)calculateHeuristicValueWithX:(float)dx andY:(float)dy { 67 | return [self.heuristic performAlgorithmWithX:dx andY:dy]; 68 | } 69 | 70 | //- (void)dealloc { 71 | // debugMethod(); 72 | //} 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BestFirstFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BestFirstFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "AStarFinder.h" 9 | 10 | /** 11 | * Best-First-Search path-finder. 12 | * @constructor 13 | * @extends AStarFinder 14 | * @param {object} opt 15 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 16 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 17 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 18 | * @param {function} opt.heuristic Heuristic function to estimate the distance 19 | * (defaults to manhattan). 20 | */ 21 | @interface BestFirstFinder : AStarFinder 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BestFirstFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BestFirstFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BestFirstFinder.h" 9 | 10 | @implementation BestFirstFinder 11 | 12 | 13 | - (float)calculateHeuristicValueWithX:(float)dx andY:(float)dy { 14 | return [super calculateHeuristicValueWithX:dx andY:dy] * 1000000; 15 | } 16 | 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiAStarFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BiAStarFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BaseFinder.h" 9 | 10 | 11 | /** 12 | * A* path-finder. 13 | * based upon https://github.com/bgrins/javascript-astar 14 | * @constructor 15 | * @param {object} opt 16 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 17 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 18 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 19 | * @param {function} opt.heuristic Heuristic function to estimate the distance 20 | * (defaults to manhattan). 21 | * @param {integer} opt.weight Weight to apply to the heuristic to allow for suboptimal paths, 22 | * in order to speed up the search. 23 | */ 24 | @interface BiAStarFinder : BaseFinder 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiAStarFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BiAStarFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BiAStarFinder.h" 9 | #import "PFUtil.h" 10 | 11 | #define BY_START 1 12 | #define BY_END 2 13 | 14 | @implementation BiAStarFinder 15 | 16 | - (NSArray *)findPathInStartNode:(PFNode *)startNode toEndNode:(PFNode *)endNode withGrid:(PFGrid *)grid trackFinding:(NSMutableArray *__autoreleasing *)trackArrForTest { 17 | 18 | NSMutableArray *startOpenList = [NSMutableArray array]; 19 | NSMutableArray *endOpenList = [NSMutableArray array]; 20 | PFNode *node = nil, *neighbor = nil; 21 | NSArray *neighbors = nil; 22 | NSUInteger l=0; 23 | int i=0, x=0, y=0, endX=endNode.x, endY=endNode.y, startX=startNode.x, startY=startNode.y; 24 | float ng = 0; 25 | 26 | // set the `g` and `f` value of the start node to be 0 27 | // and push it into the start open list 28 | startNode.g = 0; 29 | startNode.f = 0; 30 | [startOpenList addObject:startNode]; 31 | startNode.opened = BY_START; 32 | 33 | // set the `g` and `f` value of the end node to be 0 34 | // and push it into the open open list 35 | endNode.g = 0; 36 | endNode.f = 0; 37 | [endOpenList addObject:endNode]; 38 | endNode.opened = BY_END; 39 | 40 | // track 41 | if (trackArrForTest) { 42 | NSMutableArray *trackArr = [NSMutableArray array]; 43 | [trackArr addObject:[startNode copy]]; 44 | [trackArr addObject:[endNode copy]]; 45 | [(*trackArrForTest) addObject:trackArr]; 46 | } 47 | 48 | // while both the open lists are not empty 49 | while (startOpenList.count>0 && endOpenList.count>0) { 50 | 51 | // startOpenList 52 | // pop the position of start node which has the minimum `f` value. 53 | [startOpenList sortUsingSelector:@selector(descFWeightSort:)]; 54 | node = [startOpenList lastObject]; 55 | [startOpenList removeLastObject]; 56 | node.closed = YES; 57 | 58 | // track 59 | NSMutableArray *trackArr = nil; 60 | if (trackArrForTest) { 61 | [(*trackArrForTest) addObject:[node copy]]; 62 | trackArr = [NSMutableArray array]; 63 | } 64 | 65 | // get neigbours of the current node 66 | neighbors = [grid getNeighborsWith:node diagonalMovement:self.movementType]; 67 | for (i = 0, l = neighbors.count; i < l; ++i) { 68 | neighbor = neighbors[i]; 69 | 70 | if (neighbor.closed) { 71 | continue; 72 | } 73 | if (neighbor.opened == BY_END) { 74 | return [PFUtil biBacktraceWithNodeA:node andNodeB:neighbor]; 75 | } 76 | 77 | x = neighbor.x; 78 | y = neighbor.y; 79 | 80 | // get the distance between current node and the neighbor 81 | // and calculate the next g score 82 | // ng = node.g + ((x-node.x == 0 || y-node.y == 0) ? 1 : M_SQRT2); 83 | ng = node.g + ((x-node.x == 0 || y-node.y == 0) ? 1 : 1.4); 84 | 85 | // check if the neighbor has not been inspected yet, or 86 | // can be reached with smaller cost from the current node 87 | if (neighbor.opened==0 || ng < neighbor.g) { 88 | neighbor.g = ng; 89 | neighbor.h = neighbor.h==0 ? self.weight * [self calculateHeuristicValueWithX:abs(x - endX) andY:abs(y - endY)] : neighbor.h; 90 | neighbor.f = neighbor.g + neighbor.h; 91 | neighbor.parent = node; 92 | 93 | if (neighbor.opened==0) { 94 | [startOpenList addObject:neighbor]; 95 | neighbor.opened = BY_START; 96 | } 97 | 98 | // track 99 | if (trackArrForTest) { [trackArr addObject:[neighbor copy]]; } 100 | } 101 | } // end for each neighbor 102 | 103 | // track 104 | if (trackArrForTest && trackArr.count>0) { [(*trackArrForTest) addObject:trackArr]; } 105 | 106 | // endOpenList 107 | // pop the position of end node which has the minimum `f` value. 108 | [endOpenList sortUsingSelector:@selector(descFWeightSort:)]; 109 | node = [endOpenList lastObject]; 110 | [endOpenList removeLastObject]; 111 | node.closed = YES; 112 | 113 | // track 114 | if (trackArrForTest) { 115 | [(*trackArrForTest) addObject:[node copy]]; 116 | trackArr = [NSMutableArray array]; 117 | } 118 | 119 | // get neigbours of the current node 120 | neighbors = [grid getNeighborsWith:node diagonalMovement:self.movementType]; 121 | for (i = 0, l = neighbors.count; i < l; ++i) { 122 | neighbor = neighbors[i]; 123 | 124 | if (neighbor.closed) { 125 | continue; 126 | } 127 | if (neighbor.opened == BY_START) { 128 | return [PFUtil biBacktraceWithNodeA:neighbor andNodeB:node]; 129 | } 130 | 131 | x = neighbor.x; 132 | y = neighbor.y; 133 | 134 | // get the distance between current node and the neighbor 135 | // and calculate the next g score 136 | 137 | ng = node.g + ((x-node.x == 0 || y-node.y == 0) ? 1 : 1.4); 138 | 139 | // check if the neighbor has not been inspected yet, or 140 | // can be reached with smaller cost from the current node 141 | if (neighbor.opened==0 || ng < neighbor.g) { 142 | neighbor.g = ng; 143 | neighbor.h = neighbor.h==0 ? self.weight * [self calculateHeuristicValueWithX:abs(x - startX) andY:abs(y - startY)] : neighbor.h; 144 | neighbor.f = neighbor.g + neighbor.h; 145 | neighbor.parent = node; 146 | 147 | if (neighbor.opened==0) { 148 | [endOpenList addObject:neighbor]; 149 | neighbor.opened = BY_END; 150 | } 151 | 152 | // track 153 | if (trackArrForTest) { [trackArr addObject:[neighbor copy]]; } 154 | } 155 | } // end for each neighbor 156 | 157 | // track 158 | if (trackArrForTest && trackArr.count>0) { [(*trackArrForTest) addObject:trackArr]; } 159 | 160 | } // end while not open list empty 161 | 162 | // fail to find the path 163 | return nil; 164 | } 165 | 166 | 167 | 168 | 169 | 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiBestFirstFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BiBestFirstFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BiAStarFinder.h" 9 | 10 | /** 11 | * Bi-direcitional Best-First-Search path-finder. 12 | * @constructor 13 | * @extends BiAStarFinder 14 | * @param {object} opt 15 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 16 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 17 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 18 | * @param {function} opt.heuristic Heuristic function to estimate the distance 19 | * (defaults to manhattan). 20 | */ 21 | @interface BiBestFirstFinder : BiAStarFinder 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiBestFirstFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BiBestFirstFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BiBestFirstFinder.h" 9 | 10 | @implementation BiBestFirstFinder 11 | 12 | - (float)calculateHeuristicValueWithX:(float)dx andY:(float)dy { 13 | return [super calculateHeuristicValueWithX:dx andY:dy] * 1000000; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiBreadthFirstFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BiBreadthFirstFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BaseFinder.h" 9 | 10 | 11 | /** 12 | * Bi-directional Breadth-First-Search path finder. 13 | * @constructor 14 | * @param {object} opt 15 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 16 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 17 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 18 | */ 19 | @interface BiBreadthFirstFinder : BaseFinder 20 | 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiBreadthFirstFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BiBreadthFirstFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BiBreadthFirstFinder.h" 9 | #import "PFUtil.h" 10 | 11 | #define BY_START 1 12 | #define BY_END 2 13 | 14 | @implementation BiBreadthFirstFinder 15 | 16 | 17 | - (NSArray *)findPathInStartNode:(PFNode *)startNode toEndNode:(PFNode *)endNode withGrid:(PFGrid *)grid trackFinding:(NSMutableArray *__autoreleasing *)trackArrForTest { 18 | 19 | NSMutableArray *startOpenList = [NSMutableArray array]; 20 | NSMutableArray *endOpenList = [NSMutableArray array]; 21 | PFNode *node = nil, *neighbor = nil; 22 | NSArray *neighbors = nil; 23 | NSUInteger l=0; 24 | int i=0; 25 | 26 | // push the start and end nodes into the queues 27 | [startOpenList addObject:startNode]; 28 | startNode.opened = BY_START; 29 | 30 | [endOpenList addObject:endNode]; 31 | endNode.opened = BY_END; 32 | 33 | // track 34 | if (trackArrForTest) { 35 | NSMutableArray *trackArr = [NSMutableArray array]; 36 | [trackArr addObject:[startNode copy]]; 37 | [trackArr addObject:[endNode copy]]; 38 | [(*trackArrForTest) addObject:trackArr]; 39 | } 40 | 41 | // while both the queues are not empty 42 | while (startOpenList.count>0 && endOpenList.count>0) { 43 | 44 | // expand start open list 45 | node = startOpenList.firstObject; 46 | [startOpenList removeObject:node]; 47 | node.closed = YES; 48 | 49 | // track 50 | NSMutableArray *trackArr = nil; 51 | if (trackArrForTest) { 52 | [(*trackArrForTest) addObject:[node copy]]; 53 | trackArr = [NSMutableArray array]; 54 | } 55 | 56 | neighbors = [grid getNeighborsWith:node diagonalMovement:self.movementType]; 57 | for (i = 0, l = neighbors.count; i < l; ++i) { 58 | neighbor = neighbors[i]; 59 | 60 | if (neighbor.closed) { 61 | continue; 62 | } 63 | if (neighbor.opened>0) { 64 | // if this node has been inspected by the reversed search, 65 | // then a path is found. 66 | if (neighbor.opened == BY_END) { 67 | return [PFUtil biBacktraceWithNodeA:node andNodeB:neighbor]; 68 | } 69 | continue; 70 | } 71 | [startOpenList addObject:neighbor]; 72 | neighbor.parent = node; 73 | neighbor.opened = BY_START; 74 | 75 | // track 76 | if (trackArrForTest) { [trackArr addObject:[neighbor copy]]; } 77 | } 78 | // track 79 | if (trackArrForTest && trackArr.count>0) { [(*trackArrForTest) addObject:trackArr]; } 80 | 81 | // expand end open list 82 | node = endOpenList.firstObject; 83 | [endOpenList removeObject:node]; 84 | node.closed = YES; 85 | 86 | // track 87 | if (trackArrForTest) { 88 | [(*trackArrForTest) addObject:[node copy]]; 89 | trackArr = [NSMutableArray array]; 90 | } 91 | 92 | neighbors = [grid getNeighborsWith:node diagonalMovement:self.movementType]; 93 | for (i = 0, l = neighbors.count; i < l; ++i) { 94 | neighbor = neighbors[i]; 95 | 96 | if (neighbor.closed) { 97 | continue; 98 | } 99 | if (neighbor.opened>0) { 100 | if (neighbor.opened == BY_START) { 101 | return [PFUtil biBacktraceWithNodeA:neighbor andNodeB:node]; 102 | } 103 | continue; 104 | } 105 | [endOpenList addObject:neighbor]; 106 | neighbor.parent = node; 107 | neighbor.opened = BY_END; 108 | 109 | // track 110 | if (trackArrForTest) { [trackArr addObject:[neighbor copy]]; } 111 | } 112 | // track 113 | if (trackArrForTest && trackArr.count>0) { [(*trackArrForTest) addObject:trackArr]; } 114 | 115 | } // end while not open list empty 116 | 117 | return nil; 118 | } 119 | 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiDijkstraFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BiDijkstraFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BiAStarFinder.h" 9 | 10 | /** 11 | * Bi-directional Dijkstra path-finder. 12 | * @constructor 13 | * @extends BiAStarFinder 14 | * @param {object} opt 15 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 16 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 17 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 18 | */ 19 | @interface BiDijkstraFinder : BiAStarFinder 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BiDijkstraFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BiDijkstraFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BiDijkstraFinder.h" 9 | 10 | @implementation BiDijkstraFinder 11 | 12 | - (float)calculateHeuristicValueWithX:(float)dx andY:(float)dy { 13 | return 0; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BreadthFirstFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // BreadthFirstFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BaseFinder.h" 9 | 10 | /** 11 | * Breadth-First-Search path finder. 12 | * @constructor 13 | * @param {object} opt 14 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 15 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 16 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 17 | */ 18 | @interface BreadthFirstFinder : BaseFinder 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/BreadthFirstFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // BreadthFirstFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BreadthFirstFinder.h" 9 | #import "PFUtil.h" 10 | 11 | 12 | @implementation BreadthFirstFinder 13 | 14 | 15 | - (NSArray *)findPathInStartNode:(PFNode *)startNode toEndNode:(PFNode *)endNode withGrid:(PFGrid *)grid trackFinding:(NSMutableArray *__autoreleasing *)trackArrForTest { 16 | 17 | NSMutableArray *openList = [NSMutableArray array]; 18 | PFNode *node = nil, *neighbor = nil; 19 | NSArray *neighbors = nil; 20 | NSUInteger l=0; 21 | int i=0; 22 | 23 | // push the start pos into the queue 24 | [openList addObject:startNode]; 25 | startNode.opened = 1; 26 | 27 | // track 28 | if (trackArrForTest) {[(*trackArrForTest) addObject:[startNode copy]];} 29 | 30 | // while the queue is not empty 31 | while (openList.count>0) { 32 | // take the front node from the queue 33 | node = openList.firstObject; 34 | [openList removeObject:node]; 35 | node.closed = YES; 36 | 37 | // track 38 | NSMutableArray *trackArr = nil; 39 | if (trackArrForTest) { 40 | [(*trackArrForTest) addObject:[node copy]]; 41 | trackArr = [NSMutableArray array]; 42 | } 43 | 44 | // reached the end position 45 | if (node == endNode) { 46 | return [PFUtil backtrace:endNode]; 47 | } 48 | 49 | neighbors = [grid getNeighborsWith:node diagonalMovement:self.movementType]; 50 | for (i = 0, l = neighbors.count; i < l; ++i) { 51 | neighbor = neighbors[i]; 52 | 53 | // skip this neighbor if it has been inspected before 54 | if (neighbor.closed || neighbor.opened==1) { 55 | continue; 56 | } 57 | 58 | [openList addObject:neighbor]; 59 | neighbor.opened = 1; 60 | neighbor.parent = node; 61 | 62 | // track 63 | if (trackArrForTest) { [trackArr addObject:[neighbor copy]]; } 64 | } 65 | // track 66 | if (trackArrForTest && trackArr.count>0) { [(*trackArrForTest) addObject:trackArr]; } 67 | 68 | } // end while not open list empty 69 | 70 | return nil; 71 | } 72 | 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/DijkstraFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // DijkstraFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "AStarFinder.h" 9 | 10 | 11 | /** 12 | * Dijkstra path-finder. 13 | * @constructor 14 | * @extends AStarFinder 15 | * @param {object} opt 16 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 17 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 18 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 19 | */ 20 | @interface DijkstraFinder : AStarFinder 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/DijkstraFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // DijkstraFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "DijkstraFinder.h" 9 | 10 | @implementation DijkstraFinder 11 | 12 | - (float)calculateHeuristicValueWithX:(float)dx andY:(float)dy { 13 | return 0; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/IDAStarFinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // IDAStarFinder.h 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "BaseFinder.h" 9 | 10 | /** 11 | * Not Implemented Yet!!! 12 | * 13 | * Iterative Deeping A Star (IDA*) path-finder. 14 | * 15 | * Recursion based on: 16 | * http://www.apl.jhu.edu/~hall/AI-Programming/IDA-Star.html 17 | * 18 | * Path retracing based on: 19 | * V. Nageshwara Rao, Vipin Kumar and K. Ramesh 20 | * "A Parallel Implementation of Iterative-Deeping-A*", January 1987. 21 | * ftp://ftp.cs.utexas.edu/.snapshot/hourly.1/pub/AI-Lab/tech-reports/UT-AI-TR-87-46.pdf 22 | * 23 | * @author Gerard Meier (www.gerardmeier.com) 24 | * 25 | * @constructor 26 | * @param {object} opt 27 | * @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed. Deprecated, use diagonalMovement instead. 28 | * @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners. Deprecated, use diagonalMovement instead. 29 | * @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement. 30 | * @param {function} opt.heuristic Heuristic function to estimate the distance 31 | * (defaults to manhattan). 32 | * @param {integer} opt.weight Weight to apply to the heuristic to allow for suboptimal paths, 33 | * in order to speed up the search. 34 | * @param {object} opt.trackRecursion Whether to track recursion for statistical purposes. 35 | * @param {object} opt.timeLimit Maximum execution time. Use <= 0 for infinite. 36 | */ 37 | @interface IDAStarFinder : BaseFinder 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/IDAStarFinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // IDAStarFinder.m 3 | // 4 | // Created by JasioWoo on 14/10/31. 5 | // Copyright (c) 2014年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "IDAStarFinder.h" 9 | #import "PFUtil.h" 10 | 11 | @implementation IDAStarFinder 12 | 13 | 14 | 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/JPFAlwaysMoveDiagonally.h: -------------------------------------------------------------------------------- 1 | // 2 | // JPFAlwaysMoveDiagonally.h 3 | // 4 | // Created by JasioWoo on 15/3/17. 5 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "JumpPointFinderBase.h" 9 | 10 | 11 | /** 12 | * Path finder using the Jump Point Search algorithm which always moves 13 | * diagonally irrespective of the number of obstacles. 14 | */ 15 | @interface JPFAlwaysMoveDiagonally : JumpPointFinderBase 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PathFindingForObjC/finders/JPFAlwaysMoveDiagonally.m: -------------------------------------------------------------------------------- 1 | // 2 | // JPFAlwaysMoveDiagonally.m 3 | // 4 | // Created by JasioWoo on 15/3/17. 5 | // Copyright (c) 2015年 JasioWoo. All rights reserved. 6 | // 7 | 8 | #import "JPFAlwaysMoveDiagonally.h" 9 | 10 | @implementation JPFAlwaysMoveDiagonally 11 | 12 | 13 | /** 14 | * Search recursively in the direction (parent -> child), stopping only when a 15 | * jump point is found. 16 | * @protected 17 | * @return {Array.<[number, number]>} The x, y coordinate of the jump point 18 | * found, or null if not found 19 | */ 20 | - (PFNode *)jump:(PFNode*)nodeA withNode:(PFNode*)nodeB withEndNode:(PFNode*)endNode withGrid:(PFGrid *)grid andTrackArr:(NSMutableArray*)trackArr { 21 | 22 | int x=nodeA.x, y=nodeA.y; 23 | int dx = nodeA.x - nodeB.x; 24 | int dy = nodeA.y - nodeB.y; 25 | if (!nodeA.walkable) { 26 | return nil; 27 | } 28 | 29 | if(trackArr) { 30 | nodeA.tested = YES; 31 | [trackArr addObject:[nodeA copy]]; 32 | } 33 | 34 | if (nodeA == endNode) { 35 | return nodeA; 36 | } 37 | 38 | // check for forced neighbors 39 | // along the diagonal 40 | if (dx != 0 && dy != 0) { 41 | PFNode *t1Node = [grid getNodeAtX:(x - dx) andY:(y + dy)]; 42 | PFNode *t2Node = [grid getNodeAtX:(x - dx) andY:(y)]; 43 | PFNode *t3Node = [grid getNodeAtX:(x + dx) andY:(y - dy)]; 44 | PFNode *t4Node = [grid getNodeAtX:(x) andY:(y - dy)]; 45 | 46 | if ((t1Node.walkable && !t2Node.walkable) 47 | || (t3Node.walkable && !t4Node.walkable)) { 48 | return nodeA; 49 | } 50 | 51 | // when moving diagonally, must check for vertical/horizontal jump points 52 | t1Node = [grid getNodeAtX:(x + dx) andY:(y)]; 53 | t2Node = [grid getNodeAtX:(x) andY:(y + dy)]; 54 | if ([self jump:t1Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr] 55 | || [self jump:t2Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]) { 56 | return nodeA; 57 | } 58 | } 59 | // horizontally/vertically 60 | else { 61 | if( dx != 0 ) { // moving along x 62 | PFNode *t1Node = [grid getNodeAtX:(x + dx) andY:(y + 1)]; 63 | PFNode *t2Node = [grid getNodeAtX:(x) andY:(y + 1)]; 64 | PFNode *t3Node = [grid getNodeAtX:(x + dx) andY:(y - 1)]; 65 | PFNode *t4Node = [grid getNodeAtX:(x) andY:(y - 1)]; 66 | 67 | if((t1Node.walkable && !t2Node.walkable) 68 | || (t3Node.walkable && !t4Node.walkable)) { 69 | return nodeA; 70 | } 71 | } 72 | else { 73 | PFNode *t1Node = [grid getNodeAtX:(x + 1) andY:(y + dy)]; 74 | PFNode *t2Node = [grid getNodeAtX:(x + 1) andY:(y)]; 75 | PFNode *t3Node = [grid getNodeAtX:(x - 1) andY:(y + dy)]; 76 | PFNode *t4Node = [grid getNodeAtX:(x - 1) andY:(y)]; 77 | 78 | if((t1Node.walkable && !t2Node.walkable) 79 | || (t3Node.walkable && !t4Node.walkable)) { 80 | return nodeA; 81 | } 82 | } 83 | } 84 | 85 | PFNode *reNodeA = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 86 | return [self jump:reNodeA withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]; 87 | } 88 | 89 | 90 | /** 91 | * Find the neighbors for the given node. If the node has a parent, 92 | * prune the neighbors based on the jump point search algorithm, otherwise 93 | * return all available neighbors. 94 | * @return {Array.<[number, number]>} The neighbors found. 95 | */ 96 | - (NSArray *)findNeighbors:(PFNode *)node withGrid:(PFGrid *)grid { 97 | 98 | PFNode *parent = node.parent; 99 | NSMutableArray *neighbors = [NSMutableArray array]; 100 | int x=node.x, y=node.y, dx, dy; 101 | 102 | PFNode *cNode = nil; 103 | 104 | // directed pruning: can ignore most neighbors, unless forced. 105 | if (parent) { 106 | // get the normalized direction of travel 107 | dx = (x - parent.x) / MAX(abs(x - parent.x), 1); 108 | dy = (y - parent.y) / MAX(abs(y - parent.y), 1); 109 | 110 | // search diagonally 111 | if (dx != 0 && dy != 0) { 112 | PFNode *c1Node = [grid getNodeAtX:x andY:(y + dy)]; 113 | PFNode *c2Node = [grid getNodeAtX:(x + dx) andY:y]; 114 | PFNode *c3Node = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 115 | PFNode *c4Node = [grid getNodeAtX:(x - dx) andY:y]; 116 | PFNode *c5Node = [grid getNodeAtX:x andY:(y - dy)]; 117 | 118 | if (c1Node.walkable) { 119 | [neighbors addObject:c1Node]; 120 | } 121 | if (c2Node.walkable) { 122 | [neighbors addObject:c2Node]; 123 | } 124 | if (c3Node.walkable) { 125 | [neighbors addObject:c3Node]; 126 | } 127 | if (!c4Node.walkable) { 128 | cNode = [grid getNodeAtX:(x - dx) andY:(y + dy)]; 129 | cNode?[neighbors addObject:cNode]:NO; 130 | } 131 | if (!c5Node.walkable) { 132 | cNode = [grid getNodeAtX:(x + dx) andY:(y - dy)]; 133 | cNode?[neighbors addObject:cNode]:NO; 134 | } 135 | } 136 | // search horizontally/vertically 137 | else { 138 | if(dx == 0) { 139 | 140 | PFNode *c1Node = [grid getNodeAtX:x andY:(y + dy)]; 141 | PFNode *c2Node = [grid getNodeAtX:(x + 1) andY:y]; 142 | PFNode *c3Node = [grid getNodeAtX:(x - 1) andY:y]; 143 | 144 | if (c1Node.walkable) { 145 | [neighbors addObject:c1Node]; 146 | } 147 | if (!c2Node.walkable) { 148 | cNode = [grid getNodeAtX:(x + 1) andY:(y + dy)]; 149 | cNode?[neighbors addObject:cNode]:NO; 150 | } 151 | if (!c3Node.walkable) { 152 | cNode = [grid getNodeAtX:(x - 1) andY:(y + dy)]; 153 | cNode?[neighbors addObject:cNode]:NO; 154 | } 155 | } 156 | else { 157 | 158 | PFNode *c1Node = [grid getNodeAtX:(x + dx) andY:y]; 159 | PFNode *c2Node = [grid getNodeAtX:x andY:(y + 1)]; 160 | PFNode *c3Node = [grid getNodeAtX:x andY:(y - 1)]; 161 | 162 | if (c1Node.walkable) { 163 | [neighbors addObject:c1Node]; 164 | } 165 | if (!c2Node.walkable) { 166 | cNode = [grid getNodeAtX:(x + dx) andY:(y + 1)]; 167 | cNode?[neighbors addObject:cNode]:NO; 168 | } 169 | if (!c3Node.walkable) { 170 | cNode = [grid getNodeAtX:(x + dx) andY:(y - 1)]; 171 | cNode?[neighbors addObject:cNode]:NO; 172 | } 173 | 174 | } 175 | } 176 | } 177 | // return all neighbors 178 | else { 179 | 180 | NSArray *neighborNodes = [grid getNeighborsWith:node diagonalMovement:DiagonalMovement_Always]; 181 | PFNode *neighborNode=nil; 182 | for (int i=0; i child), stopping only when a 15 | * jump point is found. 16 | * @protected 17 | * @return {Array.<[number, number]>} The x, y coordinate of the jump point 18 | * found, or null if not found 19 | */ 20 | - (PFNode *)jump:(PFNode*)nodeA withNode:(PFNode*)nodeB withEndNode:(PFNode*)endNode withGrid:(PFGrid *)grid andTrackArr:(NSMutableArray*)trackArr { 21 | 22 | int x=nodeA.x, y=nodeA.y; 23 | int dx = nodeA.x - nodeB.x; 24 | int dy = nodeA.y - nodeB.y; 25 | if (!nodeA.walkable) { 26 | return nil; 27 | } 28 | 29 | if(trackArr) { 30 | nodeA.tested = YES; 31 | [trackArr addObject:[nodeA copy]]; 32 | } 33 | 34 | if (nodeA == endNode) { 35 | return nodeA; 36 | } 37 | 38 | // check for forced neighbors 39 | // along the diagonal 40 | if (dx != 0 && dy != 0) { 41 | PFNode *t1Node = [grid getNodeAtX:(x - dx) andY:(y + dy)]; 42 | PFNode *t2Node = [grid getNodeAtX:(x - dx) andY:(y)]; 43 | PFNode *t3Node = [grid getNodeAtX:(x + dx) andY:(y - dy)]; 44 | PFNode *t4Node = [grid getNodeAtX:(x) andY:(y - dy)]; 45 | 46 | if ((t1Node.walkable && !t2Node.walkable) 47 | || (t3Node.walkable && !t4Node.walkable)) { 48 | return nodeA; 49 | } 50 | 51 | // when moving diagonally, must check for vertical/horizontal jump points 52 | t1Node = [grid getNodeAtX:(x + dx) andY:(y)]; 53 | t2Node = [grid getNodeAtX:(x) andY:(y + dy)]; 54 | if ([self jump:t1Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr] 55 | || [self jump:t2Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]) { 56 | return nodeA; 57 | } 58 | } 59 | // horizontally/vertically 60 | else { 61 | if( dx != 0 ) { // moving along x 62 | PFNode *t1Node = [grid getNodeAtX:(x + dx) andY:(y + 1)]; 63 | PFNode *t2Node = [grid getNodeAtX:(x) andY:(y + 1)]; 64 | PFNode *t3Node = [grid getNodeAtX:(x + dx) andY:(y - 1)]; 65 | PFNode *t4Node = [grid getNodeAtX:(x) andY:(y - 1)]; 66 | 67 | if((t1Node.walkable && !t2Node.walkable) 68 | || (t3Node.walkable && !t4Node.walkable)) { 69 | return nodeA; 70 | } 71 | } 72 | else { 73 | PFNode *t1Node = [grid getNodeAtX:(x + 1) andY:(y + dy)]; 74 | PFNode *t2Node = [grid getNodeAtX:(x + 1) andY:(y)]; 75 | PFNode *t3Node = [grid getNodeAtX:(x - 1) andY:(y + dy)]; 76 | PFNode *t4Node = [grid getNodeAtX:(x - 1) andY:(y)]; 77 | 78 | if((t1Node.walkable && !t2Node.walkable) 79 | || (t3Node.walkable && !t4Node.walkable)) { 80 | return nodeA; 81 | } 82 | } 83 | } 84 | 85 | // moving diagonally, must make sure one of the vertical/horizontal 86 | // neighbors is open to allow the path 87 | 88 | PFNode *t1Node = [grid getNodeAtX:(x + dx) andY:(y)]; 89 | PFNode *t2Node = [grid getNodeAtX:(x) andY:(y + dy)]; 90 | if (t1Node.walkable || t2Node.walkable) { 91 | PFNode *reNodeA = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 92 | return [self jump:reNodeA withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]; 93 | } else { 94 | return nil; 95 | } 96 | } 97 | 98 | 99 | /** 100 | * Find the neighbors for the given node. If the node has a parent, 101 | * prune the neighbors based on the jump point search algorithm, otherwise 102 | * return all available neighbors. 103 | * @return {Array.<[number, number]>} The neighbors found. 104 | */ 105 | - (NSArray *)findNeighbors:(PFNode *)node withGrid:(PFGrid *)grid { 106 | 107 | PFNode *parent = node.parent; 108 | NSMutableArray *neighbors = [NSMutableArray array]; 109 | int x=node.x, y=node.y, dx, dy; 110 | 111 | // directed pruning: can ignore most neighbors, unless forced. 112 | if (parent) { 113 | // get the normalized direction of travel 114 | dx = (x - parent.x) / MAX(abs(x - parent.x), 1); 115 | dy = (y - parent.y) / MAX(abs(y - parent.y), 1); 116 | 117 | // search diagonally 118 | if (dx != 0 && dy != 0) { 119 | PFNode *cNode = nil; 120 | 121 | PFNode *c1Node = [grid getNodeAtX:x andY:(y + dy)]; 122 | PFNode *c2Node = [grid getNodeAtX:(x + dx) andY:y]; 123 | PFNode *c4Node = [grid getNodeAtX:(x - dx) andY:y]; 124 | PFNode *c5Node = [grid getNodeAtX:x andY:(y - dy)]; 125 | 126 | if (c1Node.walkable) { 127 | [neighbors addObject:c1Node]; 128 | } 129 | if (c2Node.walkable) { 130 | [neighbors addObject:c2Node]; 131 | } 132 | if (c1Node.walkable || c2Node.walkable) { 133 | cNode = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 134 | cNode?[neighbors addObject:cNode]:NO; 135 | } 136 | if (!c4Node.walkable && c1Node.walkable) { 137 | cNode = [grid getNodeAtX:(x - dx) andY:(y + dy)]; 138 | cNode?[neighbors addObject:cNode]:NO; 139 | } 140 | if (!c5Node.walkable && c2Node.walkable) { 141 | cNode = [grid getNodeAtX:(x + dx) andY:(y - dy)]; 142 | cNode?[neighbors addObject:cNode]:NO; 143 | } 144 | } 145 | // search horizontally/vertically 146 | else { 147 | if(dx == 0) { 148 | PFNode *cNode = [grid getNodeAtX:x andY:(y + dy)]; 149 | if (cNode.walkable) { 150 | [neighbors addObject:cNode]; 151 | 152 | cNode = [grid getNodeAtX:(x + 1) andY:y]; 153 | if (!cNode.walkable) { 154 | cNode = [grid getNodeAtX:(x + 1) andY:(y + dy)]; 155 | cNode?[neighbors addObject:cNode]:NO; 156 | } 157 | 158 | cNode = [grid getNodeAtX:(x - 1) andY:y]; 159 | if (!cNode.walkable) { 160 | cNode = [grid getNodeAtX:(x - 1) andY:(y + dy)]; 161 | cNode?[neighbors addObject:cNode]:NO; 162 | } 163 | } 164 | } 165 | else { 166 | PFNode *cNode = [grid getNodeAtX:(x + dx) andY:y]; 167 | if (cNode.walkable) { 168 | [neighbors addObject:cNode]; 169 | 170 | cNode = [grid getNodeAtX:(x) andY:(y + 1)]; 171 | if (!cNode.walkable) { 172 | cNode = [grid getNodeAtX:(x + dx) andY:(y + 1)]; 173 | cNode?[neighbors addObject:cNode]:NO; 174 | } 175 | 176 | cNode = [grid getNodeAtX:(x) andY:(y - 1)]; 177 | if (!cNode.walkable) { 178 | cNode = [grid getNodeAtX:(x + dx) andY:(y - 1)]; 179 | cNode?[neighbors addObject:cNode]:NO; 180 | } 181 | } 182 | } 183 | } 184 | } 185 | // return all neighbors 186 | else { 187 | 188 | NSArray *neighborNodes = [grid getNeighborsWith:node diagonalMovement:DiagonalMovement_IfAtMostOneObstacle]; 189 | PFNode *neighborNode=nil; 190 | for (int i=0; i child), stopping only when a 17 | * jump point is found. 18 | * @protected 19 | * @return {Array.<[number, number]>} The x, y coordinate of the jump point 20 | * found, or null if not found 21 | */ 22 | - (PFNode *)jump:(PFNode*)nodeA withNode:(PFNode*)nodeB withEndNode:(PFNode*)endNode withGrid:(PFGrid *)grid andTrackArr:(NSMutableArray*)trackArr { 23 | 24 | int x=nodeA.x, y=nodeA.y; 25 | int dx = nodeA.x - nodeB.x; 26 | int dy = nodeA.y - nodeB.y; 27 | if (!nodeA.walkable) { 28 | return nil; 29 | } 30 | 31 | if(trackArr) { 32 | nodeA.tested = YES; 33 | [trackArr addObject:[nodeA copy]]; 34 | } 35 | 36 | if (nodeA == endNode) { 37 | return nodeA; 38 | } 39 | 40 | // check for forced neighbors 41 | // along the diagonal 42 | if (dx != 0 && dy != 0) { 43 | // when moving diagonally, must check for vertical/horizontal jump points 44 | PFNode *t1Node = [grid getNodeAtX:(x + dx) andY:(y)]; 45 | PFNode *t2Node = [grid getNodeAtX:(x) andY:(y + dy)]; 46 | if ([self jump:t1Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr] 47 | || [self jump:t2Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]) { 48 | return nodeA; 49 | } 50 | } 51 | // horizontally/vertically 52 | else { 53 | if( dx != 0 ) { // moving along x 54 | PFNode *t1Node = [grid getNodeAtX:(x) andY:(y - 1)]; 55 | PFNode *t2Node = [grid getNodeAtX:(x - dx) andY:(y - 1)]; 56 | PFNode *t3Node = [grid getNodeAtX:(x) andY:(y + 1)]; 57 | PFNode *t4Node = [grid getNodeAtX:(x - dx) andY:(y + 1)]; 58 | 59 | if((t1Node.walkable && !t2Node.walkable) 60 | || (t3Node.walkable && !t4Node.walkable)) { 61 | return nodeA; 62 | } 63 | } 64 | else if(dy != 0) { 65 | PFNode *t1Node = [grid getNodeAtX:(x - 1) andY:(y)]; 66 | PFNode *t2Node = [grid getNodeAtX:(x - 1) andY:(y - dy)]; 67 | PFNode *t3Node = [grid getNodeAtX:(x + 1) andY:(y)]; 68 | PFNode *t4Node = [grid getNodeAtX:(x + 1) andY:(y - dy)]; 69 | 70 | if((t1Node.walkable && !t2Node.walkable) 71 | || (t3Node.walkable && !t4Node.walkable)) { 72 | return nodeA; 73 | } 74 | 75 | // When moving vertically, must check for horizontal jump points 76 | // if (this._jump(x + 1, y, x, y) || this._jump(x - 1, y, x, y)) { 77 | // return [x, y]; 78 | // } 79 | } 80 | } 81 | 82 | // moving diagonally, must make sure one of the vertical/horizontal 83 | // neighbors is open to allow the path 84 | PFNode *t1Node = [grid getNodeAtX:(x + dx) andY:(y)]; 85 | PFNode *t2Node = [grid getNodeAtX:(x) andY:(y + dy)]; 86 | if (t1Node.walkable && t2Node.walkable) { 87 | PFNode *reNodeA = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 88 | return [self jump:reNodeA withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]; 89 | } else { 90 | return nil; 91 | } 92 | } 93 | 94 | 95 | /** 96 | * Find the neighbors for the given node. If the node has a parent, 97 | * prune the neighbors based on the jump point search algorithm, otherwise 98 | * return all available neighbors. 99 | * @return {Array.<[number, number]>} The neighbors found. 100 | */ 101 | - (NSArray *)findNeighbors:(PFNode *)node withGrid:(PFGrid *)grid { 102 | 103 | PFNode *parent = node.parent; 104 | NSMutableArray *neighbors = [NSMutableArray array]; 105 | int x=node.x, y=node.y, dx, dy; 106 | 107 | // directed pruning: can ignore most neighbors, unless forced. 108 | if (parent) { 109 | // get the normalized direction of travel 110 | dx = (x - parent.x) / MAX(abs(x - parent.x), 1); 111 | dy = (y - parent.y) / MAX(abs(y - parent.y), 1); 112 | 113 | PFNode *cNode = nil; 114 | // search diagonally 115 | if (dx != 0 && dy != 0) { 116 | PFNode *c1Node = [grid getNodeAtX:x andY:(y + dy)]; 117 | PFNode *c2Node = [grid getNodeAtX:(x + dx) andY:y]; 118 | 119 | if (c1Node.walkable) { 120 | [neighbors addObject:c1Node]; 121 | } 122 | if (c2Node.walkable) { 123 | [neighbors addObject:c2Node]; 124 | } 125 | if (c1Node.walkable && c2Node.walkable) { 126 | cNode = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 127 | cNode?[neighbors addObject:cNode]:NO; 128 | } 129 | } 130 | // search horizontally/vertically 131 | else { 132 | BOOL isNextWalkable; 133 | if (dx != 0) { 134 | PFNode *c1Node = [grid getNodeAtX:(x + dx) andY:y]; 135 | PFNode *c2Node = [grid getNodeAtX:x andY:(y + 1)]; 136 | PFNode *c3Node = [grid getNodeAtX:x andY:(y - 1)]; 137 | 138 | isNextWalkable = c1Node.walkable; 139 | BOOL isTopWalkable = c2Node.walkable; 140 | BOOL isBottomWalkable = c3Node.walkable; 141 | 142 | if (isNextWalkable) { 143 | [neighbors addObject:c1Node]; 144 | if (isTopWalkable) { 145 | cNode = [grid getNodeAtX:(x + dx) andY:(y + 1)]; 146 | cNode?[neighbors addObject:cNode]:NO; 147 | } 148 | if (isBottomWalkable) { 149 | cNode = [grid getNodeAtX:(x + dx) andY:(y - 1)]; 150 | cNode?[neighbors addObject:cNode]:NO; 151 | } 152 | } 153 | if (isTopWalkable) { 154 | [neighbors addObject:c2Node]; 155 | } 156 | if (isBottomWalkable) { 157 | [neighbors addObject:c3Node]; 158 | } 159 | } 160 | else if (dy != 0) { 161 | PFNode *c1Node = [grid getNodeAtX:x andY:(y + dy)]; 162 | PFNode *c2Node = [grid getNodeAtX:(x + 1) andY:y]; 163 | PFNode *c3Node = [grid getNodeAtX:(x - 1) andY:y]; 164 | 165 | isNextWalkable = c1Node.walkable; 166 | BOOL isRightWalkable = c2Node.walkable; 167 | BOOL isLeftWalkable = c3Node.walkable; 168 | 169 | if (isNextWalkable) { 170 | [neighbors addObject:c1Node]; 171 | if (isRightWalkable) { 172 | cNode = [grid getNodeAtX:(x + 1) andY:(y + dy)]; 173 | cNode?[neighbors addObject:cNode]:NO; 174 | } 175 | if (isLeftWalkable) { 176 | cNode = [grid getNodeAtX:(x - 1) andY:(y + dy)]; 177 | cNode?[neighbors addObject:cNode]:NO; 178 | } 179 | } 180 | if (isRightWalkable) { 181 | [neighbors addObject:c2Node]; 182 | } 183 | if (isLeftWalkable) { 184 | [neighbors addObject:c3Node]; 185 | } 186 | } 187 | } 188 | } 189 | // return all neighbors 190 | else { 191 | 192 | NSArray *neighborNodes = [grid getNeighborsWith:node diagonalMovement:DiagonalMovement_OnlyWhenNoObstacles]; 193 | PFNode *neighborNode=nil; 194 | for (int i=0; i child), stopping only when a 15 | * jump point is found. 16 | * @protected 17 | * @return {Array.<[number, number]>} The x, y coordinate of the jump point 18 | * found, or null if not found 19 | */ 20 | - (PFNode *)jump:(PFNode*)nodeA withNode:(PFNode*)nodeB withEndNode:(PFNode*)endNode withGrid:(PFGrid *)grid andTrackArr:(NSMutableArray*)trackArr { 21 | 22 | int x=nodeA.x, y=nodeA.y; 23 | int dx = nodeA.x - nodeB.x; 24 | int dy = nodeA.y - nodeB.y; 25 | if (!nodeA.walkable) { 26 | return nil; 27 | } 28 | 29 | if(trackArr) { 30 | nodeA.tested = YES; 31 | [trackArr addObject:[nodeA copy]]; 32 | } 33 | 34 | if (nodeA == endNode) { 35 | return nodeA; 36 | } 37 | 38 | 39 | if (dx != 0) { 40 | PFNode *t1Node = [grid getNodeAtX:(x) andY:(y - 1)]; 41 | PFNode *t2Node = [grid getNodeAtX:(x - dx) andY:(y - 1)]; 42 | PFNode *t3Node = [grid getNodeAtX:(x) andY:(y + 1)]; 43 | PFNode *t4Node = [grid getNodeAtX:(x - dx) andY:(y + 1)]; 44 | 45 | if ((t1Node.walkable && !t2Node.walkable) 46 | || (t3Node.walkable && !t4Node.walkable)) { 47 | return nodeA; 48 | } 49 | } else if (dy != 0) { 50 | PFNode *t1Node = [grid getNodeAtX:(x - 1) andY:(y)]; 51 | PFNode *t2Node = [grid getNodeAtX:(x - 1) andY:(y - dy)]; 52 | PFNode *t3Node = [grid getNodeAtX:(x + 1) andY:(y)]; 53 | PFNode *t4Node = [grid getNodeAtX:(x + 1) andY:(y - dy)]; 54 | 55 | if((t1Node.walkable && !t2Node.walkable) 56 | || (t3Node.walkable && !t4Node.walkable)) { 57 | return nodeA; 58 | } 59 | 60 | //When moving vertically, must check for horizontal jump points 61 | // PFNode *reNodeA1 = [grid getNodeAtX:(x + 1) andY:(y)]; 62 | // PFNode *reNodeA2 = [grid getNodeAtX:(x - 1) andY:(y)]; 63 | if ([self jump:t3Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr] 64 | || [self jump:t1Node withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]) { 65 | return nodeA; 66 | } 67 | } else { 68 | // throw new Error("Only horizontal and vertical movements are allowed"); 69 | NSAssert(NO, @"Only horizontal and vertical movements are allowed"); 70 | } 71 | 72 | PFNode *reNode = [grid getNodeAtX:(x + dx) andY:(y + dy)]; 73 | return [self jump:reNode withNode:nodeA withEndNode:endNode withGrid:grid andTrackArr:trackArr]; 74 | } 75 | 76 | 77 | /** 78 | * Find the neighbors for the given node. If the node has a parent, 79 | * prune the neighbors based on the jump point search algorithm, otherwise 80 | * return all available neighbors. 81 | * @return {Array.<[number, number]>} The neighbors found. 82 | */ 83 | - (NSArray *)findNeighbors:(PFNode *)node withGrid:(PFGrid *)grid { 84 | 85 | PFNode *parent = node.parent; 86 | NSMutableArray *neighbors = [NSMutableArray array]; 87 | int x=node.x, y=node.y, dx, dy; 88 | 89 | // directed pruning: can ignore most neighbors, unless forced. 90 | if (parent) { 91 | // get the normalized direction of travel 92 | dx = (x - parent.x) / MAX(abs(x - parent.x), 1); 93 | dy = (y - parent.y) / MAX(abs(y - parent.y), 1); 94 | 95 | PFNode *c1Node, *c2Node, *c3Node; 96 | 97 | if (dx != 0) { 98 | c1Node = [grid getNodeAtX:x andY:(y - 1)]; 99 | c2Node = [grid getNodeAtX:x andY:(y + 1)]; 100 | c3Node = [grid getNodeAtX:(x + dx) andY:y]; 101 | 102 | } else if (dy != 0) { 103 | c1Node = [grid getNodeAtX:(x - 1) andY:y]; 104 | c2Node = [grid getNodeAtX:(x + 1) andY:y]; 105 | c3Node = [grid getNodeAtX:x andY:(y + dy)]; 106 | } 107 | 108 | if (c1Node.walkable) { 109 | [neighbors addObject:c1Node]; 110 | } 111 | if (c2Node.walkable) { 112 | [neighbors addObject:c2Node]; 113 | } 114 | if (c3Node.walkable) { 115 | [neighbors addObject:c3Node]; 116 | } 117 | } 118 | // return all neighbors 119 | else { 120 | 121 | NSArray *neighborNodes = [grid getNeighborsWith:node diagonalMovement:DiagonalMovement_Never]; 122 | PFNode *neighborNode=nil; 123 | for (int i=0; i0) { 60 | // pop the position of node which has the minimum `f` value. 61 | [openList sortUsingSelector:@selector(descFWeightSort:)]; 62 | node = [openList lastObject]; 63 | [openList removeLastObject]; 64 | node.closed = YES; 65 | 66 | // track 67 | if (trackArrForTest) {[(*trackArrForTest) addObject:[node copy]];} 68 | 69 | if (node == endNode) { 70 | return [PFUtil expandPath:[PFUtil backtrace:endNode]]; 71 | } 72 | 73 | [self identifySuccessors:node withEndNode:endNode andGrid:grid andOpenList:openList andTrackFinding:trackArrForTest?(*trackArrForTest):nil]; 74 | } 75 | 76 | // fail to find the path 77 | return nil; 78 | } 79 | 80 | 81 | /** 82 | * Identify successors for the given node. Runs a jump point search in the 83 | * direction of each available neighbor, adding any points found to the open 84 | * list. 85 | * @protected 86 | */ 87 | - (void)identifySuccessors:(PFNode*)node withEndNode:(PFNode*)endNode andGrid:(PFGrid *)grid andOpenList:(NSMutableArray *)openList andTrackFinding:(NSMutableArray*)trackArrForTest { 88 | 89 | int x=node.x, y=node.y, endX=endNode.x, endY=endNode.y, jx, jy; 90 | float ng = 0; 91 | 92 | Heuristic *octileHeur = [self createHeuristicWithType:HeuristicTypeOctile]; 93 | 94 | NSArray *neighbors = [self findNeighbors:node withGrid:grid]; 95 | PFNode *neighbor = nil; 96 | PFNode *jumpNode = nil; 97 | NSMutableArray *trackArr = nil; 98 | for(int i = 0; i < neighbors.count; i++) { 99 | neighbor = neighbors[i]; 100 | 101 | // track 102 | if (trackArrForTest) { 103 | trackArr = [NSMutableArray array]; 104 | } 105 | 106 | jumpNode = [self jump:neighbor withNode:node withEndNode:endNode withGrid:grid andTrackArr:trackArr]; 107 | 108 | if (jumpNode) { 109 | jx = jumpNode.x; 110 | jy = jumpNode.y; 111 | 112 | if (jumpNode.closed) { 113 | continue; 114 | } 115 | 116 | // include distance, as parent may not be immediately adjacent: 117 | int d = [octileHeur performAlgorithmWithX:abs(jx - x) andY:abs(jy - y)]; 118 | ng = node.g + d; // next `g` value 119 | 120 | if (jumpNode.opened==0 || ng < jumpNode.g) { 121 | jumpNode.g = ng; 122 | jumpNode.h = jumpNode.h==0 ? [self calculateHeuristicValueWithX:abs(jx - endX) andY:abs(jy - endY)] : jumpNode.h; 123 | jumpNode.f = jumpNode.g + jumpNode.h; 124 | jumpNode.parent = node; 125 | 126 | if (jumpNode.opened==0) { 127 | [openList addObject:jumpNode]; 128 | jumpNode.opened = 1; 129 | } 130 | 131 | // track 132 | if (trackArr) {[trackArr addObject:[jumpNode copy]];} 133 | } 134 | } 135 | 136 | // track 137 | if (trackArrForTest && trackArr.count>0) { [trackArrForTest addObject:trackArr]; } 138 | } 139 | } 140 | 141 | 142 | /** 143 | * Implemented in children class 144 | */ 145 | - (PFNode *)jump:(PFNode*)nodeA withNode:(PFNode*)nodeB withEndNode:(PFNode*)endNode withGrid:(PFGrid *)grid andTrackArr:(NSMutableArray*)trackArr { 146 | return nil; 147 | } 148 | 149 | 150 | /** 151 | * Implemented in children class 152 | */ 153 | - (NSArray *)findNeighbors:(PFNode *)node withGrid:(PFGrid *)grid { 154 | return nil; 155 | } 156 | 157 | 158 | 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PathFindingForObjC 2 | =================== 3 | 4 | A Comprehensive PathFinding Library for Objective-C. 5 | Based on [PathFinding.js](https://github.com/qiao/PathFinding.js) by [@qiao](https://github.com/qiao). 6 | 7 | ## Installation 8 | #### Cocoapods 9 | * Edit your Podfile 10 | 11 | ``` ruby 12 | pod 'PathFindingForObjC' 13 | ``` 14 | or use the `master` branch of the repo : 15 | 16 | ``` ruby 17 | pod 'PathFindingForObjC', :git => 'https://github.com/wbcyclist/PathFindingForObjC.git' 18 | ``` 19 | 20 | * Add `#import ` to your source file. 21 | 22 | > **Disable logging** 23 | > 24 | > add this code in `Podfile` 25 | > 26 | ``` ruby 27 | post_install do |installer| 28 | installer.project.targets.each do |target| 29 | target.build_configurations.each do |config| 30 | if target.name.include? 'PathFindingForObjC' 31 | preprocessorMacros = config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] 32 | if preprocessorMacros.nil? 33 | preprocessorMacros = ['$(inherited)', 'COCOAPODS=1']; 34 | end 35 | preprocessorMacros << 'PF_DEBUG=0' 36 | config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = preprocessorMacros 37 | end 38 | end 39 | end 40 | end 41 | ``` 42 | 43 | #### Manually 44 | 1. Download the [latest code version](https://github.com/wbcyclist/PathFindingForObjC/archive/master.zip) or add the repository as a git submodule to your git-tracked project. 45 | 1. Open your project in Xcode, then drag and drop `PathFindingForObjC/PathFindingForObjC` into your project 46 | 2. Add `#import PathFinding.h` to your source file. 47 | 48 | > **Disable logging** 49 | > 50 | > Click on your `Project` Target, head over to `Build Settings` and search for `Preprocessor Macros`. add `PF_DEBUG=0` to `Debug` Configuration. 51 | ![](https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/master/demo/Screenshot_01.png) 52 | 53 | ## Basic Usage 54 | ``` objective-c 55 | PathFinding *finder = [[PathFinding alloc] initWithMapSize:CGSizeMake(6, 5) 56 | tileSize:CGSizeMake(1, 1) 57 | coordsOrgin:CGPointZero]; 58 | finder.heuristicType = HeuristicTypeManhattan; 59 | finder.movementType = DiagonalMovement_Never; 60 | 61 | // add blocks 62 | [finder addBlockTilePositions:@[PF_CGPointToNSValue(CGPointMake(1, 2)), 63 | PF_CGPointToNSValue(CGPointMake(2, 2)), 64 | PF_CGPointToNSValue(CGPointMake(3, 2)) 65 | ]]; 66 | // set start point 67 | finder.startPoint = CGPointMake(2, 3); 68 | // set end point 69 | finder.endPoint = CGPointMake(2, 1); 70 | // get result 71 | NSArray *foundPaths = [finder findPathing:PathfindingAlgorithm_AStar IsConvertToOriginCoords:YES]; 72 | ``` 73 | ##### debug log: 74 | >:mag: :mag: :mag: :mag: :mag: :mag: 75 | 76 | >:mag: :mag: :pray: :heartpulse: :heartpulse: :mag: 77 | 78 | >:mag: :underage: :underage: :underage: :heartpulse: :mag: 79 | 80 | >:mag: :mag: :no_good: :heartpulse: :heartpulse: :mag: 81 | 82 | >:mag: :mag: :mag: :mag: :mag: :mag: 83 | 84 | #### Options 85 | `HeuristicType` : 86 | 87 | * HeuristicTypeManhattan 88 | * HeuristicTypeEuclidean 89 | * HeuristicTypeOctile 90 | * HeuristicTypeChebyshev 91 | 92 | `DiagonalMovement` : 93 | 94 | * DiagonalMovement_Always 95 | * DiagonalMovement_Never 96 | * DiagonalMovement_IfAtMostOneObstacle 97 | * DiagonalMovement_OnlyWhenNoObstacles 98 | 99 | `PathfindingAlgorithm` : 100 | 101 | * PathfindingAlgorithm_AStar 102 | * PathfindingAlgorithm_BestFirstSearch 103 | * PathfindingAlgorithm_Dijkstra 104 | * PathfindingAlgorithm_JumpPointSearch 105 | * PathfindingAlgorithm_BreadthFirstSearch 106 | * PathfindingAlgorithm_BiAStar 107 | * PathfindingAlgorithm_BiBestFirst 108 | * PathfindingAlgorithm_BiDijkstra 109 | * PathfindingAlgorithm_BiBreadthFirst 110 | 111 | 112 | 113 | ##DEMO 114 | * OSX [Download](https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/master/demo/PathFinding-Mac.zip) 115 |

116 | OS X 117 |

118 | 119 | * iOS 120 |

121 | iOS 122 |

123 | 124 | ## License 125 | * MIT -------------------------------------------------------------------------------- /demo/PathFinding-Mac.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/demo/PathFinding-Mac.zip -------------------------------------------------------------------------------- /demo/PathFinding_ScreenShot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/demo/PathFinding_ScreenShot.png -------------------------------------------------------------------------------- /demo/PathFinding_ScreenShot_iOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/demo/PathFinding_ScreenShot_iOS.png -------------------------------------------------------------------------------- /demo/Screenshot_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wbcyclist/PathFindingForObjC/1d62d2346a07dc3a93786a1bfaf918b9175b24ec/demo/Screenshot_01.png --------------------------------------------------------------------------------