Ticket #10896: astar_maze2.cpp

File astar_maze2.cpp, 9.8 KB (added by georg@…, 8 years ago)

rewritten sample astar_maze2.cpp

Line 
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
50using distance = double;
51
52// the graph types
53using graph_t = boost::grid_graph < DIMS >;
54using traits_t = boost::graph_traits < graph_t >;
55
56using vertex_t = traits_t::vertex_descriptor;
57using edge_t = traits_t::edge_descriptor;
58
59using vertex_path = std::vector < vertex_t > ;
60
61// the property types
62using index_map_type_t = boost::property_map<graph_t, boost::vertex_index_t>::const_type;
63using barrier_map_t = boost::vector_property_map < bool, index_map_type_t >;
64using predessor_map_t = boost::vector_property_map < vertex_t, index_map_type_t >;
65using distance_map_t = boost::vector_property_map < distance, index_map_type_t >;
66
67// the filter
68template <typename BarrierMap>
69struct 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};
82struct 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
94using filtered_graph_t = boost::filtered_graph < graph_t, edge_filter_dummy, vertex_filter_no_barrier<barrier_map_t>>;
95
96// ------------------------------------------------------------------------------
97// Helper
98// ------------------------------------------------------------------------------
99vertex_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}
108vertex_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 "#"
118void 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
186void 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
268struct found_goal {}; // exception which is thrown when we find a solution
269struct 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
279private:
280 vertex_t m_goal;
281};
282
283// This calculates the Euclidean distance between a vertex and a goal vertex.
284class euclidean_heuristic :
285 public boost::astar_heuristic<filtered_graph_t, double>
286{
287public:
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
294private:
295 vertex_t m_goal;
296};
297
298void 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// ------------------------------------------------------------------------------
334int 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}