This project provides a C++14 header-only template library for executing the A* search algorithm.
This project utilizes the CMake build tool. You can download and install CMake through their download page or through your package manager, if it is available. Debian-based distributions can install with
sudo apt-get install cmake
If you wish to have a graphical interface to CMake you can install ccmake
, which is available via apt
as cmake-curses-gui
sudo apt-get install cmake-curses-gui
The project requires a C++14 compliant compiler.
It has been tested with g++-5.1
and clang++-3.5
but should also work fine with Visual Studio 2015's cl
.
CMake supports multiple platforms and build systems. It can generate full projects for several IDEs as well as various kinds of Makefiles. These instructions are for using Unix Makefiles from a Linux command line while in the project root directory. Consult the CMake documentation for help with your particular setup, e.g. to create Visual Studio a project.
-
Configure the project
If you have elected to use
ccmake
, run it first to set your desired options, else just runcmake
.ccmake . cmake .
-
Build the executables
make [target]
Use
make help
to see a list of valid targets. -
Run an example
./examples/<example>/bin/<target> [target options]
See the wiki for documentation on each example.
The command
sudo make install
will install the header files to ${CMAKE_INSTALL_PREFIX}/include/astar
.
By default, CMAKE_INSTALL_PREFIX = /usr/local
on Linux systems.
A* requires 6 things, all of which must be provided by the user.
- A way to represent state
- A starting state
- An ending state
- A way to get from one state to others, a
Generator
function - A way to measure distance/cost between one state and the next, a
Distance
function - A way to estimate distance/cost from one state to the goal, an
Estimator
function
A state can be represented by any type T
so long as the functions can operate on the state as described below.
States must be comparable through operator<
. That is, bool operator<(const T& left, const T& right)
must be defined.
A Generator
function is any callable (function, lambda, functor) that takes one state and returns
a std::vector
of "next", "nearest", or "neighbor" states.
That is, a Generator
function g
must support the expression std::vector<T> v = g(t)
for any t
of type const T&
.
A Distance
is any callable that takes two states and returns the distance between or cost to get from the first state to the second.
The representation of that distance can be anything. Typical types are int
or double
but user defined types are also permitted.
Therefore a Distance
function d
must support the expression G g = d(from, to)
,
where from
and to
are both of type const T&
.
An Estimator
is any callable that takes two states and returns the distance between or cost to get from the first state to the second.
The representation of that distance can be anything. Typical types are int
or double
but user defined types are also permitted.
Therefore an Estimator
function e
must support the expression H h = e(from, to)
,
where from
and to
are both of type const T&
.
The return types of the Distance
and Estimator
functions do not need to be the same but they do need to be compatible
through operator+
. That is, there should exist a function J operator+(const G&, const H&)
.
Two instances of type J
must be comparable through operator>
, meaning bool operator>(const J& left, const J& right)
must be defined.
The Generator
is used to create the next potential nodes to evaluate. At the time they are generated,
the Distance
function will be called for each with the current state as the first parameter and the potential as the second.
Similarly, the Estimator
function will be called for each with the potential as the first parameter and the
provided ending state as the second.
These can all be passed to the convenience function make_solver
which has the partial signature
make_solver(const T& start, const T& goal, Generator, Distance, Estimator)
and will instantiate an appropriate solver of type AStarSolver<T,G,H>
.
It is recommended to use auto
for type deduction as this will allow the implementation to change with minimal
impact to client code. Ideally the interface will not change too drastically but backwards compatibility is not
a priority at this stage.
After instantiation, simply call solve()
.
Presently the only way to see the solution is to call print_solution
, which will write the backtrace to the
specified std::ostream
, defaulting to std::cout
. This function requires operator<<
to exist for both
T
and J
. Specifically, std::ostream& operator<<(std::ostream&, const T&)
must be defined for each type.
/*
assume anything not explicitly defined below is appropriately defined,
specifically that Type exists and has a get_neighbors(std::vector<Type>&) method,
that get_start() and get_end() each return the expected Type,
and that operator< and operator<< are both supplied for Type comparison and output
*/
#include <astar/solver.h>
Type start = get_start();
Type end = get_end();
auto gen = [](std::vector<Type>& v, const Type& t) { return t.get_neighbors(v); };
auto dist = [](const Type& from, const Type& to) { return 1; };
auto est = [](const Type& from, const Type& to) { return 0; };
auto solver = make_solver(start, end, gen, dist, est);
solver.solve();
solver.print_solution();