14

With so many implementations available, what is the fastest executing (least CPU intensive, smallest binary), cross-platform (Linux, Mac, Windows, iPhone) A* implementation for C++ using a small grid?

Implementations

Google returns:

Any others?

The Wheel

The question, as asked, pertains to reuse (plug into a game), not reinvention (at least not until performance is shown to be an issue). It might turn out that a Dijkstra implementation (or generic pathfinding algorithm) is better suited, or that the fastest implementations are not fast enough. I appreciate the suggestions of alternative algorithms, however the question is not, "Should I roll my own A*?"

Community
  • 1
  • 1
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315

5 Answers5

6

When you have specific bounds that you can work with, you're usually better off writing the algorithm yourself. In particular your small state space lends itself to optimisations that spend memory up-front to reduce CPU time, and the fact that you're using a grid rather than an arbitrary state space allows you to do things like optimise your successor node generation, or be able to treat all partial paths that end on the same grid square as equivalent (which a normal A* search will not and cannot assume).

(PS. OpenSteer, a collection of steering behaviours, is nothing to do with A*, which is a search algorithm, except that you can notionally use one, the other, or both to traverse a space. One isn't a replacement for the other in most reasonable circumstances.)

Kylotan
  • 18,290
  • 7
  • 46
  • 74
6

I have two general pieces of advice:

  • If your domain is restricted to a grid, maybe you will find better results by searching "pathfinding" rather the more generic A*.
  • If your domain is not strictly searching paths along a surface, you could get more benefits for your effort if you spend your time improving your heuristics rather than trying to optimise the algorithm itself.
fortran
  • 74,053
  • 25
  • 135
  • 175
  • 1
    I voted up for the second bullet. The first is a little confusing, as I think "pathfinding" can be more generic than "A*" – San Jacinto Jan 21 '10 at 17:31
  • 1
    A* can be used for any kind of search problems, path-finding is a well defined domain: navigating from one point of a surface to another. – fortran Jan 21 '10 at 22:02
  • 1
    +1 for second point. The heuristic is very critical to optimizing A* – lalitm Feb 05 '10 at 15:12
6

Look at other path-finding algorithms (like Breath-First, Depth-First, Minimax, Negmax etc.) and weigh the positives and negatives for your scenario.

Boost also has an A-star implementation. Try following these instructions to build boost on iPhone, but it might not work for you: it is not a "full port" of boost and it might error out.

The following is from Algorithms in a Nutshell (Java, not C++ but maybe you'd like to port it):

public Solution search( INode initial, INode goal ) {
  // Start from the initial state
  INodeSet open = StateStorageFactory.create( StateStorageFactory.TREE );
  INode copy = initial.copy();
  scoringFunction.score( copy );
  open.insert( copy );

  // Use Hashtable to store states we have already visited.
  INodeSet closed = StateStorageFactory.create( StateStorageFactory. HASH );
  while( !open.isEmpty() ) {
    // Remove node with smallest evaluation function and mark closed.
    INode n = open.remove();

    closed.insert( n );

    // Return if goal state reached.
    if( n.equals( goal ) ) { return new Solution( initial, n ); }

    // Compute successor moves and update OPEN/CLOSED lists.
    DepthTransition trans = (DepthTransition)n.storedData();
    int depth = 1;

    if( trans ! = null ) { depth = trans.depth + 1; }

    DoubleLinkedList<IMove> moves = n.validMoves();

    for( Iterator<IMove> it = moves.iterator(); it.hasNext(); ) {
      IMove move = it.next();

      // Make move and score the new board state.
      INode successor = n.copy();
      move.execute( successor );

      // Record previous move for solution trace and compute
      // evaluation function to see if we have improved upon
      // a state already closed
      successor.storedData( new DepthTransition( move, n, depth ) );
      scoringFunction.score( successor );

      // If already visited, see if we are revisiting with lower
      // cost. If not, just continue; otherwise, pull out of closed
      // and process
      INode past = closed.contains( successor );

      if( past ! = null ) {
        if( successor.score() >= past.score() ) {
          continue;
        }

        // we revisit with our lower cost.
        closed.remove( past );
      }

      // place into open.
      open.insert( successor );
    }
  }

  // No solution.
  return new Solution( initial, goal, false );
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
slf
  • 22,595
  • 11
  • 77
  • 101
  • 1
    You don't need to build Boost if you use header-only libraries. Boost.Graph is header-only if you don't use the Dot file stuff. I've used several Boost header-only libraries on the iPhone and they work fine out-of-the-box. – Emile Cormier Oct 24 '11 at 18:33
5

I suggest you implement the algorithm by yourself. Follow the pseudo code at: A* Search Algorithm and it should be straight forward. The "openset" should be implemented as a min-heap, which is also trivial; or you can use priority_queue from STL.

Ikke
  • 99,403
  • 23
  • 97
  • 120
Long Cheng
  • 388
  • 2
  • 10
  • Agreed. A* itself isn't very complicated, and can often be optimized to a specific situation. – David Thornley Jan 21 '10 at 17:22
  • 1
    You can't use `priority_queue` from the STL, since it does not allow to change the priority of an already inserted element (and forcing a heap rebuild is grossly inefficient). For a mere mortal like me, implementing an efficient A* that does not spend most of its time wading through lists and keeps the memory consumption reasonable (for instance by avoiding to store full nodes in the closed list) is anything but trivial. – kuroi neko Sep 03 '14 at 12:22
  • 1
    @kuroineko actually you can use a `priority_queue`, as you don't _need_ to remove the old node when you change the priority. You can just insert again the node with a higher priority in the open set, and it will be picked first and put in the closed set (then the "old" node in the open set will be discarded later, as it's already in the closed set) – cyril Feb 08 '16 at 14:19
2

There's a generic C++ A* implementation at http://www.ceng.metu.edu.tr/~cuneyt/codes.html. It looks like it's all cross-platform standard C++.

Geoff Reedy
  • 34,891
  • 3
  • 56
  • 79