1 #include "AppHdr.h"
2 
3 #include "mon-pathfind.h"
4 
5 #include "directn.h"
6 #include "env.h"
7 #include "los.h"
8 #include "misc.h"
9 #include "mon-movetarget.h"
10 #include "mon-place.h"
11 #include "religion.h"
12 #include "state.h"
13 #include "terrain.h"
14 #include "traps.h"
15 
16 /////////////////////////////////////////////////////////////////////////////
17 // monster_pathfind
18 
19 // The pathfinding is an implementation of the A* algorithm. Beginning at the
20 // monster position we check all neighbours of a given grid, estimate the
21 // distance needed for any shortest path including this grid and push the
22 // result into a hash. We can then easily access all points with the shortest
23 // distance estimates and then check _their_ neighbours and so on.
24 // The algorithm terminates once we reach the destination since - because
25 // of the sorting of grids by shortest distance in the hash - there can be no
26 // path between start and target that is shorter than the current one. There
27 // could be other paths that have the same length but that has no real impact.
28 // If the hash has been cleared and the start grid has not been encountered,
29 // then there's no path that matches the requirements fed into monster_pathfind.
30 // (These requirements are usually preference of habitat of a specific monster
31 // or a limit of the distance between start and any grid on the path.)
32 
mons_tracking_range(const monster * mon)33 int mons_tracking_range(const monster* mon)
34 {
35     int range = 0;
36     switch (mons_intel(*mon))
37     {
38     case I_BRAINLESS:
39         range = 3;
40         break;
41     case I_ANIMAL:
42         range = 5;
43         break;
44     case I_HUMAN:
45         range = LOS_DEFAULT_RANGE;
46         break;
47     }
48 
49     if (mons_is_native_in_branch(*mon))
50         range += 3;
51 
52     if (player_under_penance(GOD_ASHENZARI))
53         range *= 5;
54 
55     if (mons_foe_is_marked(*mon) || mon->has_ench(ENCH_HAUNTING))
56         range *= 5;
57 
58     ASSERT(range);
59 
60     return range;
61 }
62 
63 //#define DEBUG_PATHFIND
monster_pathfind()64 monster_pathfind::monster_pathfind()
65     : mons(nullptr), start(), target(), pos(), allow_diagonals(true),
66       traverse_unmapped(false), range(0), min_length(0), max_length(0),
67       dist(), prev(), hash(), traversable_cache()
68 {
69 }
70 
~monster_pathfind()71 monster_pathfind::~monster_pathfind()
72 {
73 }
74 
set_range(int r)75 void monster_pathfind::set_range(int r)
76 {
77     if (r >= 0)
78         range = r;
79 }
80 
next_pos(const coord_def & c) const81 coord_def monster_pathfind::next_pos(const coord_def &c) const
82 {
83     return c + Compass[prev[c.x][c.y]];
84 }
85 
86 // The main method in the monster_pathfind class.
87 // Returns true if a path was found, else false.
init_pathfind(const monster * mon,coord_def dest,bool diag,bool msg,bool pass_unmapped)88 bool monster_pathfind::init_pathfind(const monster* mon, coord_def dest,
89                                      bool diag, bool msg, bool pass_unmapped)
90 {
91     mons   = mon;
92 
93     start  = mon->pos();
94     target = dest;
95     pos    = start;
96     allow_diagonals   = diag;
97     traverse_unmapped = pass_unmapped;
98     traverse_in_sight = (!crawl_state.game_is_arena()
99                          && mon->friendly() &&  mon->is_summoned()
100                          && you.see_cell_no_trans(mon->pos()));
101 
102     // Easy enough. :P
103     if (start == target)
104     {
105         if (msg)
106             mpr("The monster is already there!");
107 
108         return true;
109     }
110 
111     return start_pathfind(msg);
112 }
113 
init_pathfind(coord_def src,coord_def dest,bool diag,bool msg)114 bool monster_pathfind::init_pathfind(coord_def src, coord_def dest, bool diag,
115                                      bool msg)
116 {
117     start  = src;
118     target = dest;
119     pos    = start;
120     allow_diagonals = diag;
121 
122     // Easy enough. :P
123     if (start == target)
124         return true;
125 
126     return start_pathfind(msg);
127 }
128 
start_pathfind(bool msg)129 bool monster_pathfind::start_pathfind(bool msg)
130 {
131     // NOTE: We never do any traversable() check for the target square.
132     //       This means that even if the target cannot be reached
133     //       we may still find a path leading adjacent to this position, which
134     //       is desirable if e.g. the player is hovering over deep water
135     //       surrounded by shallow water or floor, or if a foe is hiding in
136     //       a wall.
137 
138     max_length = min_length = grid_distance(pos, target);
139     for (int i = 0; i < GXM; i++)
140         for (int j = 0; j < GYM; j++)
141         {
142             dist[i][j] = INFINITE_DISTANCE;
143             traversable_cache[i][j] = MB_MAYBE;
144         }
145 
146     dist[pos.x][pos.y] = 0;
147 
148     bool success = false;
149     do
150     {
151         // Calculate the distance to all neighbours of the current position,
152         // and add them to the hash, if they haven't already been looked at.
153         success = calc_path_to_neighbours();
154         if (success)
155             return true;
156 
157         // Pull the position with shortest distance estimate to our target grid.
158         success = get_best_position();
159 
160         if (!success)
161         {
162             if (msg)
163             {
164                 mprf("Couldn't find a path from (%d,%d) to (%d,%d).",
165                      target.x, target.y, start.x, start.y);
166             }
167             return false;
168         }
169     }
170     while (true);
171 }
172 
173 // Returns true as soon as we encounter the target.
calc_path_to_neighbours()174 bool monster_pathfind::calc_path_to_neighbours()
175 {
176     coord_def npos;
177     int distance, old_dist, total;
178 
179     // For each point, we look at all neighbour points. Check the orthogonals
180     // last, so that, should an orthogonal and a diagonal direction have the
181     // same total travel cost, the orthogonal will be picked first, and thus
182     // zigzagging will be significantly reduced.
183     //
184     //      1  0  3       This means directions are looked at, in order,
185     //       \ | /        1, 3, 5, 7 (diagonals) followed by 0, 2, 4, 6
186     //      6--.--2       (orthogonals). This is achieved by the assignment
187     //       / | \        of (dir = 0) once dir has passed 7.
188     //      7  4  5
189     //
190     // To avoid bias, we'll choose a random 90 degree rotation
191     int rotate = random2(4) * 2; // equal probability of 0,2,4,6
192     for (int idir = 1; idir < 8; (idir += 2) == 9 && (idir = 0))
193     {
194         // Skip diagonal movement.
195         if (!allow_diagonals && (idir % 2))
196             continue;
197 
198         int dir = (idir + rotate) % 8;  // apply our random 90 degree rotation
199 
200         npos = pos + Compass[dir];
201 
202 #ifdef DEBUG_PATHFIND
203         mprf("Looking at neighbour (%d,%d)", npos.x, npos.y);
204 #endif
205         if (!in_bounds(npos))
206             continue;
207 
208         if (!traversable_memoized(npos) && npos != target)
209             continue;
210 
211         // Ignore this grid if it takes us above the allowed distance
212         // away from the target.
213         if (range && estimated_cost(npos) > range)
214             continue;
215 
216         distance = dist[pos.x][pos.y] + travel_cost(npos);
217         old_dist = dist[npos.x][npos.y];
218 
219         // Also bail out if this would make the path longer than twice the
220         // allowed distance from the target. (This factor may need tuning.)
221         //
222         // This is actually motivated by performance, as pathfinding
223         // in mazes with see-through walls (e.g. plants) can otherwise
224         // soak up a lot of CPU cycles.
225         if (range && distance > range * 2)
226             continue;
227 
228 #ifdef DEBUG_PATHFIND
229         mprf("old dist: %d, new dist: %d, infinite: %d", old_dist, distance,
230              INFINITE_DISTANCE);
231 #endif
232         // If the new distance is better than the old one (initialised with
233         // INFINITE), update the position.
234         if (distance < old_dist)
235         {
236             // Calculate new total path length.
237             total = distance + estimated_cost(npos);
238             if (old_dist == INFINITE_DISTANCE)
239             {
240 #ifdef DEBUG_PATHFIND
241                 mprf("Adding (%d,%d) to hash (total dist = %d)",
242                      npos.x, npos.y, total);
243 #endif
244                 add_new_pos(npos, total);
245                 if (total > max_length)
246                     max_length = total;
247             }
248             else
249             {
250 #ifdef DEBUG_PATHFIND
251                 mprf("Improving (%d,%d) to total dist %d",
252                      npos.x, npos.y, total);
253 #endif
254 
255                 update_pos(npos, total);
256             }
257 
258             // Update distance start->pos.
259             dist[npos.x][npos.y] = distance;
260 
261             // Set backtracking information.
262             // Converts the Compass direction to its counterpart.
263             //      0  1  2         4  5  6
264             //      7  .  3   ==>   3  .  7       e.g. (3 + 4) % 8          = 7
265             //      6  5  4         2  1  0            (7 + 4) % 8 = 11 % 8 = 3
266 
267             prev[npos.x][npos.y] = (dir + 4) % 8;
268 
269             // Are we finished?
270             if (npos == target)
271             {
272 #ifdef DEBUG_PATHFIND
273                 mpr("Arrived at target.");
274 #endif
275                 return true;
276             }
277         }
278     }
279     return false;
280 }
281 
282 // Starting at known min_length (minimum total estimated path distance), check
283 // the hash for existing vectors, then pick the last entry of the first vector
284 // that matches. Update min_length, if necessary.
get_best_position()285 bool monster_pathfind::get_best_position()
286 {
287     for (int i = min_length; i <= max_length; i++)
288     {
289         if (!hash[i].empty())
290         {
291             if (i > min_length)
292                 min_length = i;
293 
294             vector<coord_def> &vec = hash[i];
295             // Pick the last position pushed into the vector as it's most
296             // likely to be close to the target.
297             pos = vec[vec.size()-1];
298             vec.pop_back();
299 
300 #ifdef DEBUG_PATHFIND
301             mprf("Returning (%d, %d) as best pos with total dist %d.",
302                  pos.x, pos.y, min_length);
303 #endif
304 
305             return true;
306         }
307 #ifdef DEBUG_PATHFIND
308         mprf("No positions for path length %d.", i);
309 #endif
310     }
311 
312     // Nothing found? Then there's no path! :(
313     return false;
314 }
315 
316 // Using the prev vector backtrack from start to target to find all steps to
317 // take along the shortest path.
backtrack()318 vector<coord_def> monster_pathfind::backtrack()
319 {
320 #ifdef DEBUG_PATHFIND
321     mpr("Backtracking...");
322 #endif
323     vector<coord_def> path;
324     pos = target;
325     path.push_back(pos);
326 
327     if (pos == start)
328         return path;
329 
330     int dir;
331     do
332     {
333         dir = prev[pos.x][pos.y];
334         pos = pos + Compass[dir];
335         ASSERT_IN_BOUNDS(pos);
336 #ifdef DEBUG_PATHFIND
337         mprf("prev: (%d, %d), pos: (%d, %d)", Compass[dir].x, Compass[dir].y,
338                                               pos.x, pos.y);
339 #endif
340         path.insert(path.begin(), pos);
341 
342         if (pos.origin())
343             break;
344     }
345     while (pos != start);
346     ASSERT(pos == start);
347 
348     return path;
349 }
350 
351 // Reduces the path coordinates to only a couple of key waypoints needed
352 // to reach the target. Waypoints are chosen such that from one waypoint you
353 // can see (and, more importantly, reach) the next one. Note that
354 // can_go_straight() is probably rather too conservative in these estimates.
355 // This is done because Crawl's pathfinding - once a target is in sight and easy
356 // reach - is both very robust and natural, especially if we want to flexibly
357 // avoid plants and other monsters in the way.
calc_waypoints()358 vector<coord_def> monster_pathfind::calc_waypoints()
359 {
360     vector<coord_def> path = backtrack();
361 
362     // If no path found, nothing to be done.
363     if (path.empty())
364         return path;
365 
366     vector<coord_def> waypoints;
367     pos = path[0];
368 
369 #ifdef DEBUG_PATHFIND
370     mpr("\nWaypoints:");
371 #endif
372     for (unsigned int i = 1; i < path.size(); i++)
373     {
374         if (can_go_straight(mons, pos, path[i]) && mons_traversable(path[i]))
375             continue;
376         else
377         {
378             pos = path[i-1];
379             waypoints.push_back(pos);
380 #ifdef DEBUG_PATHFIND
381             mprf("waypoint: (%d, %d)", pos.x, pos.y);
382 #endif
383         }
384     }
385 
386     // Add the actual target to the list of waypoints, so we can later check
387     // whether a tracked enemy has moved too much, in case we have to update
388     // the path.
389     if (pos != path[path.size() - 1])
390         waypoints.push_back(path[path.size() - 1]);
391 
392     return waypoints;
393 }
394 
traversable_memoized(const coord_def & p)395 bool monster_pathfind::traversable_memoized(const coord_def& p)
396 {
397     if (traversable_cache[p.x][p.y] == MB_MAYBE)
398         traversable_cache[p.x][p.y] = frombool(traversable(p));
399     return tobool(traversable_cache[p.x][p.y], false);
400 }
401 
traversable(const coord_def & p)402 bool monster_pathfind::traversable(const coord_def& p)
403 {
404     if (!traverse_unmapped && env.grid(p) == DNGN_UNSEEN)
405         return false;
406 
407     // XXX: Hack to be somewhat consistent with uses of
408     //      opc_immob elsewhere in pathfinding.
409     //      All of this should eventually be replaced by
410     //      giving the monster a proper pathfinding LOS.
411     if (opc_immob(p) == OPC_OPAQUE && !feat_is_closed_door(env.grid(p)))
412     {
413         // XXX: Ugly hack to make thorn hunters use their briars for defensive
414         //      cover instead of just pathing around them.
415         if (mons && mons->type == MONS_THORN_HUNTER
416             && monster_at(p)
417             && monster_at(p)->type == MONS_BRIAR_PATCH)
418         {
419             return true;
420         }
421 
422         return false;
423     }
424 
425     if (mons)
426         return mons_traversable(p);
427 
428     return feat_has_solid_floor(env.grid(p));
429 }
430 
431 // Checks whether a given monster can pass over a certain position, respecting
432 // its preferred habit and capability of flight or opening doors.
mons_traversable(const coord_def & p)433 bool monster_pathfind::mons_traversable(const coord_def& p)
434 {
435     if (cell_is_runed(p))
436         return false;
437     if (!mons->is_habitable(p))
438         return false;
439 
440     return mons_can_traverse(*mons, p, traverse_in_sight);
441 }
442 
travel_cost(coord_def npos)443 int monster_pathfind::travel_cost(coord_def npos)
444 {
445     if (mons)
446         return mons_travel_cost(npos);
447 
448     return 1;
449 }
450 
451 // Assumes that grids that really cannot be entered don't even get here.
452 // (Checked by traversable().)
mons_travel_cost(coord_def npos)453 int monster_pathfind::mons_travel_cost(coord_def npos)
454 {
455     ASSERT(grid_distance(pos, npos) <= 1);
456 
457     // Doors need to be opened.
458     if (feat_is_closed_door(env.grid(npos)))
459         return 2;
460 
461     // Travelling through water, entering or leaving water is more expensive
462     // for non-amphibious monsters, so they'll avoid it where possible.
463     // (The resulting path might not be optimal but it will lead to a path
464     // a monster of such habits is likely to prefer.)
465     if (mons->floundering_at(npos))
466         return 2;
467 
468     // Try to avoid traps.
469     const trap_def* ptrap = trap_at(npos);
470     if (ptrap)
471     {
472         if (ptrap->is_bad_for_player())
473         {
474             // Your allies take extra precautions to avoid traps that are bad
475             // for you (elsewhere further checks are made to mark Zot traps as
476             // impassible).
477             if (mons->friendly())
478                 return 3;
479 
480             // To hostile monsters, these traps are completely harmless.
481             return 1;
482         }
483 
484         return 2;
485     }
486 
487     return 1;
488 }
489 
490 // The estimated cost to reach a grid is simply max(dx, dy).
estimated_cost(coord_def p)491 int monster_pathfind::estimated_cost(coord_def p)
492 {
493     return grid_distance(p, target);
494 }
495 
add_new_pos(coord_def npos,int total)496 void monster_pathfind::add_new_pos(coord_def npos, int total)
497 {
498     hash[total].push_back(npos);
499 }
500 
update_pos(coord_def npos,int total)501 void monster_pathfind::update_pos(coord_def npos, int total)
502 {
503     // Find hash position of old distance and delete it,
504     // then call_add_new_pos.
505     int old_total = dist[npos.x][npos.y] + estimated_cost(npos);
506 
507     vector<coord_def> &vec = hash[old_total];
508     for (unsigned int i = 0; i < vec.size(); i++)
509     {
510         if (vec[i] == npos)
511         {
512             vec.erase(vec.begin() + i);
513             break;
514         }
515     }
516 
517     add_new_pos(npos, total);
518 }
519