| 1 | //          Copyright W.P. McNeill 2010. | 
|---|
| 2 | //          Copyright Georg Gast 2014 | 
|---|
| 3 | // | 
|---|
| 4 | // Distributed under the Boost Software License, Version 1.0. | 
|---|
| 5 | //    (See accompanying file LICENSE_1_0.txt or copy at | 
|---|
| 6 | //          http://www.boost.org/LICENSE_1_0.txt) | 
|---|
| 7 |  | 
|---|
| 8 |  | 
|---|
| 9 | // This program uses the A-star search algorithm in the Boost Graph Library to | 
|---|
| 10 | // solve a maze.  It is an example of how to apply Boost Graph Library | 
|---|
| 11 | // algorithms to filtered graphs. | 
|---|
| 12 | // | 
|---|
| 13 | // This program generates a random maze and then tries to find the shortest | 
|---|
| 14 | // path from the lower left-hand corner to the upper right-hand corner.  Mazes | 
|---|
| 15 | // are represented by two-dimensional grids where a cell in the grid may | 
|---|
| 16 | // contain a barrier.  You may move up, down, right, or left to any adjacent | 
|---|
| 17 | // cell that does not contain a barrier. | 
|---|
| 18 | // | 
|---|
| 19 | // Once a maze solution has been attempted, the maze is printed.  If a | 
|---|
| 20 | // solution was found it will be shown in the maze printout. | 
|---|
| 21 | // Note that not all mazes have solutions. | 
|---|
| 22 | // | 
|---|
| 23 | // The default maze size is 20x10, though different dimensions may be | 
|---|
| 24 | // specified on the command line. | 
|---|
| 25 |  | 
|---|
| 26 | // Boost | 
|---|
| 27 | #include <boost/array.hpp> | 
|---|
| 28 | #include <boost/graph/grid_graph.hpp> | 
|---|
| 29 | #include <boost/graph/filtered_graph.hpp> | 
|---|
| 30 | #include <boost/graph/astar_search.hpp> | 
|---|
| 31 |  | 
|---|
| 32 | #include <boost/random/mersenne_twister.hpp> | 
|---|
| 33 | #include <boost/random/uniform_int_distribution.hpp> | 
|---|
| 34 |  | 
|---|
| 35 | #include <boost/lexical_cast.hpp> | 
|---|
| 36 |  | 
|---|
| 37 | // STL | 
|---|
| 38 | #include <iostream> | 
|---|
| 39 | #include <vector> | 
|---|
| 40 | #include <cmath> | 
|---|
| 41 | #include <ctime> | 
|---|
| 42 |  | 
|---|
| 43 | // ------------------------------------------------------------------------------ | 
|---|
| 44 | // Types and filters | 
|---|
| 45 | // ------------------------------------------------------------------------------ | 
|---|
| 46 |  | 
|---|
| 47 | #define DIMS 2 | 
|---|
| 48 |  | 
|---|
| 49 | // Distance traveled in the maze | 
|---|
| 50 | using distance = double; | 
|---|
| 51 |  | 
|---|
| 52 | // the graph types | 
|---|
| 53 | using graph_t = boost::grid_graph < DIMS >; | 
|---|
| 54 | using traits_t = boost::graph_traits < graph_t >; | 
|---|
| 55 |  | 
|---|
| 56 | using vertex_t = traits_t::vertex_descriptor; | 
|---|
| 57 | using edge_t = traits_t::edge_descriptor; | 
|---|
| 58 |  | 
|---|
| 59 | using vertex_path = std::vector < vertex_t > ; | 
|---|
| 60 |  | 
|---|
| 61 | // the property types | 
|---|
| 62 | using index_map_type_t  = boost::property_map<graph_t, boost::vertex_index_t>::const_type; | 
|---|
| 63 | using barrier_map_t             = boost::vector_property_map < bool, index_map_type_t >; | 
|---|
| 64 | using predessor_map_t   = boost::vector_property_map < vertex_t, index_map_type_t >; | 
|---|
| 65 | using distance_map_t    = boost::vector_property_map < distance, index_map_type_t >; | 
|---|
| 66 |  | 
|---|
| 67 | // the filter | 
|---|
| 68 | template <typename BarrierMap> | 
|---|
| 69 | struct vertex_filter_no_barrier | 
|---|
| 70 | { | 
|---|
| 71 | vertex_filter_no_barrier()      {} | 
|---|
| 72 | vertex_filter_no_barrier(const BarrierMap& b) : m_barrier(b) {} | 
|---|
| 73 |  | 
|---|
| 74 | template <typename Vertex> | 
|---|
| 75 | bool operator()(const Vertex& v) const | 
|---|
| 76 | { | 
|---|
| 77 | return boost::get(m_barrier, v) == false; | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | BarrierMap m_barrier; | 
|---|
| 81 | }; | 
|---|
| 82 | struct edge_filter_dummy | 
|---|
| 83 | { | 
|---|
| 84 | edge_filter_dummy()     {} | 
|---|
| 85 |  | 
|---|
| 86 | template <typename Edge> | 
|---|
| 87 | bool operator()(const Edge& e) const | 
|---|
| 88 | { | 
|---|
| 89 | return true; | 
|---|
| 90 | } | 
|---|
| 91 | }; | 
|---|
| 92 |  | 
|---|
| 93 | // the fitered grid | 
|---|
| 94 | using filtered_graph_t = boost::filtered_graph < graph_t, edge_filter_dummy, vertex_filter_no_barrier<barrier_map_t>>; | 
|---|
| 95 |  | 
|---|
| 96 | // ------------------------------------------------------------------------------ | 
|---|
| 97 | // Helper | 
|---|
| 98 | // ------------------------------------------------------------------------------ | 
|---|
| 99 | vertex_t start_vertex(const graph_t& /*graph*/) | 
|---|
| 100 | { | 
|---|
| 101 | /* | 
|---|
| 102 | size_t size_x = graph.length(0); | 
|---|
| 103 | size_t size_y = graph.length(1); | 
|---|
| 104 | */ | 
|---|
| 105 | vertex_t v = { 0, 0 }; | 
|---|
| 106 | return v; | 
|---|
| 107 | } | 
|---|
| 108 | vertex_t goal_vertex(const graph_t& graph) | 
|---|
| 109 | { | 
|---|
| 110 | size_t size_x = graph.length(0); | 
|---|
| 111 | size_t size_y = graph.length(1); | 
|---|
| 112 |  | 
|---|
| 113 | vertex_t v = { size_x - 1, size_y - 1 }; | 
|---|
| 114 | return v; | 
|---|
| 115 | } | 
|---|
| 116 |  | 
|---|
| 117 | #define BARRIER "#" | 
|---|
| 118 | void print_maze(const graph_t& graph, const barrier_map_t& barrier, const vertex_path& solution) | 
|---|
| 119 | { | 
|---|
| 120 | size_t size_x = graph.length(0); | 
|---|
| 121 | size_t size_y = graph.length(1); | 
|---|
| 122 |  | 
|---|
| 123 | // Header | 
|---|
| 124 | for (size_t i = 0; i < size_x + 2; i++) | 
|---|
| 125 | std::cout << BARRIER; | 
|---|
| 126 | std::cout << std::endl; | 
|---|
| 127 |  | 
|---|
| 128 | vertex_t s = start_vertex(graph); | 
|---|
| 129 | vertex_t g = goal_vertex(graph); | 
|---|
| 130 |  | 
|---|
| 131 | // body | 
|---|
| 132 | // x: left to right | 
|---|
| 133 | // y: bottom to top (0 bottom, growing upwards) | 
|---|
| 134 | for (size_t y = size_y - 1; y < size_y; --y) // underflow intended | 
|---|
| 135 | { | 
|---|
| 136 | for (size_t x = 0; x < size_x; ++x) | 
|---|
| 137 | { | 
|---|
| 138 | // barrier on the left side | 
|---|
| 139 | if (x == 0) | 
|---|
| 140 | { | 
|---|
| 141 | std::cout << BARRIER; | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 144 | vertex_t v = { x, y }; | 
|---|
| 145 | bool is_solution_vtx  = | 
|---|
| 146 | (std::find(solution.begin(), solution.end(), v) != solution.end()); | 
|---|
| 147 |  | 
|---|
| 148 | if (get(barrier, v)) | 
|---|
| 149 | { | 
|---|
| 150 | std::cout << "#"; | 
|---|
| 151 | } | 
|---|
| 152 | else if (is_solution_vtx) | 
|---|
| 153 | { | 
|---|
| 154 | std::cout << "."; | 
|---|
| 155 | } | 
|---|
| 156 | else if (v == s) | 
|---|
| 157 | { | 
|---|
| 158 | std::cout << "S"; | 
|---|
| 159 | } | 
|---|
| 160 | else if (v == g) | 
|---|
| 161 | { | 
|---|
| 162 | std::cout << "G"; | 
|---|
| 163 | } | 
|---|
| 164 | else | 
|---|
| 165 | { | 
|---|
| 166 | std::cout << " "; | 
|---|
| 167 | } | 
|---|
| 168 |  | 
|---|
| 169 | // barrier on the right side | 
|---|
| 170 | if (x == size_x-1) | 
|---|
| 171 | { | 
|---|
| 172 | std::cout << BARRIER; | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | } | 
|---|
| 176 | std::cout << std::endl; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | // footer | 
|---|
| 180 | for (size_t i = 0; i < size_x + 2; i++) | 
|---|
| 181 | std::cout << BARRIER; | 
|---|
| 182 | std::cout << std::endl; | 
|---|
| 183 |  | 
|---|
| 184 | } | 
|---|
| 185 |  | 
|---|
| 186 | void create_maze(const graph_t& graph, barrier_map_t& barrier) | 
|---|
| 187 | { | 
|---|
| 188 | std::cout << "Creating Barriers ..." << std::endl; | 
|---|
| 189 |  | 
|---|
| 190 | size_t size_x = graph.length(0); | 
|---|
| 191 | size_t size_y = graph.length(1); | 
|---|
| 192 | size_t total_space = size_x * size_y; | 
|---|
| 193 |  | 
|---|
| 194 | size_t barriers_left = total_space / 4; | 
|---|
| 195 | boost::random::mt19937 gen; | 
|---|
| 196 | gen.seed(std::time(0)); | 
|---|
| 197 |  | 
|---|
| 198 | boost::random::uniform_int_distribution<> start_x(0, size_x - 1); | 
|---|
| 199 | boost::random::uniform_int_distribution<> start_y(0, size_y - 1); | 
|---|
| 200 | boost::random::uniform_int_distribution<> direction(0, 1); | 
|---|
| 201 | boost::random::uniform_int_distribution<> wall_length_x(1, size_x/4); | 
|---|
| 202 | boost::random::uniform_int_distribution<> wall_length_y(1, size_y/4); | 
|---|
| 203 |  | 
|---|
| 204 | // start and goal vertex | 
|---|
| 205 | vertex_t s = start_vertex(graph); | 
|---|
| 206 | vertex_t g = goal_vertex(graph); | 
|---|
| 207 |  | 
|---|
| 208 | while (barriers_left) | 
|---|
| 209 | { | 
|---|
| 210 | size_t sx = start_x(gen); | 
|---|
| 211 | size_t sy = start_y(gen); | 
|---|
| 212 |  | 
|---|
| 213 | int dx, dy, wall; | 
|---|
| 214 | if (direction(gen) == 0) | 
|---|
| 215 | { | 
|---|
| 216 | // x | 
|---|
| 217 | dx = 1; dy = 0; | 
|---|
| 218 | wall = wall_length_x(gen); | 
|---|
| 219 | } | 
|---|
| 220 | else | 
|---|
| 221 | { | 
|---|
| 222 | // y | 
|---|
| 223 | dx = 0; dy = 1; | 
|---|
| 224 | wall = wall_length_y(gen); | 
|---|
| 225 | } | 
|---|
| 226 |  | 
|---|
| 227 | // limit wall length to 20 | 
|---|
| 228 | if (wall > 10) wall = 10; | 
|---|
| 229 |  | 
|---|
| 230 | while (wall > 0 && barriers_left > 0) | 
|---|
| 231 | { | 
|---|
| 232 | vertex_t v = { sx, sy }; | 
|---|
| 233 |  | 
|---|
| 234 | // we dont put a barrier on start or goal | 
|---|
| 235 | if (v == s || v == g) | 
|---|
| 236 | break; | 
|---|
| 237 |  | 
|---|
| 238 | bool current_value = boost::get(barrier, v); | 
|---|
| 239 | if (current_value == false) | 
|---|
| 240 | { | 
|---|
| 241 | boost::put(barrier, v, true); | 
|---|
| 242 | barriers_left--; | 
|---|
| 243 | wall--; | 
|---|
| 244 | } | 
|---|
| 245 | else | 
|---|
| 246 | { | 
|---|
| 247 | // stop this wall on this obstacle | 
|---|
| 248 | break; | 
|---|
| 249 | } | 
|---|
| 250 | // next vertex | 
|---|
| 251 | sx += dx; sy += dy; | 
|---|
| 252 |  | 
|---|
| 253 | // we dont go outside of our grid | 
|---|
| 254 | if (sx >= size_x || sy >= size_y) | 
|---|
| 255 | break; | 
|---|
| 256 | if (barriers_left % 100 == 0) | 
|---|
| 257 | { | 
|---|
| 258 | std::cout << barriers_left << "..."; | 
|---|
| 259 | } | 
|---|
| 260 | } | 
|---|
| 261 | } | 
|---|
| 262 | std::cout << std::endl; | 
|---|
| 263 | } | 
|---|
| 264 | // ------------------------------------------------------------------------------ | 
|---|
| 265 | // AStar | 
|---|
| 266 | // ------------------------------------------------------------------------------ | 
|---|
| 267 | // Visitor that terminates when we find the goal vertex | 
|---|
| 268 | struct found_goal {}; // exception which is thrown when we find a solution | 
|---|
| 269 | struct astar_goal_visitor :public boost::default_astar_visitor | 
|---|
| 270 | { | 
|---|
| 271 | astar_goal_visitor(vertex_t goal) :m_goal(goal) {}; | 
|---|
| 272 |  | 
|---|
| 273 | void examine_vertex(vertex_t u, const filtered_graph_t&) | 
|---|
| 274 | { | 
|---|
| 275 | if (u == m_goal) | 
|---|
| 276 | throw found_goal(); | 
|---|
| 277 | } | 
|---|
| 278 |  | 
|---|
| 279 | private: | 
|---|
| 280 | vertex_t m_goal; | 
|---|
| 281 | }; | 
|---|
| 282 |  | 
|---|
| 283 | // This calculates the Euclidean distance between a vertex and a goal vertex. | 
|---|
| 284 | class euclidean_heuristic : | 
|---|
| 285 | public boost::astar_heuristic<filtered_graph_t, double> | 
|---|
| 286 | { | 
|---|
| 287 | public: | 
|---|
| 288 | euclidean_heuristic(vertex_t goal) :m_goal(goal) {}; | 
|---|
| 289 |  | 
|---|
| 290 | double operator()(vertex_t v) { | 
|---|
| 291 | return sqrt(pow(double(m_goal[0] - v[0]), 2) + pow(double(m_goal[1] - v[1]), 2)); | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | private: | 
|---|
| 295 | vertex_t m_goal; | 
|---|
| 296 | }; | 
|---|
| 297 |  | 
|---|
| 298 | void solve(vertex_t s, vertex_t g, filtered_graph_t& fg, vertex_path& solution) | 
|---|
| 299 | { | 
|---|
| 300 | // get the index map for the associated properties | 
|---|
| 301 | index_map_type_t index_map(get(boost::vertex_index, fg)); | 
|---|
| 302 |  | 
|---|
| 303 | // The predecessor map is a vertex-to-vertex mapping. | 
|---|
| 304 | predessor_map_t predecessor(num_vertices(fg), index_map); | 
|---|
| 305 |  | 
|---|
| 306 | // The distance map is a vertex-to-distance mapping. | 
|---|
| 307 | distance_map_t distances(num_vertices(fg), index_map); | 
|---|
| 308 |  | 
|---|
| 309 | // finally the weight map | 
|---|
| 310 | boost::static_property_map<distance> weight(1); | 
|---|
| 311 |  | 
|---|
| 312 | euclidean_heuristic heuristic(g); | 
|---|
| 313 | astar_goal_visitor visitor(g); | 
|---|
| 314 |  | 
|---|
| 315 | try { | 
|---|
| 316 | astar_search(fg, s, heuristic, | 
|---|
| 317 | boost::weight_map(weight). | 
|---|
| 318 | predecessor_map(predecessor). | 
|---|
| 319 | distance_map(distances). | 
|---|
| 320 | visitor(visitor)); | 
|---|
| 321 | } | 
|---|
| 322 | catch (const found_goal&  /*fg*/) { | 
|---|
| 323 | // Walk backwards from the goal through the predecessor chain adding | 
|---|
| 324 | // vertices to the solution path. | 
|---|
| 325 | for (vertex_t u = g; u != s; u = predecessor[u]) | 
|---|
| 326 | { | 
|---|
| 327 | if (u != s && u != g)   solution.push_back(u); | 
|---|
| 328 | } | 
|---|
| 329 | } | 
|---|
| 330 | } | 
|---|
| 331 | // ------------------------------------------------------------------------------ | 
|---|
| 332 | // main | 
|---|
| 333 | // ------------------------------------------------------------------------------ | 
|---|
| 334 | int main(int argc, char** argv) | 
|---|
| 335 | { | 
|---|
| 336 | // The default maze size is 20x10.  A different size may be specified on | 
|---|
| 337 | // the command line. | 
|---|
| 338 | std::size_t x = 20; | 
|---|
| 339 | std::size_t y = 10; | 
|---|
| 340 |  | 
|---|
| 341 | if (argc == 3) { | 
|---|
| 342 | x = boost::lexical_cast<std::size_t>(argv[1]); | 
|---|
| 343 | y = boost::lexical_cast<std::size_t>(argv[2]); | 
|---|
| 344 | } | 
|---|
| 345 |  | 
|---|
| 346 | // Define a grid which doesnt wrap | 
|---|
| 347 | boost::array<std::size_t, DIMS> lengths = { x, y }; | 
|---|
| 348 | boost::array<bool, DIMS> wrapped = { false, false }; | 
|---|
| 349 | graph_t graph(lengths, wrapped); | 
|---|
| 350 |  | 
|---|
| 351 | // Get the index map of the grid graph | 
|---|
| 352 | index_map_type_t index_map(get(boost::vertex_index, graph)); | 
|---|
| 353 |  | 
|---|
| 354 | // create the property for the vertexes: Barrier | 
|---|
| 355 | barrier_map_t barrier_map(num_vertices(graph), index_map); | 
|---|
| 356 |  | 
|---|
| 357 | // create the walls | 
|---|
| 358 | create_maze(graph, barrier_map); | 
|---|
| 359 |  | 
|---|
| 360 | // filter the graph | 
|---|
| 361 | filtered_graph_t fg(graph, edge_filter_dummy(), vertex_filter_no_barrier<barrier_map_t>(barrier_map)); | 
|---|
| 362 |  | 
|---|
| 363 | vertex_path solution; | 
|---|
| 364 | vertex_t s = start_vertex(graph); | 
|---|
| 365 | vertex_t g = goal_vertex(graph); | 
|---|
| 366 | solve(s,g,fg, solution); | 
|---|
| 367 | print_maze(graph, barrier_map, solution); | 
|---|
| 368 |  | 
|---|
| 369 | if (solution.size()) | 
|---|
| 370 | std::cout << "Maze solved" << std::endl; | 
|---|
| 371 | else | 
|---|
| 372 | std::cout << "Maze _NOT_ solved" << std::endl; | 
|---|
| 373 | } | 
|---|