1 /**
2 * @file
3 * @brief Coordinate iterators.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "coordit.h"
9
10 #include "coord.h"
11 #include "libutil.h"
12 #include "losglobal.h"
13 #include "los.h"
14 #include "random.h"
15
rectangle_iterator(const coord_def & corner1,const coord_def & corner2)16 rectangle_iterator::rectangle_iterator(const coord_def& corner1,
17 const coord_def& corner2)
18 {
19 topleft.x = min(corner1.x, corner2.x);
20 topleft.y = min(corner1.y, corner2.y); // not really necessary
21 bottomright.x = max(corner1.x, corner2.x);
22 bottomright.y = max(corner1.y, corner2.y);
23 current = topleft;
24 }
25
rectangle_iterator(const coord_def & center,int halfside,bool clip_to_map)26 rectangle_iterator::rectangle_iterator(const coord_def& center, int halfside,
27 bool clip_to_map)
28 {
29 topleft.x = center.x - halfside;
30 topleft.y = center.y - halfside;
31 if (clip_to_map)
32 {
33 topleft.x = max(topleft.x, X_BOUND_1);
34 topleft.y = max(topleft.y, Y_BOUND_1);
35 }
36 bottomright.x = center.x + halfside;
37 bottomright.y = center.y + halfside;
38 if (clip_to_map)
39 {
40 bottomright.x = min(bottomright.x, X_BOUND_2);
41 bottomright.y = min(bottomright.y, Y_BOUND_2);
42 }
43 current = topleft;
44 }
45
rectangle_iterator(int x_border_dist,int y_border_dist)46 rectangle_iterator::rectangle_iterator(int x_border_dist, int y_border_dist)
47 {
48 if (y_border_dist < 0)
49 y_border_dist = x_border_dist;
50
51 topleft.set(x_border_dist, y_border_dist);
52 bottomright.set(GXM - x_border_dist - 1, GYM - y_border_dist - 1);
53 current = topleft;
54 }
55
operator bool() const56 rectangle_iterator::operator bool() const
57 {
58 return current.y <= bottomright.y;
59 }
60
operator *() const61 coord_def rectangle_iterator::operator *() const
62 {
63 return current;
64 }
65
operator ->() const66 const coord_def* rectangle_iterator::operator->() const
67 {
68 return ¤t;
69 }
70
operator ++()71 void rectangle_iterator::operator ++()
72 {
73 if (current.x == bottomright.x)
74 {
75 current.x = topleft.x;
76 current.y++;
77 }
78 else
79 current.x++;
80 }
81
operator ++(int)82 void rectangle_iterator::operator++(int)
83 {
84 ++(*this);
85 }
86
random_rectangle_iterator(const coord_def & corner1,const coord_def & corner2)87 random_rectangle_iterator::random_rectangle_iterator(const coord_def& corner1,
88 const coord_def& corner2)
89 {
90 int left = min(corner1.x, corner2.x);
91 int right = max(corner1.x, corner2.x);
92 int top = min(corner1.y, corner2.y);
93 int bottom = max(corner1.y, corner2.y);
94
95 top_left.x = left;
96 top_left.y = top;
97
98 for (int y = top; y <= bottom; y++)
99 for (int x = left; x <= right; x++)
100 remaining.emplace_back(x, y);
101
102 if (remaining.empty())
103 current = 0;
104 else
105 current = random2(remaining.size());
106 }
107
random_rectangle_iterator(int x_border_dist,int y_border_dist)108 random_rectangle_iterator::random_rectangle_iterator(int x_border_dist,
109 int y_border_dist)
110 {
111 if (y_border_dist < 0)
112 y_border_dist = x_border_dist;
113
114 int right = GXM - x_border_dist - 1;
115 int bottom = GYM - y_border_dist - 1;
116
117 top_left.x = x_border_dist;
118 top_left.y = y_border_dist;
119
120 for (int y = y_border_dist; y <= bottom; y++)
121 for (int x = x_border_dist; x <= right; x++)
122 remaining.emplace_back(x, y);
123
124 if (remaining.empty())
125 current = 0;
126 else
127 current = random2(remaining.size());
128 }
129
operator bool() const130 random_rectangle_iterator::operator bool() const
131 {
132 return !remaining.empty();
133 }
134
operator *() const135 coord_def random_rectangle_iterator::operator *() const
136 {
137 if (remaining.empty())
138 return top_left;
139 else
140 return remaining[current];
141 }
142
operator ->() const143 const coord_def* random_rectangle_iterator::operator->() const
144 {
145 if (remaining.empty())
146 return &top_left;
147 else
148 return &(remaining[current]);
149 }
150
operator ++()151 void random_rectangle_iterator::operator ++()
152 {
153 if (!remaining.empty())
154 {
155 remaining[current] = remaining.back();
156 remaining.pop_back();
157 if (!remaining.empty())
158 current = random2(remaining.size());
159 }
160 }
161
operator ++(int)162 void random_rectangle_iterator::operator++(int)
163 {
164 ++(*this);
165 }
166
167 /*
168 * radius iterator
169 */
radius_iterator(const coord_def _center,int r,circle_type ctype,bool _exclude_center)170 radius_iterator::radius_iterator(const coord_def _center, int r,
171 circle_type ctype,
172 bool _exclude_center)
173 : state(RI_START),
174 center(_center),
175 los(LOS_NONE)
176 {
177 ASSERT(map_bounds(_center));
178 switch (ctype)
179 {
180 case C_CIRCLE: credit = r; break;
181 case C_POINTY: credit = r * r; break;
182 case C_ROUND: credit = r * r + 1; break;
183 case C_SQUARE: credit = r; break;
184 }
185 is_square = (ctype == C_SQUARE);
186 ++(*this);
187 if (_exclude_center)
188 ++(*this);
189 }
190
radius_iterator(const coord_def _center,los_type _los,bool _exclude_center)191 radius_iterator::radius_iterator(const coord_def _center,
192 los_type _los,
193 bool _exclude_center)
194 : state(RI_START),
195 center(_center),
196 los(_los)
197 {
198 ASSERT(map_bounds(_center));
199 credit = get_los_radius();
200 is_square = true;
201 ++(*this);
202 if (_exclude_center)
203 ++(*this);
204 }
205
radius_iterator(const coord_def _center,int r,circle_type ctype,los_type _los,bool _exclude_center)206 radius_iterator::radius_iterator(const coord_def _center,
207 int r,
208 circle_type ctype,
209 los_type _los,
210 bool _exclude_center)
211 : state(RI_START),
212 center(_center),
213 los(_los)
214 {
215 ASSERT(map_bounds(_center));
216 switch (ctype)
217 {
218 case C_CIRCLE: credit = r; break;
219 case C_POINTY: credit = r * r; break;
220 case C_ROUND: credit = r * r + 1; break;
221 case C_SQUARE: credit = r; break;
222 }
223 is_square = (ctype == C_SQUARE);
224 ++(*this);
225 if (_exclude_center)
226 ++(*this);
227 }
228
operator bool() const229 radius_iterator::operator bool() const
230 {
231 return state;
232 }
233
operator *() const234 coord_def radius_iterator::operator *() const
235 {
236 return current;
237 }
238
operator ->() const239 const coord_def* radius_iterator::operator->() const
240 {
241 return ¤t;
242 }
243
244 #define coreturn(id) { state = id; return; case id:; }
245 #define cobegin(id) switch (state) { case id:
246 #define coend(id) coreturn(id); }
247 #define ret_coord(dx, dy, id) do \
248 { \
249 current.x = center.x + (dx); \
250 current.y = center.y + (dy); \
251 if (!los || cell_see_cell(center, current, los)) \
252 coreturn(id); \
253 } while (0)
254
operator ++()255 void radius_iterator::operator++()
256 {
257 cobegin(RI_START);
258
259 base_cost = is_square ? 1 : -1;
260 inc_cost = is_square ? 0 : 2;
261
262 y = 0;
263 cost_y = base_cost;
264 credit_y = credit;
265
266 do
267 {
268 x = 0;
269 cost_x = base_cost;
270 credit_x = (is_square ? credit : credit_y);
271
272 do
273 {
274 if (x + center.x < GXM)
275 {
276 if (y + center.y < GYM)
277 ret_coord( x, y, RI_SE);
278 if (y && y <= center.y)
279 ret_coord( x, -y, RI_NE);
280 }
281 if (x && x <= center.x)
282 {
283 if (y + center.y < GYM)
284 ret_coord(-x, y, RI_SW);
285 if (y && y <= center.y)
286 ret_coord(-x, -y, RI_NW);
287 }
288 x++;
289 credit_x -= (cost_x += inc_cost);
290 } while (credit_x >= 0);
291
292 y++;
293 credit_y -= (cost_y += inc_cost);
294 } while (credit_y >= 0);
295
296 coend(RI_DONE);
297 }
298
operator ++(int)299 void radius_iterator::operator++(int)
300 {
301 ++(*this);
302 }
303
operator ++()304 void vision_iterator::operator++()
305 {
306 do
307 {
308 radius_iterator::operator++();
309 }
310 while (*this && !who.see_cell(**this));
311 }
312
operator ++(int)313 void vision_iterator::operator++(int)
314 {
315 ++(*this);
316 }
317
318 /*
319 * adjacent iterator
320 */
321 extern const struct coord_def Compass[9];
322
operator bool() const323 adjacent_iterator::operator bool() const
324 {
325 return i >= 0;
326 }
327
operator *() const328 coord_def adjacent_iterator::operator *() const
329 {
330 return val;
331 }
332
operator ->() const333 const coord_def* adjacent_iterator::operator->() const
334 {
335 return &val;
336 }
337
operator ++()338 void adjacent_iterator::operator ++()
339 {
340 while (--i >= 0)
341 {
342 val = center + Compass[i];
343 if (map_bounds(val))
344 return;
345 }
346 }
347
operator ++(int)348 void adjacent_iterator::operator++(int)
349 {
350 ++(*this);
351 }
352
353 /*
354 * spiral iterator
355 */
distance_iterator(const coord_def & _center,bool _fair,bool exclude_center,int _max_radius)356 distance_iterator::distance_iterator(const coord_def& _center, bool _fair,
357 bool exclude_center, int _max_radius) :
358 center(_center), current(_center), r(0), max_radius(_max_radius),
359 threshold(0), icur(0), iend(0), fair(_fair)
360 {
361 vcur = lists + 0;
362 vnear = lists + 1;
363 vfar = lists + 2;
364
365 for (int dx = -1; dx <= 1; dx++)
366 for (int dy = -1; dy <= 1; dy++)
367 if (dx || dy)
368 vnear->emplace_back(dx, dy);
369
370 if (exclude_center)
371 advance();
372 }
373
advance()374 bool distance_iterator::advance()
375 {
376 while (true)
377 {
378 if (++icur >= vcur->size())
379 icur = 0;
380 if (icur == iend)
381 {
382 // Advance to the next radius.
383 vector<coord_def> *tmp = vcur;
384 vcur = vnear;
385 vnear = vfar;
386 vfar = tmp;
387 tmp->clear();
388
389 if (!vcur->size())
390 return false;
391 // Randomize the order various directions are returned.
392 // Just the initial angle is enough.
393 if (fair)
394 icur = iend = random2(vcur->size());
395 else
396 icur = iend = 0; // randomness is costly
397
398 if (r++ >= max_radius)
399 {
400 vcur->clear();
401 return false;
402 }
403 threshold = r+1;
404 }
405
406 coord_def d = (*vcur)[icur];
407 if (in_bounds(current = center + d))
408 {
409 ASSERT(d.x || d.y);
410 if (!d.y)
411 push_neigh(d, sgn(d.x), 0);
412 if (!d.x)
413 push_neigh(d, 0, sgn(d.y));
414 if (d.x <= 0)
415 {
416 if (d.y <= 0)
417 push_neigh(d, -1, -1);
418 if (d.y >= 0)
419 push_neigh(d, -1, +1);
420 }
421 if (d.x >= 0)
422 {
423 if (d.y <= 0)
424 push_neigh(d, +1, -1);
425 if (d.y >= 0)
426 push_neigh(d, +1, +1);
427 }
428
429 return true;
430 }
431 }
432 }
433
push_neigh(coord_def d,int dx,int dy)434 void distance_iterator::push_neigh(coord_def d, int dx, int dy)
435 {
436 d.x += dx;
437 d.y += dy;
438 ((d.rdist() <= threshold) ? vnear : vfar)->push_back(d);
439 }
440
operator bool() const441 distance_iterator::operator bool() const
442 {
443 return in_bounds(current) && r <= max_radius;
444 }
445
operator *() const446 coord_def distance_iterator::operator *() const
447 {
448 return current;
449 }
450
operator ->() const451 const coord_def* distance_iterator::operator->() const
452 {
453 return ¤t;
454 }
455
operator ++()456 const distance_iterator& distance_iterator::operator++()
457 {
458 advance();
459 return *this;
460 }
461
operator ++(int)462 void distance_iterator::operator++(int)
463 {
464 ++(*this);
465 }
466
radius() const467 int distance_iterator::radius() const
468 {
469 return r;
470 }
471
472 /********************/
473 /* regression tests */
474 /********************/
475 #ifdef DEBUG_TESTS
_test_ai(const coord_def c,bool exc,size_t expected)476 static void _test_ai(const coord_def c, bool exc, size_t expected)
477 {
478 set<coord_def> seen;
479
480 for (adjacent_iterator ai(c, exc); ai; ++ai)
481 {
482 if (seen.count(*ai))
483 die("adjacent_iterator: %d,%d seen twice", ai->x, ai->y);
484 seen.insert(*ai);
485
486 if (c == *ai && !exc)
487 continue;
488 if ((c - *ai).rdist() != 1)
489 {
490 die("adjacent_iterator: %d,%d not adjacent to %d,%d",
491 ai->x, ai->y, c.x, c.y);
492 }
493 }
494
495 if (seen.size() != expected)
496 {
497 die("adjacent_iterator(%d,%d): seen %d, expected %d",
498 c.x, c.y, (int)seen.size(), (int)expected);
499 }
500 }
501
coordit_tests()502 void coordit_tests()
503 {
504 // bounding box of our playground
505 #define BC 16
506 #define BBOX 32
507 int los_radius = get_los_radius();
508 ASSERT(los_radius < BC - 2);
509 coord_def center(BC, BC);
510
511 FixedBitArray<BBOX, BBOX> seen;
512
513 for (int r = 0; r <= los_radius * los_radius + 1; ++r)
514 {
515 seen.reset();
516
517 for (radius_iterator ri(center, r, C_CIRCLE); ri; ++ri)
518 {
519 if (seen(*ri))
520 die("radius_iterator(C%d): %d,%d seen twice", r, ri->x, ri->y);
521 seen.set(*ri);
522 }
523
524 for (int x = 0; x < BBOX; x++)
525 for (int y = 0; y < BBOX; y++)
526 {
527 bool in = sqr(x - BC) + sqr(y - BC) <= r;
528 if (seen(coord_def(x, y)) != in)
529 {
530 die("radius_iterator(C%d) mismatch at %d,%d: %d != %d",
531 r, x, y, seen(coord_def(x, y)), in);
532 }
533 }
534 }
535
536 for (radius_iterator ri(coord_def(2, 2), 5, C_ROUND); ri; ++ri)
537 if (!map_bounds(*ri))
538 die("radius_iterator(R5) out of bounds at %d, %d", ri->x, ri->y);
539 for (radius_iterator ri(coord_def(GXM - 1, GYM - 1), 7, C_ROUND); ri; ++ri)
540 if (!map_bounds(*ri))
541 die("radius_iterator(R7) out of bounds at %d, %d", ri->x, ri->y);
542
543 seen.reset();
544 int rd = 0;
545 for (distance_iterator di(center, true, false, BC - 1); di; ++di)
546 {
547 if (seen(*di))
548 die("distance_iterator: %d,%d seen twice", di->x, di->y);
549 seen.set(*di);
550
551 int rc = (center - *di).rdist();
552 if (rc < rd)
553 die("distance_iterator went backwards");
554 rd = rc;
555 }
556
557 for (int x = 0; x < BBOX; x++)
558 for (int y = 0; y < BBOX; y++)
559 {
560 bool in = max(abs(x - BC), abs(y - BC)) <= BC - 1;
561 if (seen(coord_def(x, y)) != in)
562 {
563 die("distance_iterator mismatch at %d,%d: %d != %d",
564 x, y, seen(coord_def(x, y)), in);
565 }
566 }
567
568 _test_ai(center, false, 9);
569 _test_ai(center, true, 8);
570 _test_ai(coord_def(3, 0), false, 6);
571 _test_ai(coord_def(3, 0), true, 5);
572 _test_ai(coord_def(0, 0), false, 4);
573 _test_ai(coord_def(0, 0), true, 3);
574 _test_ai(coord_def(GXM, GYM), false, 1);
575 _test_ai(coord_def(GXM, GYM), true, 1);
576 }
577 #endif
578