├── .github └── workflows │ └── c-cpp.yml ├── .gitignore ├── .travis.yml ├── README.md ├── cpp ├── 8puzzle.cpp ├── findpath.cpp ├── fsa.h ├── makefile ├── min_path_to_Bucharest.cpp ├── stlastar.h └── tests.cpp └── license.txt /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master", "stage" ] 6 | pull_request: 7 | branches: [ "master", "stage" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: make test 17 | working-directory: ./cpp 18 | run: make test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | cpp/8puzzle 3 | cpp/findpath 4 | cpp/minpathbucharest 5 | cpp/tests 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - g++ 4 | - clang 5 | 6 | script: 7 | cd cpp 8 | make 9 | make test 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## astar-algorithm 2 | 3 | ![Build Status](https://github.com/justinhj/astar-algorithm-cpp/workflows/C/C++%20CI/badge.svg) 4 | 5 | ### Summary 6 | 7 | An efficient implementation in C++ of the A* algorithm, designed to be used in high performance realtime applications (video games) and includes an optional pool memory allocator. 8 | 9 | It accompanies this A* tutorial: 10 | https://www.heyes-jones.com/astar.php 11 | 12 | The A* algorithm is due to Hart, Nillson and Raphael. See https://ieeexplore.ieee.org/document/4082128. 13 | 14 | This repository is dedicated to the memory of [Nils Nilsson](https://ai.stanford.edu/~nilsson/) who sadly passed away in 2019. 15 | 16 | Looking for a C# version? Checkout the companion repository [astar-algorithm-csharp](https://github.com/justinhj/astar-algorithm-csharp) for a port by [@scaryg](https://github.com/scaryg) 17 | 18 | ### Star History 19 | 20 | [![Star History Chart](https://api.star-history.com/svg?repos=justinhj/astar-algorithm-cpp&type=Date)](https://star-history.com/#justinhj/astar-algorithm-cpp&Date) 21 | 22 | ### Release notes 23 | 24 | [v1.2](https://github.com/justinhj/astar-algorithm-cpp/releases/tag/v1.2) 25 | Breaking changes! C++ 11 is now the minimum required C++ standard. 26 | User is now required to provide a Hash function for their Node type. Thanks to a contribution from @btdubs the closed 27 | list is now an unordered_set and this greatly speeds up the execution time of the algorithm. 28 | Check the included demo code for examples of the Hash implementation for various Node types. 29 | 30 | [v1.1](https://github.com/justinhj/astar-algorithm-cpp/releases/tag/v1.1) 31 | Code cleanup and final version that does not require C++11 32 | 33 | [v1.0](https://github.com/justinhj/astar-algorithm-cpp/releases/tag/v1.0) 34 | Initial release once API stable. 35 | 36 | ### License 37 | 38 | This software is released under the MIT License, see license.txt 39 | 40 | ### Commercial Use 41 | 42 | This software has been used in a number of AAA video games, which is an area of software that relies on efficiency and reliability. In addition it has been used in a number of academic and personal projects that require efficient search. Please 43 | 44 | Commercial users of the code are encouraged to make a donation to http://www.unicef.org/ if they find this project useful. 45 | 46 | ### Projects using this code 47 | 48 | If you wish to be added to the list of known products/educational projects using the code please contact me. 49 | 50 | * Gun, Activision 51 | * Company of Heroes (various versions), Relic Entertainment 52 | * Angel Engine, a game prototyping engine http://code.google.com/p/angel-engine/ 53 | * War of Sonria, a strategy war game on PSP and Playstation 3 by Playground Squad 54 | * Lighthouses AI contest https://github.com/marcan/lighthouses_aicontest 55 | * Self-Driving Car Engineer Nanodegree Program https://github.com/vanAken/CarND-Path-Planning-Project 56 | 57 | ### Compilation 58 | 59 | Enter the cpp folder and run make 60 | 61 | #### Introduction 62 | 63 | This implementation is intended to be simple to read yet fairly 64 | efficient. To build it you can compile, with any recent C++ compiler, 65 | the following files : 66 | 67 | For 8-puzzle solver 68 | 69 | * 8puzzle.cpp 70 | * stlastar.h 71 | * optionally fsa.h 72 | 73 | #### Command line 74 | 75 | 8puzzle with no arguments runs with one of the boards in the cpp file, you can 76 | select the one you want changing the conditional compiliation instructions. Or if you 77 | prefer pass in a board on the command line using digits for the tile positions, where 78 | zero is the space. The board runs from left to right, each row at a time: 79 | 80 | 8puzzle 013824765 81 | 82 | For path finder 83 | * findpath.cpp 84 | * stlastar.h 85 | * optionally fsa.h 86 | 87 | pathfind has no arguments. You can edit the simple map in pathfind.cpp and the start 88 | and goal co-ordinates to experiement with the pathfinder. 89 | 90 | #### Fixed size allocator 91 | 92 | FSA is just a simple memory pool that uses a doubly linked list of available nodes in an array. This is 93 | a very efficient way to manage memory. It has no automatic resizing, so you must account for the fact it 94 | will use a fixed block of memory per instance and will report an error when memory is exhausted. 95 | 96 | As mentioned briefly in the tutorial you can enable and disable the faster memory allocation. This allocates 97 | a fixed size block of memory, so you have to specify this size with the astar constructor. You need to enlarge 98 | it if you hit an out of memory assert during the search. 99 | 100 | Compatibility notes: 101 | 102 | This version of the code requires any standards compliant C++ using std C++11 103 | -------------------------------------------------------------------------------- /cpp/8puzzle.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // STL A* Search implementation 3 | // (C)2001 Justin Heyes-Jones 4 | // 5 | // This uses my A* code to solve the 8-puzzle 6 | 7 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | using namespace std; 18 | 19 | // Configuration 20 | 21 | #define NUM_TIMES_TO_RUN_SEARCH 1 22 | #define DISPLAY_SOLUTION_FORWARDS 1 23 | #define DISPLAY_SOLUTION_BACKWARDS 0 24 | #define DISPLAY_SOLUTION_INFO 1 25 | #define DEBUG_LISTS 0 26 | 27 | // AStar search class 28 | #include "stlastar.h" // See header for copyright and usage information 29 | 30 | // Global data 31 | 32 | #define BOARD_WIDTH (3) 33 | #define BOARD_HEIGHT (3) 34 | 35 | #define GM_TILE (-1) 36 | #define GM_SPACE (0) 37 | #define GM_OFF_BOARD (1) 38 | 39 | // Definitions 40 | 41 | // To use the search class you must define the following calls... 42 | 43 | // Data 44 | // Your own state space information 45 | // Functions 46 | // (Optional) Constructor. 47 | // Nodes are created by the user, so whether you use a 48 | // constructor with parameters as below, or just set the object up after the 49 | // constructor, is up to you. 50 | // 51 | // (Optional) Destructor. 52 | // The destructor will be called if you create one. You 53 | // can rely on the default constructor unless you dynamically allocate something in 54 | // your data 55 | // 56 | // float GoalDistanceEstimate( PuzzleState &nodeGoal ); 57 | // Return the estimated cost to goal from this node (pass reference to goal node) 58 | // 59 | // bool IsGoal( PuzzleState &nodeGoal ); 60 | // Return true if this node is the goal. 61 | // 62 | // bool GetSuccessors( AStarSearch *astarsearch ); 63 | // For each successor to this state call the AStarSearch's AddSuccessor call to 64 | // add each one to the current search - return false if you are out of memory and the search 65 | // will fail 66 | // 67 | // float GetCost( PuzzleState *successor ); 68 | // Return the cost moving from this state to the state of successor 69 | // 70 | // bool IsSameState( PuzzleState &rhs ); 71 | // Return true if the provided state is the same as this state 72 | 73 | // Here the example is the 8-puzzle state ... 74 | class PuzzleState 75 | { 76 | 77 | public: 78 | 79 | // defs 80 | 81 | typedef enum 82 | { 83 | TL_SPACE, 84 | TL_1, 85 | TL_2, 86 | TL_3, 87 | TL_4, 88 | TL_5, 89 | TL_6, 90 | TL_7, 91 | TL_8 92 | 93 | } TILE; 94 | 95 | // data 96 | 97 | static TILE g_goal[ BOARD_WIDTH*BOARD_HEIGHT]; 98 | static TILE g_start[ BOARD_WIDTH*BOARD_HEIGHT]; 99 | 100 | // the tile data for the 8-puzzle 101 | TILE tiles[ BOARD_WIDTH*BOARD_HEIGHT ]; 102 | 103 | // member functions 104 | 105 | PuzzleState() { 106 | memcpy( tiles, g_goal, sizeof( TILE ) * BOARD_WIDTH * BOARD_HEIGHT ); 107 | } 108 | 109 | PuzzleState( TILE *param_tiles ) 110 | { 111 | memcpy( tiles, param_tiles, sizeof( TILE ) * BOARD_WIDTH * BOARD_HEIGHT ); 112 | } 113 | 114 | float GoalDistanceEstimate( PuzzleState &nodeGoal ); 115 | bool IsGoal( PuzzleState &nodeGoal ); 116 | bool GetSuccessors( AStarSearch *astarsearch, PuzzleState *parent_node ); 117 | float GetCost( PuzzleState &successor ); 118 | bool IsSameState( PuzzleState &rhs ); 119 | size_t Hash(); 120 | 121 | void PrintNodeInfo(); 122 | 123 | private: 124 | // User stuff - Just add what you need to help you write the above functions... 125 | 126 | void GetSpacePosition( PuzzleState *pn, int *rx, int *ry ); 127 | bool LegalMove( TILE *StartTiles, TILE *TargetTiles, int spx, int spy, int tx, int ty ); 128 | int GetMap( int x, int y, TILE *tiles ); 129 | 130 | 131 | 132 | 133 | }; 134 | 135 | // Goal state 136 | PuzzleState::TILE PuzzleState::g_goal[] = 137 | { 138 | TL_1, 139 | TL_2, 140 | TL_3, 141 | TL_8, 142 | TL_SPACE, 143 | TL_4, 144 | TL_7, 145 | TL_6, 146 | TL_5, 147 | }; 148 | 149 | // Some nice Start states 150 | PuzzleState::TILE PuzzleState::g_start[] = 151 | { 152 | 153 | // Three example start states from Bratko's Prolog Programming for Artificial Intelligence 154 | 155 | #if 0 156 | // ex a - 4 steps 157 | TL_1 , 158 | TL_3 , 159 | TL_4 , 160 | TL_8 , 161 | TL_SPACE , 162 | TL_2 , 163 | TL_7 , 164 | TL_6 , 165 | TL_5 , 166 | 167 | #elif 0 168 | 169 | // ex b - 5 steps 170 | TL_2 , 171 | TL_8 , 172 | TL_3 , 173 | TL_1 , 174 | TL_6 , 175 | TL_4 , 176 | TL_7 , 177 | TL_SPACE , 178 | TL_5 , 179 | 180 | #elif 0 181 | 182 | // ex c - 18 steps 183 | TL_2 , 184 | TL_1 , 185 | TL_6 , 186 | TL_4 , 187 | TL_SPACE , 188 | TL_8 , 189 | TL_7 , 190 | TL_5 , 191 | TL_3 , 192 | 193 | #elif 0 194 | 195 | // nasty one - doesn't solve 196 | TL_6 , 197 | TL_3 , 198 | TL_SPACE , 199 | TL_4 , 200 | TL_8 , 201 | TL_5 , 202 | TL_7 , 203 | TL_2 , 204 | TL_1 , 205 | 206 | #elif 0 207 | 208 | // sent by email - does work though 209 | 210 | TL_1 , TL_2 , TL_3 , 211 | TL_4 , TL_5 , TL_6 , 212 | TL_8 , TL_7 , TL_SPACE , 213 | 214 | 215 | // from http://www.cs.utexas.edu/users/novak/asg-8p.html 216 | 217 | //Goal: Easy: Medium: Hard: Worst: 218 | 219 | //1 2 3 1 3 4 2 8 1 2 8 1 5 6 7 220 | //8 4 8 6 2 4 3 4 6 3 4 8 221 | //7 6 5 7 5 7 6 5 7 5 3 2 1 222 | 223 | 224 | #elif 0 225 | 226 | // easy 5 227 | TL_1 , 228 | TL_3 , 229 | TL_4 , 230 | 231 | TL_8 , 232 | TL_6 , 233 | TL_2 , 234 | 235 | TL_7 , 236 | TL_SPACE , 237 | TL_5 , 238 | 239 | 240 | #elif 0 241 | 242 | // medium 9 243 | TL_2 , 244 | TL_8 , 245 | TL_1 , 246 | 247 | TL_SPACE , 248 | TL_4 , 249 | TL_3 , 250 | 251 | TL_7 , 252 | TL_6 , 253 | TL_5 , 254 | 255 | #elif 0 256 | 257 | // hard 12 258 | TL_2 , 259 | TL_8 , 260 | TL_1 , 261 | 262 | TL_4 , 263 | TL_6 , 264 | TL_3 , 265 | 266 | TL_SPACE , 267 | TL_7 , 268 | TL_5 , 269 | 270 | #elif 1 271 | 272 | // worst 30 273 | TL_5 , 274 | TL_6 , 275 | TL_7 , 276 | 277 | TL_4 , 278 | TL_SPACE , 279 | TL_8 , 280 | 281 | TL_3 , 282 | TL_2 , 283 | TL_1 , 284 | 285 | #elif 0 286 | 287 | // 123 288 | // 784 289 | // 65 290 | 291 | // two move simple board 292 | TL_1 , 293 | TL_2 , 294 | TL_3 , 295 | 296 | TL_7 , 297 | TL_8 , 298 | TL_4 , 299 | 300 | TL_SPACE , 301 | TL_6 , 302 | TL_5 , 303 | 304 | #elif 0 305 | // a1 b2 c3 d4 e5 f6 g7 h8 306 | //C3,Blank,H8,A1,G8,F6,E5,D4,B2 307 | 308 | TL_3 , 309 | TL_SPACE , 310 | TL_8 , 311 | 312 | TL_1 , 313 | TL_8 , 314 | TL_6 , 315 | 316 | TL_5 , 317 | TL_4 , 318 | TL_2 , 319 | 320 | 321 | #endif 322 | 323 | }; 324 | 325 | bool PuzzleState::IsSameState( PuzzleState &rhs ) 326 | { 327 | 328 | for( int i=0; i<(BOARD_HEIGHT*BOARD_WIDTH); i++ ) 329 | { 330 | if( tiles[i] != rhs.tiles[i] ) 331 | { 332 | return false; 333 | } 334 | } 335 | 336 | return true; 337 | 338 | } 339 | 340 | // The 9 tiles positions can be encoded as digits 341 | size_t PuzzleState::Hash() 342 | { 343 | std::size_t hash = 0; 344 | for( size_t i = 0; i < (BOARD_HEIGHT * BOARD_WIDTH); i++ ) 345 | { 346 | hash ^= std::hash()(tiles[i]) + 0x9e3779b9 + (hash << 6) + (hash >> 2); 347 | } 348 | return hash; 349 | } 350 | 351 | void PuzzleState::PrintNodeInfo() 352 | { 353 | const int strSize = 100; 354 | char str[strSize]; 355 | snprintf( str, strSize, "%c %c %c\n%c %c %c\n%c %c %c\n", 356 | tiles[0] + '0', 357 | tiles[1] + '0', 358 | tiles[2] + '0', 359 | tiles[3] + '0', 360 | tiles[4] + '0', 361 | tiles[5] + '0', 362 | tiles[6] + '0', 363 | tiles[7] + '0', 364 | tiles[8] + '0' 365 | ); 366 | 367 | cout << str; 368 | } 369 | 370 | // Here's the heuristic function that estimates the distance from a PuzzleState 371 | // to the Goal. 372 | 373 | float PuzzleState::GoalDistanceEstimate( PuzzleState &nodeGoal ) 374 | { 375 | 376 | // Nilsson's sequence score 377 | 378 | int i, cx, cy, ax, ay, h = 0, s, t; 379 | 380 | // given a tile this returns the tile that should be clockwise 381 | TILE correct_follower_to[ BOARD_WIDTH * BOARD_HEIGHT ] = 382 | { 383 | TL_SPACE, // always wrong 384 | TL_2, 385 | TL_3, 386 | TL_4, 387 | TL_5, 388 | TL_6, 389 | TL_7, 390 | TL_8, 391 | TL_1, 392 | }; 393 | 394 | // given a table index returns the index of the tile that is clockwise to it 3*3 only 395 | int clockwise_tile_of[ BOARD_WIDTH * BOARD_HEIGHT ] = 396 | { 397 | 1, 398 | 2, // 012 399 | 5, // 345 400 | 0, // 678 401 | -1, // never called with center square 402 | 8, 403 | 3, 404 | 6, 405 | 7 406 | }; 407 | 408 | int tile_x[ BOARD_WIDTH * BOARD_HEIGHT ] = 409 | { 410 | /* TL_SPACE */ 1, 411 | /* TL_1 */ 0, 412 | /* TL_2 */ 1, 413 | /* TL_3 */ 2, 414 | /* TL_4 */ 2, 415 | /* TL_5 */ 2, 416 | /* TL_6 */ 1, 417 | /* TL_7 */ 0, 418 | /* TL_8 */ 0, 419 | }; 420 | 421 | int tile_y[ BOARD_WIDTH * BOARD_HEIGHT ] = 422 | { 423 | /* TL_SPACE */ 1, 424 | /* TL_1 */ 0, 425 | /* TL_2 */ 0, 426 | /* TL_3 */ 0, 427 | /* TL_4 */ 1, 428 | /* TL_5 */ 2, 429 | /* TL_6 */ 2, 430 | /* TL_7 */ 2, 431 | /* TL_8 */ 1, 432 | }; 433 | 434 | s=0; 435 | 436 | // score 1 point if centre is not correct 437 | if( tiles[(BOARD_HEIGHT*BOARD_WIDTH)/2] != nodeGoal.tiles[(BOARD_HEIGHT*BOARD_WIDTH)/2] ) 438 | { 439 | s = 1; 440 | } 441 | 442 | for( i=0; i<(BOARD_HEIGHT*BOARD_WIDTH); i++ ) 443 | { 444 | // this loop adds up the totaldist element in h and 445 | // the sequence score in s 446 | 447 | // the space does not count 448 | if( tiles[i] == TL_SPACE ) 449 | { 450 | continue; 451 | } 452 | 453 | // get correct x and y of this tile 454 | cx = tile_x[tiles[i]]; 455 | cy = tile_y[tiles[i]]; 456 | 457 | // get actual 458 | ax = i % BOARD_WIDTH; 459 | ay = i / BOARD_WIDTH; 460 | 461 | // add manhatten distance to h 462 | h += abs( cx-ax ); 463 | h += abs( cy-ay ); 464 | 465 | // no s score for center tile 466 | if( (ax == (BOARD_WIDTH/2)) && (ay == (BOARD_HEIGHT/2)) ) 467 | { 468 | continue; 469 | } 470 | 471 | // score 2 points if not followed by successor 472 | if( correct_follower_to[ tiles[i] ] != tiles[ clockwise_tile_of[ i ] ] ) 473 | { 474 | s += 2; 475 | } 476 | 477 | 478 | } 479 | 480 | // mult by 3 and add to h 481 | t = h + (3*s); 482 | 483 | return (float) t; 484 | 485 | } 486 | 487 | bool PuzzleState::IsGoal( PuzzleState &nodeGoal ) 488 | { 489 | return IsSameState( nodeGoal ); 490 | } 491 | 492 | // Helper 493 | // Return the x and y position of the space tile 494 | void PuzzleState::GetSpacePosition( PuzzleState *pn, int *rx, int *ry ) 495 | { 496 | int x,y; 497 | 498 | for( y=0; ytiles[(y*BOARD_WIDTH)+x] == TL_SPACE ) 503 | { 504 | *rx = x; 505 | *ry = y; 506 | 507 | return; 508 | } 509 | } 510 | } 511 | 512 | assert( false && "Something went wrong. There's no space on the board" ); 513 | 514 | } 515 | 516 | int PuzzleState::GetMap( int x, int y, TILE *tiles ) 517 | { 518 | 519 | if( x < 0 || 520 | x >= BOARD_WIDTH || 521 | y < 0 || 522 | y >= BOARD_HEIGHT 523 | ) 524 | return GM_OFF_BOARD; 525 | 526 | if( tiles[(y*BOARD_WIDTH)+x] == TL_SPACE ) 527 | { 528 | return GM_SPACE; 529 | } 530 | 531 | return GM_TILE; 532 | } 533 | 534 | // Given a node set of tiles and a set of tiles to move them into, do the move as if it was on a tile board 535 | // note : returns false if the board wasn't changed, and simply returns the tiles as they were in the target 536 | // spx and spy is the space position while tx and ty is the target move from position 537 | 538 | bool PuzzleState::LegalMove( TILE *StartTiles, TILE *TargetTiles, int spx, int spy, int tx, int ty ) 539 | { 540 | 541 | int t; 542 | 543 | if( GetMap( spx, spy, StartTiles ) == GM_SPACE ) 544 | { 545 | if( GetMap( tx, ty, StartTiles ) == GM_TILE ) 546 | { 547 | 548 | // copy tiles 549 | for( t=0; t<(BOARD_HEIGHT*BOARD_WIDTH); t++ ) 550 | { 551 | TargetTiles[t] = StartTiles[t]; 552 | } 553 | 554 | 555 | TargetTiles[ (ty*BOARD_WIDTH)+tx ] = StartTiles[ (spy*BOARD_WIDTH)+spx ]; 556 | TargetTiles[ (spy*BOARD_WIDTH)+spx ] = StartTiles[ (ty*BOARD_WIDTH)+tx ]; 557 | 558 | return true; 559 | } 560 | } 561 | 562 | 563 | return false; 564 | 565 | } 566 | 567 | // This generates the successors to the given PuzzleState. It uses a helper function called 568 | // AddSuccessor to give the successors to the AStar class. The A* specific initialisation 569 | // is done for each node internally, so here you just set the state information that 570 | // is specific to the application 571 | bool PuzzleState::GetSuccessors( AStarSearch *astarsearch, PuzzleState *parent_node ) 572 | { 573 | PuzzleState NewNode; 574 | 575 | int sp_x,sp_y; 576 | 577 | GetSpacePosition( this, &sp_x, &sp_y ); 578 | 579 | bool ret; 580 | 581 | if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x, sp_y-1 ) == true ) 582 | { 583 | ret = astarsearch->AddSuccessor( NewNode ); 584 | 585 | if( !ret ) return false; 586 | } 587 | 588 | if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x, sp_y+1 ) == true ) 589 | { 590 | ret = astarsearch->AddSuccessor( NewNode ); 591 | 592 | if( !ret ) return false; 593 | } 594 | 595 | if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x-1, sp_y ) == true ) 596 | { 597 | ret = astarsearch->AddSuccessor( NewNode ); 598 | 599 | if( !ret ) return false; 600 | } 601 | 602 | if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x+1, sp_y ) == true ) 603 | { 604 | ret = astarsearch->AddSuccessor( NewNode ); 605 | 606 | if( !ret ) return false; 607 | } 608 | 609 | return true; 610 | } 611 | 612 | // given this node, what does it cost to move to successor. In the case 613 | // of our map the answer is the map terrain value at this node since that is 614 | // conceptually where we're moving 615 | 616 | float PuzzleState::GetCost( PuzzleState &successor ) 617 | { 618 | return 1.0f; // I love it when life is simple 619 | 620 | } 621 | 622 | 623 | // Main 624 | 625 | int main( int argc, char *argv[] ) 626 | { 627 | 628 | cout << "STL A* 8-puzzle solver implementation\n(C)2001 Justin Heyes-Jones\n"; 629 | 630 | if( argc > 1 ) 631 | { 632 | int i = 0; 633 | int c; 634 | 635 | while( (c = argv[1][i]) ) 636 | { 637 | if( isdigit( c ) ) 638 | { 639 | int num = (c - '0'); 640 | 641 | PuzzleState::g_start[i] = static_cast(num); 642 | 643 | } 644 | 645 | i++; 646 | } 647 | 648 | 649 | } 650 | 651 | // Create an instance of the search class... 652 | 653 | AStarSearch astarsearch; 654 | 655 | int NumTimesToSearch = NUM_TIMES_TO_RUN_SEARCH; 656 | 657 | while( NumTimesToSearch-- ) 658 | { 659 | 660 | // Create a start state 661 | PuzzleState nodeStart( PuzzleState::g_start ); 662 | 663 | // Define the goal state 664 | PuzzleState nodeEnd( PuzzleState::g_goal ); 665 | 666 | // Set Start and goal states 667 | astarsearch.SetStartAndGoalStates( nodeStart, nodeEnd ); 668 | 669 | unsigned int SearchState; 670 | 671 | unsigned int SearchSteps = 0; 672 | 673 | do 674 | { 675 | SearchState = astarsearch.SearchStep(); 676 | 677 | #if DEBUG_LISTS 678 | 679 | float f,g,h; 680 | 681 | cout << "Search step " << SearchSteps << endl; 682 | 683 | cout << "Open:\n"; 684 | PuzzleState *p = astarsearch.GetOpenListStart( f,g,h ); 685 | while( p ) 686 | { 687 | ((PuzzleState *)p)->PrintNodeInfo(); 688 | cout << "f: " << f << " g: " << g << " h: " << h << "\n\n"; 689 | 690 | p = astarsearch.GetOpenListNext( f,g,h ); 691 | 692 | } 693 | 694 | cout << "Closed:\n"; 695 | p = astarsearch.GetClosedListStart( f,g,h ); 696 | while( p ) 697 | { 698 | p->PrintNodeInfo(); 699 | cout << "f: " << f << " g: " << g << " h: " << h << "\n\n"; 700 | 701 | p = astarsearch.GetClosedListNext( f,g,h ); 702 | } 703 | 704 | #endif 705 | 706 | // Test cancel search 707 | #if 0 708 | int StepCount = astarsearch.GetStepCount(); 709 | if( StepCount == 10 ) 710 | { 711 | astarsearch.CancelSearch(); 712 | } 713 | #endif 714 | SearchSteps++; 715 | } 716 | while( SearchState == AStarSearch::SEARCH_STATE_SEARCHING ); 717 | 718 | if( SearchState == AStarSearch::SEARCH_STATE_SUCCEEDED ) 719 | { 720 | #if DISPLAY_SOLUTION_FORWARDS 721 | cout << "Search found goal state\n"; 722 | #endif 723 | PuzzleState *node = astarsearch.GetSolutionStart(); 724 | 725 | #if DISPLAY_SOLUTION_FORWARDS 726 | cout << "Displaying solution\n"; 727 | #endif 728 | int steps = 0; 729 | 730 | #if DISPLAY_SOLUTION_FORWARDS 731 | node->PrintNodeInfo(); 732 | cout << endl; 733 | #endif 734 | for( ;; ) 735 | { 736 | node = astarsearch.GetSolutionNext(); 737 | 738 | if( !node ) 739 | { 740 | break; 741 | } 742 | 743 | #if DISPLAY_SOLUTION_FORWARDS 744 | node->PrintNodeInfo(); 745 | cout << endl; 746 | #endif 747 | steps ++; 748 | 749 | }; 750 | 751 | #if DISPLAY_SOLUTION_FORWARDS 752 | // todo move step count into main algorithm 753 | cout << "Solution steps " << steps << endl; 754 | #endif 755 | 756 | //////////// 757 | 758 | node = astarsearch.GetSolutionEnd(); 759 | 760 | #if DISPLAY_SOLUTION_BACKWARDS 761 | cout << "Displaying reverse solution\n"; 762 | #endif 763 | steps = 0; 764 | 765 | node->PrintNodeInfo(); 766 | cout << endl; 767 | for( ;; ) 768 | { 769 | node = astarsearch.GetSolutionPrev(); 770 | 771 | if( !node ) 772 | { 773 | break; 774 | } 775 | #if DISPLAY_SOLUTION_BACKWARDS 776 | node->PrintNodeInfo(); 777 | cout << endl; 778 | #endif 779 | steps ++; 780 | 781 | }; 782 | 783 | #if DISPLAY_SOLUTION_BACKWARDS 784 | cout << "Solution steps " << steps << endl; 785 | #endif 786 | 787 | ////////////// 788 | 789 | // Once you're done with the solution you can free the nodes up 790 | astarsearch.FreeSolutionNodes(); 791 | 792 | } 793 | else if( SearchState == AStarSearch::SEARCH_STATE_FAILED ) 794 | { 795 | #if DISPLAY_SOLUTION_INFO 796 | cout << "Search terminated. Did not find goal state\n"; 797 | #endif 798 | } 799 | else if( SearchState == AStarSearch::SEARCH_STATE_OUT_OF_MEMORY ) 800 | { 801 | #if DISPLAY_SOLUTION_INFO 802 | cout << "Search terminated. Out of memory\n"; 803 | #endif 804 | } 805 | 806 | 807 | 808 | // Display the number of loops the search went through 809 | #if DISPLAY_SOLUTION_INFO 810 | cout << "SearchSteps : " << astarsearch.GetStepCount() << endl; 811 | #endif 812 | } 813 | 814 | return 0; 815 | } 816 | 817 | 818 | -------------------------------------------------------------------------------- /cpp/findpath.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | 3 | // STL A* Search implementation 4 | // (C)2001 Justin Heyes-Jones 5 | // 6 | // Finding a path on a simple grid maze 7 | // This shows how to do shortest path finding using A* 8 | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | #include "stlastar.h" // See header for copyright and usage information 18 | 19 | #define DEBUG_LISTS 0 20 | #define DEBUG_LIST_LENGTHS_ONLY 0 21 | 22 | // Global data 23 | 24 | // The world map 25 | 26 | const int MAP_WIDTH = 20; 27 | const int MAP_HEIGHT = 20; 28 | 29 | int world_map[ MAP_WIDTH * MAP_HEIGHT ] = 30 | { 31 | 32 | // 0001020304050607080910111213141516171819 33 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 00 34 | 1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1, // 01 35 | 1,9,9,1,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 02 36 | 1,9,9,1,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 03 37 | 1,9,1,1,1,1,9,9,1,9,1,9,1,1,1,1,9,9,1,1, // 04 38 | 1,9,1,1,9,1,1,1,1,9,1,1,1,1,9,1,1,1,1,1, // 05 39 | 1,9,9,9,9,1,1,1,1,1,1,9,9,9,9,1,1,1,1,1, // 06 40 | 1,9,9,9,9,9,9,9,9,1,1,1,9,9,9,9,9,9,9,1, // 07 41 | 1,9,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1, // 08 42 | 1,9,1,9,9,9,9,9,9,9,1,1,9,9,9,9,9,9,9,1, // 09 43 | 1,9,1,1,1,1,9,1,1,9,1,1,1,1,1,1,1,1,1,1, // 10 44 | 1,9,9,9,9,9,1,9,1,9,1,9,9,9,9,9,1,1,1,1, // 11 45 | 1,9,1,9,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 12 46 | 1,9,1,9,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 13 47 | 1,9,1,1,1,1,9,9,1,9,1,9,1,1,1,1,9,9,1,1, // 14 48 | 1,9,1,1,9,1,1,1,1,9,1,1,1,1,9,1,1,1,1,1, // 15 49 | 1,9,9,9,9,1,1,1,1,1,1,9,9,9,9,1,1,1,1,1, // 16 50 | 1,1,9,9,9,9,9,9,9,1,1,1,9,9,9,1,9,9,9,9, // 17 51 | 1,9,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1, // 18 52 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 19 53 | 54 | }; 55 | 56 | // map helper functions 57 | 58 | int GetMap( int x, int y ) 59 | { 60 | if( x < 0 || 61 | x >= MAP_WIDTH || 62 | y < 0 || 63 | y >= MAP_HEIGHT 64 | ) 65 | { 66 | return 9; 67 | } 68 | 69 | return world_map[(y*MAP_WIDTH)+x]; 70 | } 71 | 72 | 73 | 74 | // Definitions 75 | 76 | class MapSearchNode 77 | { 78 | public: 79 | int x; // the (x,y) positions of the node 80 | int y; 81 | 82 | MapSearchNode() { x = y = 0; } 83 | MapSearchNode( int px, int py ) { x=px; y=py; } 84 | 85 | float GoalDistanceEstimate( MapSearchNode &nodeGoal ); 86 | bool IsGoal( MapSearchNode &nodeGoal ); 87 | bool GetSuccessors( AStarSearch *astarsearch, MapSearchNode *parent_node ); 88 | float GetCost( MapSearchNode &successor ); 89 | bool IsSameState( MapSearchNode &rhs ); 90 | size_t Hash(); 91 | 92 | void PrintNodeInfo(); 93 | 94 | 95 | }; 96 | 97 | bool MapSearchNode::IsSameState( MapSearchNode &rhs ) 98 | { 99 | 100 | // same state in a maze search is simply when (x,y) are the same 101 | if( (x == rhs.x) && 102 | (y == rhs.y) ) 103 | { 104 | return true; 105 | } 106 | else 107 | { 108 | return false; 109 | } 110 | 111 | } 112 | 113 | size_t MapSearchNode::Hash() 114 | { 115 | size_t h1 = hash{}(x); 116 | size_t h2 = hash{}(y); 117 | return h1 ^ (h2 << 1); 118 | } 119 | 120 | void MapSearchNode::PrintNodeInfo() 121 | { 122 | const int strSize = 100; 123 | char str[strSize]; 124 | snprintf( str, strSize, "Node position : (%d,%d)\n", x,y ); 125 | 126 | cout << str; 127 | } 128 | 129 | // Here's the heuristic function that estimates the distance from a Node 130 | // to the Goal. 131 | 132 | float MapSearchNode::GoalDistanceEstimate( MapSearchNode &nodeGoal ) 133 | { 134 | return abs(x - nodeGoal.x) + abs(y - nodeGoal.y); 135 | } 136 | 137 | bool MapSearchNode::IsGoal( MapSearchNode &nodeGoal ) 138 | { 139 | 140 | if( (x == nodeGoal.x) && 141 | (y == nodeGoal.y) ) 142 | { 143 | return true; 144 | } 145 | 146 | return false; 147 | } 148 | 149 | // This generates the successors to the given Node. It uses a helper function called 150 | // AddSuccessor to give the successors to the AStar class. The A* specific initialisation 151 | // is done for each node internally, so here you just set the state information that 152 | // is specific to the application 153 | bool MapSearchNode::GetSuccessors( AStarSearch *astarsearch, MapSearchNode *parent_node ) 154 | { 155 | 156 | int parent_x = -1; 157 | int parent_y = -1; 158 | 159 | if( parent_node ) 160 | { 161 | parent_x = parent_node->x; 162 | parent_y = parent_node->y; 163 | } 164 | 165 | 166 | MapSearchNode NewNode; 167 | 168 | // push each possible move except allowing the search to go backwards 169 | 170 | if( (GetMap( x-1, y ) < 9) 171 | && !((parent_x == x-1) && (parent_y == y)) 172 | ) 173 | { 174 | NewNode = MapSearchNode( x-1, y ); 175 | astarsearch->AddSuccessor( NewNode ); 176 | } 177 | 178 | if( (GetMap( x, y-1 ) < 9) 179 | && !((parent_x == x) && (parent_y == y-1)) 180 | ) 181 | { 182 | NewNode = MapSearchNode( x, y-1 ); 183 | astarsearch->AddSuccessor( NewNode ); 184 | } 185 | 186 | if( (GetMap( x+1, y ) < 9) 187 | && !((parent_x == x+1) && (parent_y == y)) 188 | ) 189 | { 190 | NewNode = MapSearchNode( x+1, y ); 191 | astarsearch->AddSuccessor( NewNode ); 192 | } 193 | 194 | 195 | if( (GetMap( x, y+1 ) < 9) 196 | && !((parent_x == x) && (parent_y == y+1)) 197 | ) 198 | { 199 | NewNode = MapSearchNode( x, y+1 ); 200 | astarsearch->AddSuccessor( NewNode ); 201 | } 202 | 203 | return true; 204 | } 205 | 206 | // given this node, what does it cost to move to successor. In the case 207 | // of our map the answer is the map terrain value at this node since that is 208 | // conceptually where we're moving 209 | 210 | float MapSearchNode::GetCost( MapSearchNode &successor ) 211 | { 212 | return (float) GetMap( x, y ); 213 | 214 | } 215 | 216 | 217 | // Main 218 | 219 | int main( int argc, char *argv[] ) 220 | { 221 | 222 | cout << "STL A* Search implementation\n(C)2001 Justin Heyes-Jones\n"; 223 | 224 | // Our sample problem defines the world as a 2d array representing a terrain 225 | // Each element contains an integer from 0 to 5 which indicates the cost 226 | // of travel across the terrain. Zero means the least possible difficulty 227 | // in travelling (think ice rink if you can skate) whilst 5 represents the 228 | // most difficult. 9 indicates that we cannot pass. 229 | 230 | // Create an instance of the search class... 231 | 232 | AStarSearch astarsearch; 233 | 234 | unsigned int SearchCount = 0; 235 | 236 | const unsigned int NumSearches = 1; 237 | 238 | while(SearchCount < NumSearches) 239 | { 240 | 241 | // Create a start state 242 | MapSearchNode nodeStart; 243 | nodeStart.x = rand()%MAP_WIDTH; 244 | nodeStart.y = rand()%MAP_HEIGHT; 245 | 246 | // Define the goal state 247 | MapSearchNode nodeEnd; 248 | nodeEnd.x = rand()%MAP_WIDTH; 249 | nodeEnd.y = rand()%MAP_HEIGHT; 250 | 251 | // Set Start and goal states 252 | 253 | astarsearch.SetStartAndGoalStates( nodeStart, nodeEnd ); 254 | 255 | unsigned int SearchState; 256 | unsigned int SearchSteps = 0; 257 | 258 | do 259 | { 260 | SearchState = astarsearch.SearchStep(); 261 | 262 | SearchSteps++; 263 | 264 | #if DEBUG_LISTS 265 | 266 | cout << "Steps:" << SearchSteps << "\n"; 267 | 268 | int len = 0; 269 | 270 | cout << "Open:\n"; 271 | MapSearchNode *p = astarsearch.GetOpenListStart(); 272 | while( p ) 273 | { 274 | len++; 275 | #if !DEBUG_LIST_LENGTHS_ONLY 276 | ((MapSearchNode *)p)->PrintNodeInfo(); 277 | #endif 278 | p = astarsearch.GetOpenListNext(); 279 | 280 | } 281 | 282 | cout << "Open list has " << len << " nodes\n"; 283 | 284 | len = 0; 285 | 286 | cout << "Closed:\n"; 287 | p = astarsearch.GetClosedListStart(); 288 | while( p ) 289 | { 290 | len++; 291 | #if !DEBUG_LIST_LENGTHS_ONLY 292 | p->PrintNodeInfo(); 293 | #endif 294 | p = astarsearch.GetClosedListNext(); 295 | } 296 | 297 | cout << "Closed list has " << len << " nodes\n"; 298 | #endif 299 | 300 | } 301 | while( SearchState == AStarSearch::SEARCH_STATE_SEARCHING ); 302 | 303 | if( SearchState == AStarSearch::SEARCH_STATE_SUCCEEDED ) 304 | { 305 | cout << "Search found goal state\n"; 306 | 307 | MapSearchNode *node = astarsearch.GetSolutionStart(); 308 | 309 | #if DISPLAY_SOLUTION 310 | cout << "Displaying solution\n"; 311 | #endif 312 | int steps = 0; 313 | 314 | node->PrintNodeInfo(); 315 | for( ;; ) 316 | { 317 | node = astarsearch.GetSolutionNext(); 318 | 319 | if( !node ) 320 | { 321 | break; 322 | } 323 | 324 | node->PrintNodeInfo(); 325 | steps ++; 326 | 327 | }; 328 | 329 | cout << "Solution steps " << steps << endl; 330 | 331 | // Once you're done with the solution you can free the nodes up 332 | astarsearch.FreeSolutionNodes(); 333 | 334 | 335 | } 336 | else if( SearchState == AStarSearch::SEARCH_STATE_FAILED ) 337 | { 338 | cout << "Search terminated. Did not find goal state\n"; 339 | 340 | } 341 | 342 | // Display the number of loops the search went through 343 | cout << "SearchSteps : " << SearchSteps << "\n"; 344 | 345 | SearchCount ++; 346 | 347 | astarsearch.EnsureMemoryFreed(); 348 | } 349 | 350 | return 0; 351 | } 352 | 353 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 354 | -------------------------------------------------------------------------------- /cpp/fsa.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | A* Algorithm Implementation using STL is 4 | Copyright (C)2001-2005 Justin Heyes-Jones 5 | 6 | Permission is given by the author to freely redistribute and 7 | include this code in any program as long as this credit is 8 | given where due. 9 | 10 | COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 11 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 12 | INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE 13 | IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 14 | OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND 15 | PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED 16 | CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL 17 | DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY 18 | NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF 19 | WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE 20 | OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 21 | THIS DISCLAIMER. 22 | 23 | Use at your own risk! 24 | 25 | 26 | 27 | FixedSizeAllocator class 28 | Copyright 2001 Justin Heyes-Jones 29 | 30 | This class is a constant time O(1) memory manager for objects of 31 | a specified type. The type is specified using a template class. 32 | 33 | Memory is allocated from a fixed size buffer which you can specify in the 34 | class constructor or use the default. 35 | 36 | Using GetFirst and GetNext it is possible to iterate through the elements 37 | one by one, and this would be the most common use for the class. 38 | 39 | I would suggest using this class when you want O(1) add and delete 40 | and you don't do much searching, which would be O(n). Structures such as binary 41 | trees can be used instead to get O(logn) access time. 42 | 43 | */ 44 | 45 | #ifndef FSA_H 46 | #define FSA_H 47 | 48 | #include 49 | #include 50 | 51 | template class FixedSizeAllocator 52 | { 53 | 54 | public: 55 | // Constants 56 | enum 57 | { 58 | FSA_DEFAULT_SIZE = 100 59 | }; 60 | 61 | // This class enables us to transparently manage the extra data 62 | // needed to enable the user class to form part of the double-linked 63 | // list class 64 | struct FSA_ELEMENT 65 | { 66 | USER_TYPE UserType; 67 | 68 | FSA_ELEMENT *pPrev; 69 | FSA_ELEMENT *pNext; 70 | }; 71 | 72 | public: // methods 73 | FixedSizeAllocator( unsigned int MaxElements = FSA_DEFAULT_SIZE ) : 74 | m_pFirstUsed( NULL ), 75 | m_MaxElements( MaxElements ) 76 | { 77 | // Allocate enough memory for the maximum number of elements 78 | 79 | char *pMem = new char[ m_MaxElements * sizeof(FSA_ELEMENT) ]; 80 | 81 | m_pMemory = (FSA_ELEMENT *) pMem; 82 | 83 | // Set the free list first pointer 84 | m_pFirstFree = m_pMemory; 85 | 86 | // Clear the memory 87 | memset( m_pMemory, 0, sizeof( FSA_ELEMENT ) * m_MaxElements ); 88 | 89 | // Point at first element 90 | FSA_ELEMENT *pElement = m_pFirstFree; 91 | 92 | // Set the double linked free list 93 | for( unsigned int i=0; ipPrev = pElement-1; 96 | pElement->pNext = pElement+1; 97 | 98 | pElement++; 99 | } 100 | 101 | // first element should have a null prev 102 | m_pFirstFree->pPrev = NULL; 103 | // last element should have a null next 104 | (pElement-1)->pNext = NULL; 105 | 106 | } 107 | 108 | 109 | ~FixedSizeAllocator() 110 | { 111 | // Free up the memory 112 | delete [] (char *) m_pMemory; 113 | } 114 | 115 | // Allocate a new USER_TYPE and return a pointer to it 116 | USER_TYPE *alloc() 117 | { 118 | 119 | FSA_ELEMENT *pNewNode = NULL; 120 | 121 | if( !m_pFirstFree ) 122 | { 123 | return NULL; 124 | } 125 | else 126 | { 127 | pNewNode = m_pFirstFree; 128 | m_pFirstFree = pNewNode->pNext; 129 | 130 | // if the new node points to another free node then 131 | // change that nodes prev free pointer... 132 | if( pNewNode->pNext ) 133 | { 134 | pNewNode->pNext->pPrev = NULL; 135 | } 136 | 137 | // node is now on the used list 138 | 139 | pNewNode->pPrev = NULL; // the allocated node is always first in the list 140 | 141 | if( m_pFirstUsed == NULL ) 142 | { 143 | pNewNode->pNext = NULL; // no other nodes 144 | } 145 | else 146 | { 147 | m_pFirstUsed->pPrev = pNewNode; // insert this at the head of the used list 148 | pNewNode->pNext = m_pFirstUsed; 149 | } 150 | 151 | m_pFirstUsed = pNewNode; 152 | } 153 | 154 | return reinterpret_cast(pNewNode); 155 | } 156 | 157 | // Free the given user type 158 | // For efficiency I don't check whether the user_data is a valid 159 | // pointer that was allocated. I may add some debug only checking 160 | // (To add the debug check you'd need to make sure the pointer is in 161 | // the m_pMemory area and is pointing at the start of a node) 162 | void free( USER_TYPE *user_data ) 163 | { 164 | FSA_ELEMENT *pNode = reinterpret_cast(user_data); 165 | 166 | // manage used list, remove this node from it 167 | if( pNode->pPrev ) 168 | { 169 | pNode->pPrev->pNext = pNode->pNext; 170 | } 171 | else 172 | { 173 | // this handles the case that we delete the first node in the used list 174 | m_pFirstUsed = pNode->pNext; 175 | } 176 | 177 | if( pNode->pNext ) 178 | { 179 | pNode->pNext->pPrev = pNode->pPrev; 180 | } 181 | 182 | // add to free list 183 | if( m_pFirstFree == NULL ) 184 | { 185 | // free list was empty 186 | m_pFirstFree = pNode; 187 | pNode->pPrev = NULL; 188 | pNode->pNext = NULL; 189 | } 190 | else 191 | { 192 | // Add this node at the start of the free list 193 | m_pFirstFree->pPrev = pNode; 194 | pNode->pNext = m_pFirstFree; 195 | m_pFirstFree = pNode; 196 | } 197 | 198 | } 199 | 200 | // For debugging this displays both lists (using the prev/next list pointers) 201 | void Debug() 202 | { 203 | printf( "free list " ); 204 | 205 | FSA_ELEMENT *p = m_pFirstFree; 206 | while( p ) 207 | { 208 | printf( "%x!%x ", p->pPrev, p->pNext ); 209 | p = p->pNext; 210 | } 211 | printf( "\n" ); 212 | 213 | printf( "used list " ); 214 | 215 | p = m_pFirstUsed; 216 | while( p ) 217 | { 218 | printf( "%x!%x ", p->pPrev, p->pNext ); 219 | p = p->pNext; 220 | } 221 | printf( "\n" ); 222 | } 223 | 224 | // Iterators 225 | 226 | USER_TYPE *GetFirst() 227 | { 228 | return reinterpret_cast(m_pFirstUsed); 229 | } 230 | 231 | USER_TYPE *GetNext( USER_TYPE *node ) 232 | { 233 | return reinterpret_cast 234 | ( 235 | (reinterpret_cast(node))->pNext 236 | ); 237 | } 238 | 239 | public: // data 240 | 241 | private: // methods 242 | 243 | private: // data 244 | 245 | FSA_ELEMENT *m_pFirstFree; 246 | FSA_ELEMENT *m_pFirstUsed; 247 | unsigned int m_MaxElements; 248 | FSA_ELEMENT *m_pMemory; 249 | 250 | }; 251 | 252 | #endif // defined FSA_H 253 | -------------------------------------------------------------------------------- /cpp/makefile: -------------------------------------------------------------------------------- 1 | all : 8puzzle findpath minpathbucharest tests 2 | 3 | # Set the C++ compiler to g++ 4 | CXX = g++ 5 | 6 | # Set some common C++ flags 7 | CXXFLAGS = -std=c++11 -Wall 8 | 9 | clean : 10 | rm 8puzzle findpath minpathbucharest tests 11 | 12 | minpathbucharest : min_path_to_Bucharest.cpp stlastar.h 13 | $(CXX) $(CXXFLAGS) min_path_to_Bucharest.cpp -o minpathbucharest 14 | 15 | 8puzzle : 8puzzle.cpp stlastar.h 16 | $(CXX) $(CXXFLAGS) -Wall 8puzzle.cpp -o 8puzzle 17 | 18 | findpath : findpath.cpp stlastar.h 19 | $(CXX) $(CXXFLAGS) findpath.cpp -o findpath 20 | 21 | tests : tests.cpp 8puzzle findpath minpathbucharest 22 | $(CXX) $(CXXFLAGS) tests.cpp -o tests 23 | 24 | test: tests 25 | ./tests 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /cpp/min_path_to_Bucharest.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // This example code illustrate how to use STL A* Search implementation to find the minimum path between two 3 | // cities given a map. The example is taken from the book AI: A Modern Approach, 3rd Ed., by Russel, where a map 4 | // of Romania is given. The target node is Bucharest, and the user can specify the initial city from which the 5 | // search algorithm will start looking for the minimum path to Bucharest. 6 | // 7 | // Usage: min_path_to_Bucharest 8 | // Example: 9 | // min_path_to_Bucharest Arad 10 | // 11 | // Thanks to Rasoul Mojtahedzadeh for this contribution 12 | // Mojtahedzadeh _atsign_ gmail com 13 | // 14 | // Please note that this exercise is academic in nature and that the distances between the cities may not be 15 | // correct compared to the latitude and longnitude differences in real life. Thanks to parthi2929 for noticing 16 | // this issue. In a real application you would use some kind of exact x,y position of each city in order to get 17 | // accurate heuristics. In fact, that is part of the point of this example, in the book you will see Norvig 18 | // mention that the algorithm does some backtracking because the heuristic is not accurate (yet still admissable). 19 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace std; 27 | 28 | #include "stlastar.h" 29 | 30 | #define DEBUG_LISTS 0 31 | #define DEBUG_LIST_LENGTHS_ONLY 0 32 | 33 | const int MAX_CITIES = 20; 34 | 35 | enum ENUM_CITIES{Arad=0, Bucharest, Craiova, Drobeta, Eforie, Fagaras, Giurgiu, Hirsova, Iasi, Lugoj, Mehadia, Neamt, Oradea, Pitesti, RimnicuVilcea, Sibiu, Timisoara, Urziceni, Vaslui, Zerind}; 36 | vector CityNames(MAX_CITIES); 37 | float RomaniaMap[MAX_CITIES][MAX_CITIES]; 38 | 39 | class PathSearchNode 40 | { 41 | public: 42 | 43 | ENUM_CITIES city; 44 | 45 | PathSearchNode() { city = Arad; } 46 | PathSearchNode( ENUM_CITIES in ) { city = in; } 47 | 48 | float GoalDistanceEstimate( PathSearchNode &nodeGoal ); 49 | bool IsGoal( PathSearchNode &nodeGoal ); 50 | bool GetSuccessors( AStarSearch *astarsearch, PathSearchNode *parent_node ); 51 | float GetCost( PathSearchNode &successor ); 52 | bool IsSameState( PathSearchNode &rhs ); 53 | size_t Hash(); 54 | 55 | void PrintNodeInfo(); 56 | }; 57 | 58 | // check if "this" node is the same as "rhs" node 59 | bool PathSearchNode::IsSameState( PathSearchNode &rhs ) 60 | { 61 | if(city == rhs.city) return(true); 62 | return(false); 63 | } 64 | 65 | size_t PathSearchNode::Hash() 66 | { 67 | return hash{}(city); 68 | } 69 | 70 | // Euclidean distance between "this" node city and Bucharest 71 | // Note: Numbers are taken from the book 72 | float PathSearchNode::GoalDistanceEstimate( PathSearchNode &nodeGoal ) 73 | { 74 | // goal is always Bucharest! 75 | switch(city) 76 | { 77 | case Arad: return 366; break; 78 | case Bucharest: return 0; break; 79 | case Craiova: return 160; break; 80 | case Drobeta: return 242; break; 81 | case Eforie: return 161; break; 82 | case Fagaras: return 176; break; 83 | case Giurgiu: return 77; break; 84 | case Hirsova: return 151; break; 85 | case Iasi: return 226; break; 86 | case Lugoj: return 244; break; 87 | case Mehadia: return 241; break; 88 | case Neamt: return 234; break; 89 | case Oradea: return 380; break; 90 | case Pitesti: return 100; break; 91 | case RimnicuVilcea: return 193; break; 92 | case Sibiu: return 253; break; 93 | case Timisoara: return 329; break; 94 | case Urziceni: return 80; break; 95 | case Vaslui: return 199; break; 96 | case Zerind: return 374; break; 97 | } 98 | cerr << "ASSERT: city = " << CityNames[city] << endl; 99 | return 0; 100 | } 101 | 102 | // check if "this" node is the goal node 103 | bool PathSearchNode::IsGoal( PathSearchNode &nodeGoal ) 104 | { 105 | if( city == Bucharest ) return(true); 106 | return(false); 107 | } 108 | 109 | // generates the successor nodes of "this" node 110 | bool PathSearchNode::GetSuccessors( AStarSearch *astarsearch, PathSearchNode *parent_node ) 111 | { 112 | PathSearchNode NewNode; 113 | for(int c=0; cAddSuccessor( NewNode ); 118 | } 119 | return true; 120 | } 121 | 122 | // the cost of going from "this" node to the "successor" node 123 | float PathSearchNode::GetCost( PathSearchNode &successor ) 124 | { 125 | return RomaniaMap[city][successor.city]; 126 | } 127 | 128 | // prints out information about the node 129 | void PathSearchNode::PrintNodeInfo() 130 | { 131 | cout << " " << CityNames[city] << "\n"; 132 | } 133 | 134 | int main( int argc, char *argv[] ) 135 | { 136 | // creating map of Romania 137 | for(int i=0; i astarsearch; 230 | 231 | unsigned int SearchCount = 0; 232 | const unsigned int NumSearches = 1; 233 | 234 | while(SearchCount < NumSearches) 235 | { 236 | // Create a start state 237 | PathSearchNode nodeStart; 238 | nodeStart.city = initCity; 239 | 240 | // Define the goal state, always Bucharest! 241 | PathSearchNode nodeEnd; 242 | nodeEnd.city = Bucharest; 243 | 244 | // Set Start and goal states 245 | astarsearch.SetStartAndGoalStates( nodeStart, nodeEnd ); 246 | 247 | unsigned int SearchState; 248 | unsigned int SearchSteps = 0; 249 | 250 | do 251 | { 252 | SearchState = astarsearch.SearchStep(); 253 | SearchSteps++; 254 | 255 | #if DEBUG_LISTS 256 | 257 | cout << "Steps:" << SearchSteps << "\n"; 258 | 259 | int len = 0; 260 | 261 | cout << "Open:\n"; 262 | PathSearchNode *p = astarsearch.GetOpenListStart(); 263 | while( p ) 264 | { 265 | len++; 266 | #if !DEBUG_LIST_LENGTHS_ONLY 267 | ((PathSearchNode *)p)->PrintNodeInfo(); 268 | #endif 269 | p = astarsearch.GetOpenListNext(); 270 | 271 | } 272 | cout << "Open list has " << len << " nodes\n"; 273 | 274 | len = 0; 275 | 276 | cout << "Closed:\n"; 277 | p = astarsearch.GetClosedListStart(); 278 | while( p ) 279 | { 280 | len++; 281 | #if !DEBUG_LIST_LENGTHS_ONLY 282 | p->PrintNodeInfo(); 283 | #endif 284 | p = astarsearch.GetClosedListNext(); 285 | } 286 | 287 | cout << "Closed list has " << len << " nodes\n"; 288 | #endif 289 | 290 | } 291 | while( SearchState == AStarSearch::SEARCH_STATE_SEARCHING ); 292 | 293 | if( SearchState == AStarSearch::SEARCH_STATE_SUCCEEDED ) 294 | { 295 | cout << "Search found the goal state\n"; 296 | PathSearchNode *node = astarsearch.GetSolutionStart(); 297 | cout << "Displaying solution\n"; 298 | int steps = 0; 299 | node->PrintNodeInfo(); 300 | for( ;; ) 301 | { 302 | node = astarsearch.GetSolutionNext(); 303 | if( !node ) break; 304 | node->PrintNodeInfo(); 305 | steps ++; 306 | }; 307 | cout << "Solution steps " << steps << endl; 308 | // Once you're done with the solution you can free the nodes up 309 | astarsearch.FreeSolutionNodes(); 310 | } 311 | else if( SearchState == AStarSearch::SEARCH_STATE_FAILED ) 312 | { 313 | cout << "Search terminated. Did not find goal state\n"; 314 | } 315 | // Display the number of loops the search went through 316 | cout << "SearchSteps : " << SearchSteps << "\n"; 317 | SearchCount ++; 318 | astarsearch.EnsureMemoryFreed(); 319 | } 320 | 321 | return 0; 322 | } 323 | -------------------------------------------------------------------------------- /cpp/stlastar.h: -------------------------------------------------------------------------------- 1 | /* 2 | A* Algorithm Implementation using STL is 3 | Copyright (C)2001-2005 Justin Heyes-Jones 4 | 5 | Permission is given by the author to freely redistribute and 6 | include this code in any program as long as this credit is 7 | given where due. 8 | 9 | COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 10 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 11 | INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE 12 | IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 13 | OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND 14 | PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED 15 | CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL 16 | DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY 17 | NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF 18 | WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE 19 | OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER 20 | THIS DISCLAIMER. 21 | 22 | Use at your own risk! 23 | 24 | */ 25 | 26 | #ifndef STLASTAR_H 27 | #define STLASTAR_H 28 | // used for text debugging 29 | #include 30 | #include 31 | //#include 32 | #include 33 | 34 | // stl includes 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | // fast fixed size memory allocator, used for fast node memory management 41 | #include "fsa.h" 42 | 43 | // Fixed size memory allocator can be disabled to compare performance 44 | // Uses std new and delete instead if you turn it off 45 | #define USE_FSA_MEMORY 1 46 | 47 | // disable warning that debugging information has lines that are truncated 48 | // occurs in stl headers 49 | #if defined(WIN32) && defined(_WINDOWS) 50 | #pragma warning( disable : 4786 ) 51 | #endif 52 | 53 | template class AStarState; 54 | 55 | // The AStar search class. UserState is the users state space type 56 | template class AStarSearch 57 | { 58 | 59 | public: // data 60 | 61 | enum 62 | { 63 | SEARCH_STATE_NOT_INITIALISED, 64 | SEARCH_STATE_SEARCHING, 65 | SEARCH_STATE_SUCCEEDED, 66 | SEARCH_STATE_FAILED, 67 | SEARCH_STATE_OUT_OF_MEMORY, 68 | SEARCH_STATE_INVALID 69 | }; 70 | 71 | 72 | // A node represents a possible state in the search 73 | // The user provided state type is included inside this type 74 | 75 | public: 76 | 77 | class Node 78 | { 79 | public: 80 | 81 | Node *parent; // used during the search to record the parent of successor nodes 82 | Node *child; // used after the search for the application to view the search in reverse 83 | 84 | float g; // cost of this node + its predecessors 85 | float h; // heuristic estimate of distance to goal 86 | float f; // sum of cumulative cost of predecessors and self and heuristic 87 | 88 | Node() : 89 | parent( 0 ), 90 | child( 0 ), 91 | g( 0.0f ), 92 | h( 0.0f ), 93 | f( 0.0f ) 94 | { 95 | } 96 | 97 | bool operator==(const Node& otherNode) const 98 | { 99 | return this->m_UserState.IsSameState(otherNode->m_UserState); 100 | } 101 | 102 | UserState m_UserState; 103 | }; 104 | 105 | // For sorting the heap the STL needs compare function that lets us compare 106 | // the f value of two nodes 107 | 108 | class HeapCompare_f 109 | { 110 | public: 111 | 112 | bool operator() ( const Node *x, const Node *y ) const 113 | { 114 | return x->f > y->f; 115 | } 116 | }; 117 | 118 | 119 | public: // methods 120 | 121 | 122 | // constructor just initialises private data 123 | AStarSearch() : 124 | m_State( SEARCH_STATE_NOT_INITIALISED ), 125 | m_CurrentSolutionNode( NULL ), 126 | #if USE_FSA_MEMORY 127 | m_FixedSizeAllocator( 1000 ), 128 | #endif 129 | m_AllocateNodeCount(0), 130 | m_CancelRequest( false ) 131 | { 132 | } 133 | 134 | AStarSearch( int MaxNodes ) : 135 | m_State( SEARCH_STATE_NOT_INITIALISED ), 136 | m_CurrentSolutionNode( NULL ), 137 | #if USE_FSA_MEMORY 138 | m_FixedSizeAllocator( MaxNodes ), 139 | #endif 140 | m_AllocateNodeCount(0), 141 | m_CancelRequest( false ) 142 | { 143 | } 144 | 145 | // call at any time to cancel the search and free up all the memory 146 | void CancelSearch() 147 | { 148 | m_CancelRequest = true; 149 | } 150 | 151 | // Set Start and goal states 152 | void SetStartAndGoalStates( UserState &Start, UserState &Goal ) 153 | { 154 | m_CancelRequest = false; 155 | 156 | m_Start = AllocateNode(); 157 | m_Goal = AllocateNode(); 158 | 159 | assert((m_Start != NULL && m_Goal != NULL)); 160 | 161 | m_Start->m_UserState = Start; 162 | m_Goal->m_UserState = Goal; 163 | 164 | m_State = SEARCH_STATE_SEARCHING; 165 | 166 | // Initialise the AStar specific parts of the Start Node 167 | // The user only needs fill out the state information 168 | 169 | m_Start->g = 0; 170 | m_Start->h = m_Start->m_UserState.GoalDistanceEstimate( m_Goal->m_UserState ); 171 | m_Start->f = m_Start->g + m_Start->h; 172 | m_Start->parent = 0; 173 | 174 | // Push the start node on the Open list 175 | 176 | m_OpenList.push_back( m_Start ); // heap now unsorted 177 | 178 | // Sort back element into heap 179 | push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() ); 180 | 181 | // Initialise counter for search steps 182 | m_Steps = 0; 183 | } 184 | 185 | // Advances search one step 186 | unsigned int SearchStep() 187 | { 188 | // Firstly break if the user has not initialised the search 189 | assert( (m_State > SEARCH_STATE_NOT_INITIALISED) && 190 | (m_State < SEARCH_STATE_INVALID) ); 191 | 192 | // Next I want it to be safe to do a searchstep once the search has succeeded... 193 | if( (m_State == SEARCH_STATE_SUCCEEDED) || 194 | (m_State == SEARCH_STATE_FAILED) 195 | ) 196 | { 197 | return m_State; 198 | } 199 | 200 | // Failure is defined as emptying the open list as there is nothing left to 201 | // search... 202 | // New: Allow user abort 203 | if( m_OpenList.empty() || m_CancelRequest ) 204 | { 205 | FreeAllNodes(); 206 | m_State = SEARCH_STATE_FAILED; 207 | return m_State; 208 | } 209 | 210 | // Incremement step count 211 | m_Steps ++; 212 | 213 | // Pop the best node (the one with the lowest f) 214 | Node *n = m_OpenList.front(); // get pointer to the node 215 | pop_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() ); 216 | m_OpenList.pop_back(); 217 | 218 | // Check for the goal, once we pop that we're done 219 | if( n->m_UserState.IsGoal( m_Goal->m_UserState ) ) 220 | { 221 | // The user is going to use the Goal Node he passed in 222 | // so copy the parent pointer of n 223 | m_Goal->parent = n->parent; 224 | m_Goal->g = n->g; 225 | 226 | // A special case is that the goal was passed in as the start state 227 | // so handle that here 228 | if( false == n->m_UserState.IsSameState( m_Start->m_UserState ) ) 229 | { 230 | FreeNode( n ); 231 | 232 | // set the child pointers in each node (except Goal which has no child) 233 | Node *nodeChild = m_Goal; 234 | Node *nodeParent = m_Goal->parent; 235 | 236 | do 237 | { 238 | nodeParent->child = nodeChild; 239 | 240 | nodeChild = nodeParent; 241 | nodeParent = nodeParent->parent; 242 | 243 | } 244 | while( nodeChild != m_Start ); // Start is always the first node by definition 245 | 246 | } 247 | 248 | // delete nodes that aren't needed for the solution 249 | FreeUnusedNodes(); 250 | 251 | m_State = SEARCH_STATE_SUCCEEDED; 252 | 253 | return m_State; 254 | } 255 | else // not goal 256 | { 257 | 258 | // We now need to generate the successors of this node 259 | // The user helps us to do this, and we keep the new nodes in 260 | // m_Successors ... 261 | 262 | m_Successors.clear(); // empty vector of successor nodes to n 263 | 264 | // User provides this functions and uses AddSuccessor to add each successor of 265 | // node 'n' to m_Successors 266 | bool ret = n->m_UserState.GetSuccessors( this, n->parent ? &n->parent->m_UserState : NULL ); 267 | 268 | if( !ret ) 269 | { 270 | 271 | typename vector< Node * >::iterator successor; 272 | 273 | // free the nodes that may previously have been added 274 | for( successor = m_Successors.begin(); successor != m_Successors.end(); successor ++ ) 275 | { 276 | FreeNode( (*successor) ); 277 | } 278 | 279 | m_Successors.clear(); // empty vector of successor nodes to n 280 | 281 | // free up everything else we allocated 282 | FreeNode( (n) ); 283 | FreeAllNodes(); 284 | 285 | m_State = SEARCH_STATE_OUT_OF_MEMORY; 286 | return m_State; 287 | } 288 | 289 | // Now handle each successor to the current node ... 290 | for( typename vector< Node * >::iterator successor = m_Successors.begin(); successor != m_Successors.end(); successor ++ ) 291 | { 292 | // The g value for this successor ... 293 | float newg = n->g + n->m_UserState.GetCost( (*successor)->m_UserState ); 294 | 295 | // Now we need to find whether the node is on the open or closed lists 296 | // If it is but the node that is already on them is better (lower g) 297 | // then we can forget about this successor 298 | 299 | // First linear search of open list to find node 300 | 301 | typename vector< Node * >::iterator openlist_result; 302 | 303 | for( openlist_result = m_OpenList.begin(); openlist_result != m_OpenList.end(); openlist_result ++ ) 304 | { 305 | if( (*openlist_result)->m_UserState.IsSameState( (*successor)->m_UserState ) ) 306 | { 307 | break; 308 | } 309 | } 310 | 311 | if( openlist_result != m_OpenList.end() ) 312 | { 313 | 314 | // we found this state on open 315 | 316 | if( (*openlist_result)->g <= newg ) 317 | { 318 | FreeNode( (*successor) ); 319 | 320 | // the one on Open is cheaper than this one 321 | continue; 322 | } 323 | } 324 | typename unordered_set::iterator closedlist_result; 325 | 326 | closedlist_result = m_ClosedList.find(*successor); 327 | 328 | if( closedlist_result != m_ClosedList.end() ) 329 | { 330 | 331 | // we found this state on closed 332 | 333 | if( (*closedlist_result)->g <= newg ) 334 | { 335 | // the one on Closed is cheaper than this one 336 | FreeNode( (*successor) ); 337 | 338 | continue; 339 | } 340 | } 341 | 342 | // This node is the best node so far with this particular state 343 | // so lets keep it and set up its AStar specific data ... 344 | 345 | (*successor)->parent = n; 346 | (*successor)->g = newg; 347 | (*successor)->h = (*successor)->m_UserState.GoalDistanceEstimate( m_Goal->m_UserState ); 348 | (*successor)->f = (*successor)->g + (*successor)->h; 349 | 350 | // Successor in closed list 351 | // 1 - Update old version of this node in closed list 352 | // 2 - Move it from closed to open list 353 | // 3 - Sort heap again in open list 354 | 355 | if( closedlist_result != m_ClosedList.end() ) 356 | { 357 | // Update closed node with successor node AStar data 358 | //*(*closedlist_result) = *(*successor); 359 | (*closedlist_result)->parent = (*successor)->parent; 360 | (*closedlist_result)->g = (*successor)->g; 361 | (*closedlist_result)->h = (*successor)->h; 362 | (*closedlist_result)->f = (*successor)->f; 363 | 364 | // Free successor node 365 | FreeNode( (*successor) ); 366 | 367 | // Push closed node into open list 368 | m_OpenList.push_back( (*closedlist_result) ); 369 | 370 | // Remove closed node from closed list 371 | m_ClosedList.erase( closedlist_result ); 372 | 373 | // Sort back element into heap 374 | push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() ); 375 | 376 | // Fix thanks to ... 377 | // Greg Douglas 378 | // who noticed that this code path was incorrect 379 | // Here we have found a new state which is already CLOSED 380 | 381 | } 382 | 383 | // Successor in open list 384 | // 1 - Update old version of this node in open list 385 | // 2 - sort heap again in open list 386 | 387 | else if( openlist_result != m_OpenList.end() ) 388 | { 389 | // Update open node with successor node AStar data 390 | //*(*openlist_result) = *(*successor); 391 | (*openlist_result)->parent = (*successor)->parent; 392 | (*openlist_result)->g = (*successor)->g; 393 | (*openlist_result)->h = (*successor)->h; 394 | (*openlist_result)->f = (*successor)->f; 395 | 396 | // Free successor node 397 | FreeNode( (*successor) ); 398 | 399 | // re-make the heap 400 | // make_heap rather than sort_heap is an essential bug fix 401 | // thanks to Mike Ryynanen for pointing this out and then explaining 402 | // it in detail. sort_heap called on an invalid heap does not work 403 | make_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() ); 404 | } 405 | 406 | // New successor 407 | // 1 - Move it from successors to open list 408 | // 2 - sort heap again in open list 409 | 410 | else 411 | { 412 | // Push successor node into open list 413 | m_OpenList.push_back( (*successor) ); 414 | 415 | // Sort back element into heap 416 | push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() ); 417 | } 418 | 419 | } 420 | 421 | // push n onto Closed, as we have expanded it now 422 | 423 | m_ClosedList.insert( n ); 424 | 425 | } // end else (not goal so expand) 426 | 427 | return m_State; // Succeeded bool is false at this point. 428 | 429 | } 430 | 431 | // User calls this to add a successor to a list of successors 432 | // when expanding the search frontier 433 | bool AddSuccessor( UserState &State ) 434 | { 435 | Node *node = AllocateNode(); 436 | 437 | if( node ) 438 | { 439 | node->m_UserState = State; 440 | 441 | m_Successors.push_back( node ); 442 | 443 | return true; 444 | } 445 | 446 | return false; 447 | } 448 | 449 | // Free the solution nodes 450 | // This is done to clean up all used Node memory when you are done with the 451 | // search 452 | void FreeSolutionNodes() 453 | { 454 | Node *n = m_Start; 455 | 456 | if( m_Start->child ) 457 | { 458 | do 459 | { 460 | Node *del = n; 461 | n = n->child; 462 | FreeNode( del ); 463 | 464 | del = NULL; 465 | 466 | } while( n != m_Goal ); 467 | 468 | FreeNode( n ); // Delete the goal 469 | 470 | } 471 | else 472 | { 473 | // if the start node is the solution we need to just delete the start and goal 474 | // nodes 475 | FreeNode( m_Start ); 476 | FreeNode( m_Goal ); 477 | } 478 | 479 | } 480 | 481 | // Functions for traversing the solution 482 | 483 | // Get start node 484 | UserState *GetSolutionStart() 485 | { 486 | m_CurrentSolutionNode = m_Start; 487 | if( m_Start ) 488 | { 489 | return &m_Start->m_UserState; 490 | } 491 | else 492 | { 493 | return NULL; 494 | } 495 | } 496 | 497 | // Get next node 498 | UserState *GetSolutionNext() 499 | { 500 | if( m_CurrentSolutionNode ) 501 | { 502 | if( m_CurrentSolutionNode->child ) 503 | { 504 | 505 | Node *child = m_CurrentSolutionNode->child; 506 | 507 | m_CurrentSolutionNode = m_CurrentSolutionNode->child; 508 | 509 | return &child->m_UserState; 510 | } 511 | } 512 | 513 | return NULL; 514 | } 515 | 516 | // Get end node 517 | UserState *GetSolutionEnd() 518 | { 519 | m_CurrentSolutionNode = m_Goal; 520 | if( m_Goal ) 521 | { 522 | return &m_Goal->m_UserState; 523 | } 524 | else 525 | { 526 | return NULL; 527 | } 528 | } 529 | 530 | // Step solution iterator backwards 531 | UserState *GetSolutionPrev() 532 | { 533 | if( m_CurrentSolutionNode ) 534 | { 535 | if( m_CurrentSolutionNode->parent ) 536 | { 537 | 538 | Node *parent = m_CurrentSolutionNode->parent; 539 | 540 | m_CurrentSolutionNode = m_CurrentSolutionNode->parent; 541 | 542 | return &parent->m_UserState; 543 | } 544 | } 545 | 546 | return NULL; 547 | } 548 | 549 | // Get final cost of solution 550 | // Returns FLT_MAX if goal is not defined or there is no solution 551 | float GetSolutionCost() 552 | { 553 | if( m_Goal && m_State == SEARCH_STATE_SUCCEEDED ) 554 | { 555 | return m_Goal->g; 556 | } 557 | else 558 | { 559 | return FLT_MAX; 560 | } 561 | } 562 | 563 | // For educational use and debugging it is useful to be able to view 564 | // the open and closed list at each step, here are two functions to allow that. 565 | 566 | UserState *GetOpenListStart() 567 | { 568 | float f,g,h; 569 | return GetOpenListStart( f,g,h ); 570 | } 571 | 572 | UserState *GetOpenListStart( float &f, float &g, float &h ) 573 | { 574 | iterDbgOpen = m_OpenList.begin(); 575 | if( iterDbgOpen != m_OpenList.end() ) 576 | { 577 | f = (*iterDbgOpen)->f; 578 | g = (*iterDbgOpen)->g; 579 | h = (*iterDbgOpen)->h; 580 | return &(*iterDbgOpen)->m_UserState; 581 | } 582 | 583 | return NULL; 584 | } 585 | 586 | UserState *GetOpenListNext() 587 | { 588 | float f,g,h; 589 | return GetOpenListNext( f,g,h ); 590 | } 591 | 592 | UserState *GetOpenListNext( float &f, float &g, float &h ) 593 | { 594 | iterDbgOpen++; 595 | if( iterDbgOpen != m_OpenList.end() ) 596 | { 597 | f = (*iterDbgOpen)->f; 598 | g = (*iterDbgOpen)->g; 599 | h = (*iterDbgOpen)->h; 600 | return &(*iterDbgOpen)->m_UserState; 601 | } 602 | 603 | return NULL; 604 | } 605 | 606 | UserState *GetClosedListStart() 607 | { 608 | float f,g,h; 609 | return GetClosedListStart( f,g,h ); 610 | } 611 | 612 | UserState *GetClosedListStart( float &f, float &g, float &h ) 613 | { 614 | iterDbgClosed = m_ClosedList.begin(); 615 | if( iterDbgClosed != m_ClosedList.end() ) 616 | { 617 | f = (*iterDbgClosed)->f; 618 | g = (*iterDbgClosed)->g; 619 | h = (*iterDbgClosed)->h; 620 | 621 | return &(*iterDbgClosed)->m_UserState; 622 | } 623 | 624 | return NULL; 625 | } 626 | 627 | UserState *GetClosedListNext() 628 | { 629 | float f,g,h; 630 | return GetClosedListNext( f,g,h ); 631 | } 632 | 633 | UserState *GetClosedListNext( float &f, float &g, float &h ) 634 | { 635 | iterDbgClosed++; 636 | if( iterDbgClosed != m_ClosedList.end() ) 637 | { 638 | f = (*iterDbgClosed)->f; 639 | g = (*iterDbgClosed)->g; 640 | h = (*iterDbgClosed)->h; 641 | 642 | return &(*iterDbgClosed)->m_UserState; 643 | } 644 | 645 | return NULL; 646 | } 647 | 648 | // Get the number of steps 649 | 650 | int GetStepCount() { return m_Steps; } 651 | 652 | void EnsureMemoryFreed() 653 | { 654 | #if USE_FSA_MEMORY 655 | assert(m_AllocateNodeCount == 0); 656 | #endif 657 | 658 | } 659 | 660 | private: // methods 661 | 662 | // This is called when a search fails or is cancelled to free all used 663 | // memory 664 | void FreeAllNodes() 665 | { 666 | // iterate open list and delete all nodes 667 | typename vector< Node * >::iterator iterOpen = m_OpenList.begin(); 668 | 669 | while( iterOpen != m_OpenList.end() ) 670 | { 671 | Node *n = (*iterOpen); 672 | FreeNode( n ); 673 | 674 | iterOpen ++; 675 | } 676 | 677 | m_OpenList.clear(); 678 | 679 | // iterate closed list and delete unused nodes 680 | typename unordered_set::iterator iterClosed; 681 | 682 | for( iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed ++ ) 683 | { 684 | Node *n = (*iterClosed); 685 | FreeNode( n ); 686 | } 687 | 688 | m_ClosedList.clear(); 689 | 690 | // delete the goal 691 | 692 | FreeNode(m_Goal); 693 | } 694 | 695 | 696 | // This call is made by the search class when the search ends. A lot of nodes may be 697 | // created that are still present when the search ends. They will be deleted by this 698 | // routine once the search ends 699 | void FreeUnusedNodes() 700 | { 701 | // iterate open list and delete unused nodes 702 | typename vector< Node * >::iterator iterOpen = m_OpenList.begin(); 703 | 704 | while( iterOpen != m_OpenList.end() ) 705 | { 706 | Node *n = (*iterOpen); 707 | 708 | if( !n->child ) 709 | { 710 | FreeNode( n ); 711 | 712 | n = NULL; 713 | } 714 | 715 | iterOpen ++; 716 | } 717 | 718 | m_OpenList.clear(); 719 | 720 | // iterate closed list and delete unused nodes 721 | typename unordered_set::iterator iterClosed; 722 | 723 | for( iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed ++ ) 724 | { 725 | Node *n = (*iterClosed); 726 | 727 | if( !n->child ) 728 | { 729 | FreeNode( n ); 730 | n = NULL; 731 | 732 | } 733 | } 734 | 735 | m_ClosedList.clear(); 736 | 737 | } 738 | 739 | // Node memory management 740 | Node *AllocateNode() 741 | { 742 | 743 | #if !USE_FSA_MEMORY 744 | m_AllocateNodeCount ++; 745 | Node *p = new Node; 746 | return p; 747 | #else 748 | Node *address = m_FixedSizeAllocator.alloc(); 749 | 750 | if( !address ) 751 | { 752 | return NULL; 753 | } 754 | m_AllocateNodeCount ++; 755 | Node *p = new (address) Node; 756 | return p; 757 | #endif 758 | } 759 | 760 | void FreeNode( Node *node ) 761 | { 762 | 763 | m_AllocateNodeCount --; 764 | 765 | #if !USE_FSA_MEMORY 766 | delete node; 767 | #else 768 | node->~Node(); 769 | m_FixedSizeAllocator.free( node ); 770 | #endif 771 | } 772 | 773 | private: // data 774 | 775 | // Heap (simple vector but used as a heap, cf. Steve Rabin's game gems article) 776 | vector< Node *> m_OpenList; 777 | 778 | // Closed is an unordered_set 779 | struct NodeHash { 780 | size_t operator() (Node* const& n) const { 781 | return n->m_UserState.Hash(); 782 | } 783 | }; 784 | struct NodeEqual { 785 | bool operator()(Node* a, Node* b) const { 786 | return a->m_UserState.IsSameState(b->m_UserState); 787 | } 788 | }; 789 | unordered_set m_ClosedList; 790 | 791 | 792 | // Successors is a vector filled out by the user each type successors to a node 793 | // are generated 794 | vector< Node * > m_Successors; 795 | 796 | // State 797 | unsigned int m_State; 798 | 799 | // Counts steps 800 | int m_Steps; 801 | 802 | // Start and goal state pointers 803 | Node *m_Start; 804 | Node *m_Goal; 805 | 806 | Node *m_CurrentSolutionNode; 807 | 808 | #if USE_FSA_MEMORY 809 | // Memory 810 | FixedSizeAllocator m_FixedSizeAllocator; 811 | #endif 812 | 813 | //Debug : need to keep these two iterators around 814 | // for the user Dbg functions 815 | typename vector< Node * >::iterator iterDbgOpen; 816 | typename vector< Node * >::iterator iterDbgClosed; 817 | 818 | // debugging : count memory allocation and free's 819 | int m_AllocateNodeCount; 820 | 821 | bool m_CancelRequest; 822 | 823 | }; 824 | 825 | template class AStarState 826 | { 827 | public: 828 | virtual ~AStarState() {} 829 | virtual float GoalDistanceEstimate( T &nodeGoal ) = 0; // Heuristic function which computes the estimated cost to the goal node 830 | virtual bool IsGoal( T &nodeGoal ) = 0; // Returns true if this node is the goal node 831 | virtual bool GetSuccessors( AStarSearch *astarsearch, T *parent_node ) = 0; // Retrieves all successors to this node and adds them via astarsearch.addSuccessor() 832 | virtual float GetCost( T &successor ) = 0; // Computes the cost of travelling from this node to the successor node 833 | virtual bool IsSameState( T &rhs ) = 0; // Returns true if this node is the same as the rhs node 834 | virtual size_t Hash() = 0; // Returns a hash for the state 835 | }; 836 | 837 | #endif 838 | 839 | 840 | -------------------------------------------------------------------------------- /cpp/tests.cpp: -------------------------------------------------------------------------------- 1 | // A unit test suite 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | #include "stlastar.h" 10 | 11 | const int MAP_WIDTH = 20; 12 | const int MAP_HEIGHT = 20; 13 | 14 | int world_map[ MAP_WIDTH * MAP_HEIGHT ] = 15 | { 16 | 17 | // 0001020304050607080910111213141516171819 18 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 00 19 | 1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1, // 01 20 | 1,9,9,1,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 02 21 | 1,9,9,1,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 03 22 | 1,9,1,9,1,9,9,9,1,9,1,9,1,1,1,1,9,9,1,1, // 04 23 | 1,9,1,1,9,1,1,1,1,9,1,1,1,1,9,1,1,1,1,1, // 05 24 | 1,9,9,9,9,1,1,1,1,1,1,9,9,9,9,1,1,1,1,1, // 06 25 | 1,9,9,9,9,9,9,9,9,1,1,1,9,9,9,9,9,9,9,1, // 07 26 | 1,9,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1, // 08 27 | 1,9,1,9,9,9,9,9,9,9,1,1,9,9,9,9,9,9,9,1, // 09 28 | 1,9,1,1,1,1,9,1,1,9,1,1,1,1,1,1,1,1,1,1, // 10 29 | 1,9,9,9,9,9,1,9,1,9,1,9,9,9,9,9,1,1,1,1, // 11 30 | 1,9,1,9,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 12 31 | 1,9,1,9,1,9,9,9,1,9,1,9,1,9,1,9,9,9,1,1, // 13 32 | 1,9,1,1,1,1,9,9,1,9,1,9,1,1,1,1,9,9,1,1, // 14 33 | 1,9,1,1,9,1,1,1,1,9,1,1,1,1,9,1,1,1,1,1, // 15 34 | 1,9,9,9,9,1,1,1,1,1,1,9,9,9,9,1,1,1,1,1, // 16 35 | 1,1,9,9,9,9,9,9,9,1,1,1,9,9,9,1,9,9,9,9, // 17 36 | 1,9,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1, // 18 37 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 19 38 | 39 | }; 40 | 41 | 42 | // map helper functions 43 | 44 | int GetMap( int x, int y ) 45 | { 46 | if( x < 0 || 47 | x >= MAP_WIDTH || 48 | y < 0 || 49 | y >= MAP_HEIGHT 50 | ) 51 | { 52 | return 9; 53 | } 54 | 55 | return world_map[(y*MAP_WIDTH)+x]; 56 | } 57 | 58 | 59 | 60 | // Definitions 61 | 62 | class MapSearchNode 63 | { 64 | public: 65 | int x; // the (x,y) positions of the node 66 | int y; 67 | 68 | MapSearchNode() { x = y = 0; } 69 | MapSearchNode( int px, int py ) { x=px; y=py; } 70 | 71 | float GoalDistanceEstimate( MapSearchNode &nodeGoal ); 72 | bool IsGoal( MapSearchNode &nodeGoal ); 73 | bool GetSuccessors( AStarSearch *astarsearch, MapSearchNode *parent_node ); 74 | float GetCost( MapSearchNode &successor ); 75 | bool IsSameState( MapSearchNode &rhs ); 76 | size_t Hash(); 77 | 78 | void PrintNodeInfo(); 79 | 80 | 81 | }; 82 | 83 | bool MapSearchNode::IsSameState( MapSearchNode &rhs ) 84 | { 85 | 86 | // same state in a maze search is simply when (x,y) are the same 87 | if( (x == rhs.x) && 88 | (y == rhs.y) ) 89 | { 90 | return true; 91 | } 92 | else 93 | { 94 | return false; 95 | } 96 | 97 | } 98 | 99 | void MapSearchNode::PrintNodeInfo() 100 | { 101 | const int strSize = 100; 102 | char str[strSize]; 103 | snprintf( str, strSize, "Node position : (%d,%d)\n", x,y ); 104 | 105 | cout << str; 106 | } 107 | 108 | // Here's the heuristic function that estimates the distance from a Node 109 | // to the Goal. 110 | 111 | float MapSearchNode::GoalDistanceEstimate( MapSearchNode &nodeGoal ) 112 | { 113 | return abs(x - nodeGoal.x) + abs(y - nodeGoal.y); 114 | } 115 | 116 | bool MapSearchNode::IsGoal( MapSearchNode &nodeGoal ) 117 | { 118 | 119 | if( (x == nodeGoal.x) && 120 | (y == nodeGoal.y) ) 121 | { 122 | return true; 123 | } 124 | 125 | return false; 126 | } 127 | 128 | // This generates the successors to the given Node. It uses a helper function called 129 | // AddSuccessor to give the successors to the AStar class. The A* specific initialisation 130 | // is done for each node internally, so here you just set the state information that 131 | // is specific to the application 132 | bool MapSearchNode::GetSuccessors( AStarSearch *astarsearch, MapSearchNode *parent_node ) 133 | { 134 | 135 | int parent_x = -1; 136 | int parent_y = -1; 137 | 138 | if( parent_node ) 139 | { 140 | parent_x = parent_node->x; 141 | parent_y = parent_node->y; 142 | } 143 | 144 | 145 | MapSearchNode NewNode; 146 | 147 | // push each possible move except allowing the search to go backwards 148 | 149 | if( (GetMap( x-1, y ) < 9) 150 | && !((parent_x == x-1) && (parent_y == y)) 151 | ) 152 | { 153 | NewNode = MapSearchNode( x-1, y ); 154 | astarsearch->AddSuccessor( NewNode ); 155 | } 156 | 157 | if( (GetMap( x, y-1 ) < 9) 158 | && !((parent_x == x) && (parent_y == y-1)) 159 | ) 160 | { 161 | NewNode = MapSearchNode( x, y-1 ); 162 | astarsearch->AddSuccessor( NewNode ); 163 | } 164 | 165 | if( (GetMap( x+1, y ) < 9) 166 | && !((parent_x == x+1) && (parent_y == y)) 167 | ) 168 | { 169 | NewNode = MapSearchNode( x+1, y ); 170 | astarsearch->AddSuccessor( NewNode ); 171 | } 172 | 173 | 174 | if( (GetMap( x, y+1 ) < 9) 175 | && !((parent_x == x) && (parent_y == y+1)) 176 | ) 177 | { 178 | NewNode = MapSearchNode( x, y+1 ); 179 | astarsearch->AddSuccessor( NewNode ); 180 | } 181 | 182 | return true; 183 | } 184 | 185 | // given this node, what does it cost to move to successor. In the case 186 | // of our map the answer is the map terrain value at this node since that is 187 | // conceptually where we're moving 188 | 189 | float MapSearchNode::GetCost( MapSearchNode &successor ) 190 | { 191 | return (float) GetMap( x, y ); 192 | 193 | } 194 | 195 | size_t MapSearchNode::Hash() 196 | { 197 | size_t h1 = hash{}(x); 198 | size_t h2 = hash{}(y); 199 | return h1 ^ (h2 << 1); 200 | } 201 | 202 | int main(int argc, char *argv[]) { 203 | 204 | AStarSearch astarsearch; 205 | 206 | unsigned int SearchCount = 0; 207 | 208 | const unsigned int NumSearches = 1; 209 | 210 | while(SearchCount < NumSearches) 211 | { 212 | 213 | // Create a start state 214 | MapSearchNode nodeStart; 215 | nodeStart.x = 0; 216 | nodeStart.y = 0; 217 | 218 | // Define the goal state 219 | MapSearchNode nodeEnd; 220 | nodeEnd.x = 3; 221 | nodeEnd.y = 3; 222 | 223 | // Set Start and goal states 224 | 225 | astarsearch.SetStartAndGoalStates( nodeStart, nodeEnd ); 226 | 227 | unsigned int SearchState; 228 | unsigned int SearchSteps = 0; 229 | 230 | do 231 | { 232 | SearchState = astarsearch.SearchStep(); 233 | 234 | SearchSteps++; 235 | 236 | #if DEBUG_LISTS 237 | 238 | cout << "Steps:" << SearchSteps << "\n"; 239 | 240 | int len = 0; 241 | 242 | cout << "Open:\n"; 243 | MapSearchNode *p = astarsearch.GetOpenListStart(); 244 | while( p ) 245 | { 246 | len++; 247 | #if !DEBUG_LIST_LENGTHS_ONLY 248 | ((MapSearchNode *)p)->PrintNodeInfo(); 249 | #endif 250 | p = astarsearch.GetOpenListNext(); 251 | 252 | } 253 | 254 | cout << "Open list has " << len << " nodes\n"; 255 | 256 | len = 0; 257 | 258 | cout << "Closed:\n"; 259 | p = astarsearch.GetClosedListStart(); 260 | while( p ) 261 | { 262 | len++; 263 | #if !DEBUG_LIST_LENGTHS_ONLY 264 | p->PrintNodeInfo(); 265 | #endif 266 | p = astarsearch.GetClosedListNext(); 267 | } 268 | 269 | cout << "Closed list has " << len << " nodes\n"; 270 | #endif 271 | 272 | } 273 | while( SearchState == AStarSearch::SEARCH_STATE_SEARCHING ); 274 | 275 | if( SearchState == AStarSearch::SEARCH_STATE_SUCCEEDED ) 276 | { 277 | cout << "Search found goal state\n"; 278 | 279 | MapSearchNode *node = astarsearch.GetSolutionStart(); 280 | 281 | #if DISPLAY_SOLUTION 282 | cout << "Displaying solution\n"; 283 | #endif 284 | int steps = 0; 285 | 286 | node->PrintNodeInfo(); 287 | for( ;; ) 288 | { 289 | node = astarsearch.GetSolutionNext(); 290 | 291 | if( !node ) 292 | { 293 | break; 294 | } 295 | 296 | node->PrintNodeInfo(); 297 | steps ++; 298 | 299 | }; 300 | 301 | cout << "Solution steps " << steps << endl; 302 | 303 | // Once you're done with the solution you can free the nodes up 304 | astarsearch.FreeSolutionNodes(); 305 | 306 | 307 | } 308 | else if( SearchState == AStarSearch::SEARCH_STATE_FAILED ) 309 | { 310 | cout << "Search terminated. Did not find goal state\n"; 311 | 312 | } 313 | 314 | // Display the number of loops the search went through 315 | cout << "SearchSteps : " << SearchSteps << "\n"; 316 | 317 | SearchCount ++; 318 | 319 | astarsearch.EnsureMemoryFreed(); 320 | } 321 | 322 | assert(true && "failed to be true"); 323 | 324 | printf("Tests succeeded\n"); 325 | 326 | } 327 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2001, 2013 Justin Heyes-Jones 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | --------------------------------------------------------------------------------