Euphoria
generator_maze.cc
Go to the documentation of this file.
1 #include "core/generator_maze.h"
2 
3 #include "base/random.h"
4 #include "core/image_draw.h"
5 #include "base/colors.h"
6 
7 namespace eu::core::generator
8 {
9  vec2i
11  {
12  switch(d)
13  {
14  case Direction::south: return { 0, -1};
15  case Direction::north: return { 0, 1};
16  case Direction::west: return {-1, 0};
17  case Direction::east: return { 1, 0};
18  default: return {0, 0};
19  }
20  }
21 
22 
23  const std::vector<Direction>&
25  {
26  const static auto dirs = std::vector<Direction>
27  {
29  };
30  return dirs;
31  }
32 
33 
34  Direction
36  {
37  switch(d)
38  {
41  case Direction::east: return Direction::west;
42  case Direction::west: return Direction::east;
43  default: return Direction::north;
44  }
45  }
46 
47 
50  {
51  switch(d)
52  {
55  case Direction::east: return cell::path_east;
56  case Direction::west: return cell::path_west;
57  default: return cell::path_north;
58  }
59  }
60 
61 
62  void
63  set_visited(Maze* maze, const vec2i& np)
64  {
65  (*maze)[np] |= cell::visited;
66  }
67 
68 
69  vec2i
70  add_step_to_maze(Maze* maze, const vec2i& c, Direction dir)
71  {
72  const auto o = from_dir_to_offset(dir);
73  const auto np = c + o;
74  (*maze)[np] |= cell::visited | from_dir_to_cell_path(flip_direction(dir));
75  (*maze)[c] |= from_dir_to_cell_path(dir);
76  return np;
77  }
78 
79 
80  bool
81  has_visited(Maze* maze, const vec2i& np)
82  {
83  return ((*maze)[np] &cell::visited) != 0;
84  }
85 
86 
87  bool
89  {
90  const auto world_size = Recti::from_width_height
91  (
92  maze->get_width() - 1,
93  maze->get_height() - 1
94  );
95  return world_size.contains_inclusive(np) && !has_visited(maze, np);
96  }
97 
98 
99  template <typename T>
100  T
101  pop_random(std::vector<T>* vec, Random* r)
102  {
103  ASSERT(!vec->empty());
104  const auto i = get_random_in_range(r, vec->size());
105  T t = (*vec)[i];
106  vec->erase(vec->begin() + i);
107  return t;
108  }
109 
110 
111  vec2i
113  {
114  return
115  {
118  };
119  }
120 
121 
123 
124  void
126  {
128 
129  const auto random_position = calc_random_position_on_maze(random, maze);
130 
131  stack.push(random_position);
132  (*maze)[random_position] = cell::visited;
133  visited_cells = 1;
134  }
135 
136 
137  bool
139  {
140  return false == (visited_cells < maze->get_width() * maze->get_height());
141  }
142 
143 
144  void
146  {
147  const auto c = stack.top();
148 
149  std::vector<Direction> neighbours;
150 
151  for(auto d: get_all_directions())
152  {
153  const auto np = c + from_dir_to_offset(d);
155  {
156  neighbours.push_back(d);
157  }
158  }
159 
160  if(neighbours.empty())
161  {
162  stack.pop();
163  }
164  else
165  {
166  const Direction dir = get_random_item_in_vector(random, neighbours);
167  auto np = add_step_to_maze(maze, c, dir);
168  stack.push(np);
169  visited_cells += 1;
170  }
171  }
172 
173 
175 
176  void
178  (
179  Maze* maze,
180  std::vector<RandomTraversal::Entry>* frontier,
181  const vec2i& p
182  )
183  {
184  set_visited(maze, p);
185  for(auto d: get_all_directions())
186  {
188  {
189  frontier->push_back({p, d});
190  }
191  }
192  }
193 
194 
195  void
197  {
200  }
201 
202 
203  bool
205  {
206  return frontier.empty();
207  }
208 
209 
210  void
212  {
213  auto f = pop_random(&frontier, random);
214  const auto np = f.position + from_dir_to_offset(f.direction);
215 
217  {
218  return;
219  }
220 
221  add_step_to_maze(maze, f.position, f.direction);
223  }
224 
225 
227 
228 
230  : wall_color(NamedColor::black)
231  , cell_color(NamedColor::light_gray)
232  , cell_visited_color(NamedColor::white)
233  , unit_color(NamedColor::red)
234  , corridor_color(NamedColor::white)
235  {
236  }
237 
238 
239  Rgbi
240  Drawer::calc_cell_color(int x, int y) const
241  {
242  const auto cell_value = (*maze)[{x, y}];
243 
244  if(tracker != nullptr && false == tracker->is_done() && false == tracker->stack.empty())
245  {
246  const auto t = tracker->stack.top();
247  if(x == t.x && y == t.y)
248  {
249  return unit_color;
250  }
251  }
252 
253  if(traversal != nullptr)
254  {
255  for(auto e: traversal->frontier)
256  {
257  auto p = e.position + from_dir_to_offset(e.direction);
258  if(p.x == x && p.y == y)
259  {
260  return unit_color;
261  }
262  }
263  }
264 
265  if((cell_value & cell::visited) != 0)
266  {
267  return cell_visited_color;
268  }
269  else
270  {
271  return cell_color;
272  }
273  }
274 
275 
276  void
278  {
279  const auto path_size = cell_size + wall_size;
280 
282  (
283  wall_size + maze->get_width() * path_size,
284  wall_size + maze->get_height() * path_size
285  );
286 
288 
289  for(int x = 0; x < maze->get_width(); x += 1)
290  {
291  for(int y = 0; y < maze->get_height(); y += 1)
292  {
293  const auto px = wall_size + x * path_size;
294  const auto py = wall_size + y * path_size + cell_size - 1;
295 
297  (
298  &image,
299  calc_cell_color(x, y),
300  px,
301  py,
302  cell_size
303  );
304 
305  const auto xywh = [](int ax, int ay, int aw, int ah)
306  {
307  return Recti::from_top_left_width_height(vec2i{ax, ay + 1}, aw, ah);
308  };
309 
310  const auto cell_value = (*maze)[{x, y}];
311 
312  if((cell_value & cell::path_south) != 0)
313  {
314  draw_rect
315  (
316  &image,
318  xywh(px, py - cell_size, cell_size, wall_size)
319  );
320  }
321  if((cell_value & cell::path_east) != 0)
322  {
323  draw_rect
324  (
325  &image,
327  xywh(px + cell_size, py, wall_size, cell_size)
328  );
329  }
330  }
331  }
332  }
333 
334 
335 }
#define ASSERT(x)
Definition: assert.h:29
cell::Type from_dir_to_cell_path(const Direction d)
vec2i calc_random_position_on_maze(Random *random, Maze *maze)
bool has_visited(Maze *maze, const vec2i &np)
void add_to_frontier(Maze *maze, std::vector< RandomTraversal::Entry > *frontier, const vec2i &p)
vec2i add_step_to_maze(Maze *maze, const vec2i &c, Direction dir)
bool can_visit_without_making_loop(Maze *maze, const vec2i &np)
vec2i from_dir_to_offset(const Direction d)
const std::vector< Direction > & get_all_directions()
Direction flip_direction(const Direction d)
T pop_random(std::vector< T > *vec, Random *r)
void set_visited(Maze *maze, const vec2i &np)
void clear(Image *image, const Rgbai &color)
Definition: image_draw.cc:32
void draw_square(Image *image, const Rgbai &color, int x, int y, int size)
Definition: image_draw.cc:67
void draw_rect(Image *image, const Rgbai &color, const Recti &rect)
Definition: image_draw.cc:39
NamedColor
Definition: colors.h:12
T get_random_in_range(Random *rand, const Range< T > &range)
Definition: random.h:50
const T & get_random_item_in_vector(Random *r, const std::vector< T > &v)
Definition: random.h:68
WEL512 Random Number Generator.
Definition: random.h:21
static Recti from_width_height(int width, int height)
Definition: rect.cc:573
static Recti from_top_left_width_height(const vec2i &topleft, int width, int height)
Definition: rect.cc:561
Definition: rgb.h:26
void setup_no_alpha_support(int image_width, int image_height, int default_value=0)
Definition: image.cc:52
Idx get_height() const
Definition: table.h:158
Idx get_width() const
Definition: table.h:153
void clear(T d=T())
Definition: table.h:40
RecursiveBacktracker * tracker
RandomTraversal * traversal
Rgbi calc_cell_color(int x, int y) const
Definition: vec2.h:72