Devilution
Diablo devolved - magic behind the 1996 computer game
path.cpp
Go to the documentation of this file.
1 
6 #include "all.h"
7 
8 
28 
30 const char pathxdir[8] = { -1, -1, 1, 1, -1, 0, 1, 0 };
31 const char pathydir[8] = { -1, 1, -1, 1, 0, -1, 0, 1 };
32 
33 /* data */
34 
44 char path_directions[9] = { 5, 1, 6, 2, 0, 3, 8, 4, 7 };
45 
51 int FindPath(BOOL (*PosOk)(int, int, int), int PosOkArg, int sx, int sy, int dx, int dy, char *path)
52 {
53  PATHNODE *path_start, *next_node, *current;
54  int path_length, i;
55 
56  // clear all nodes, create root nodes for the visited/frontier linked lists
57  gdwCurNodes = 0;
60  gdwCurPathStep = 0;
61  path_start = path_new_step();
62  path_start->g = 0;
63  path_start->h = path_get_h_cost(sx, sy, dx, dy);
64  path_start->x = sx;
65  path_start->f = path_start->h + path_start->g;
66  path_start->y = sy;
67  path_2_nodes->NextNode = path_start;
68  // A* search until we find (dx,dy) or fail
69  while ((next_node = GetNextPath())) {
70  // reached the end, success!
71  if (next_node->x == dx && next_node->y == dy) {
72  current = next_node;
73  path_length = 0;
74  while (current->Parent) {
75  if (path_length >= MAX_PATH_LENGTH)
76  break;
77  pnode_vals[path_length++] = path_directions[3 * (current->y - current->Parent->y) - current->Parent->x + 4 + current->x];
78  current = current->Parent;
79  }
80  if (path_length != MAX_PATH_LENGTH) {
81  for (i = 0; i < path_length; i++)
82  path[i] = pnode_vals[path_length - i - 1];
83  return i;
84  }
85  return 0;
86  }
87  // ran out of nodes, abort!
88  if (!path_get_path(PosOk, PosOkArg, next_node, dx, dy))
89  return 0;
90  }
91  // frontier is empty, no path!
92  return 0;
93 }
94 
98 int path_get_h_cost(int sx, int sy, int dx, int dy)
99 {
100  int delta_x = abs(sx - dx);
101  int delta_y = abs(sy - dy);
102 
103  int min = delta_x < delta_y ? delta_x : delta_y;
104  int max = delta_x > delta_y ? delta_x : delta_y;
105 
106  // see path_check_equal for why this is times 2
107  return 2 * (min + max);
108 }
109 
117 int path_check_equal(PATHNODE *pPath, int dx, int dy)
118 {
119  if (pPath->x == dx || pPath->y == dy)
120  return 2;
121 
122  return 3;
123 }
124 
129 {
130  PATHNODE *result;
131 
132  result = path_2_nodes->NextNode;
133  if (result == NULL) {
134  return result;
135  }
136 
137  path_2_nodes->NextNode = result->NextNode;
138  result->NextNode = pnode_ptr->NextNode;
139  pnode_ptr->NextNode = result;
140  return result;
141 }
142 
153 BOOL path_solid_pieces(PATHNODE *pPath, int dx, int dy)
154 {
155  BOOL rv = TRUE;
156  switch (path_directions[3 * (dy - pPath->y) + 3 - pPath->x + 1 + dx]) {
157  case 5:
158  rv = !nSolidTable[dPiece[dx][dy + 1]] && !nSolidTable[dPiece[dx + 1][dy]];
159  break;
160  case 6:
161  rv = !nSolidTable[dPiece[dx][dy + 1]] && !nSolidTable[dPiece[dx - 1][dy]];
162  break;
163  case 7:
164  rv = !nSolidTable[dPiece[dx][dy - 1]] && !nSolidTable[dPiece[dx - 1][dy]];
165  break;
166  case 8:
167  rv = !nSolidTable[dPiece[dx + 1][dy]] && !nSolidTable[dPiece[dx][dy - 1]];
168  break;
169  }
170  return rv;
171 }
172 
178 BOOL path_get_path(BOOL (*PosOk)(int, int, int), int PosOkArg, PATHNODE *pPath, int x, int y)
179 {
180  int dx, dy;
181  int i;
182  BOOL ok;
183 
184  for (i = 0; i < 8; i++) {
185  dx = pPath->x + pathxdir[i];
186  dy = pPath->y + pathydir[i];
187  ok = PosOk(PosOkArg, dx, dy);
188  if (ok && path_solid_pieces(pPath, dx, dy) || !ok && dx == x && dy == y) {
189  if (!path_parent_path(pPath, dx, dy, x, y))
190  return FALSE;
191  }
192  }
193 
194  return TRUE;
195 }
196 
202 BOOL path_parent_path(PATHNODE *pPath, int dx, int dy, int sx, int sy)
203 {
204  int next_g;
205  PATHNODE *dxdy;
206  int i;
207 
208  next_g = pPath->g + path_check_equal(pPath, dx, dy);
209 
210  // 3 cases to consider
211  // case 1: (dx,dy) is already on the frontier
212  dxdy = path_get_node1(dx, dy);
213  if (dxdy != NULL) {
214  for (i = 0; i < 8; i++) {
215  if (pPath->Child[i] == NULL)
216  break;
217  }
218  pPath->Child[i] = dxdy;
219  if (next_g < dxdy->g) {
220  if (path_solid_pieces(pPath, dx, dy)) {
221  // we'll explore it later, just update
222  dxdy->Parent = pPath;
223  dxdy->g = next_g;
224  dxdy->f = next_g + dxdy->h;
225  }
226  }
227  } else {
228  // case 2: (dx,dy) was already visited
229  dxdy = path_get_node2(dx, dy);
230  if (dxdy != NULL) {
231  for (i = 0; i < 8; i++) {
232  if (pPath->Child[i] == NULL)
233  break;
234  }
235  pPath->Child[i] = dxdy;
236  if (next_g < dxdy->g && path_solid_pieces(pPath, dx, dy)) {
237  // update the node
238  dxdy->Parent = pPath;
239  dxdy->g = next_g;
240  dxdy->f = next_g + dxdy->h;
241  // already explored, so re-update others starting from that node
242  path_set_coords(dxdy);
243  }
244  } else {
245  // case 3: (dx,dy) is totally new
246  dxdy = path_new_step();
247  if (dxdy == NULL)
248  return FALSE;
249  dxdy->Parent = pPath;
250  dxdy->g = next_g;
251  dxdy->h = path_get_h_cost(dx, dy, sx, sy);
252  dxdy->f = next_g + dxdy->h;
253  dxdy->x = dx;
254  dxdy->y = dy;
255  // add it to the frontier
256  path_next_node(dxdy);
257 
258  for (i = 0; i < 8; i++) {
259  if (pPath->Child[i] == NULL)
260  break;
261  }
262  pPath->Child[i] = dxdy;
263  }
264  }
265  return TRUE;
266 }
267 
271 PATHNODE *path_get_node1(int dx, int dy)
272 {
273  PATHNODE *result = path_2_nodes->NextNode;
274  while (result != NULL && (result->x != dx || result->y != dy))
275  result = result->NextNode;
276  return result;
277 }
278 
282 PATHNODE *path_get_node2(int dx, int dy)
283 {
284  PATHNODE *result = pnode_ptr->NextNode;
285  while (result != NULL && (result->x != dx || result->y != dy))
286  result = result->NextNode;
287  return result;
288 }
289 
294 {
295  PATHNODE *next, *current;
296  int f;
297 
298  next = path_2_nodes;
299  if (!path_2_nodes->NextNode) {
300  path_2_nodes->NextNode = pPath;
301  } else {
302  current = path_2_nodes;
303  next = path_2_nodes->NextNode;
304  f = pPath->f;
305  while (next && next->f < f) {
306  current = next;
307  next = next->NextNode;
308  }
309  pPath->NextNode = next;
310  current->NextNode = pPath;
311  }
312 }
313 
318 {
319  PATHNODE *PathOld;
320  PATHNODE *PathAct;
321  int i;
322 
323  path_push_active_step(pPath);
324  while (gdwCurPathStep) {
325  PathOld = path_pop_active_step();
326  for (i = 0; i < 8; i++) {
327  PathAct = PathOld->Child[i];
328  if (PathAct == NULL)
329  break;
330 
331  if (PathOld->g + path_check_equal(PathOld, PathAct->x, PathAct->y) < PathAct->g) {
332  if (path_solid_pieces(PathOld, PathAct->x, PathAct->y)) {
333  PathAct->Parent = PathOld;
334  PathAct->g = PathOld->g + path_check_equal(PathOld, PathAct->x, PathAct->y);
335  PathAct->f = PathAct->g + PathAct->h;
336  path_push_active_step(PathAct);
337  }
338  }
339  }
340  }
341 }
342 
347 {
348  int stack_index = gdwCurPathStep;
349  gdwCurPathStep++;
350  pnode_tblptr[stack_index] = pPath;
351 }
352 
357 {
358  gdwCurPathStep--;
360 }
361 
366 {
367  PATHNODE *new_node;
368 
369  if (gdwCurNodes == MAXPATHNODES)
370  return NULL;
371 
372  new_node = &path_nodes[gdwCurNodes];
373  gdwCurNodes++;
374  memset(new_node, 0, sizeof(PATHNODE));
375  return new_node;
376 }
377 
MAXPATHNODES
#define MAXPATHNODES
Definition: defs.h:72
path_push_active_step
void path_push_active_step(PATHNODE *pPath)
push pPath onto the pnode_tblptr stack
Definition: path.cpp:346
PATHNODE::y
int y
Definition: structs.h:1427
path_nodes
DEVILUTION_BEGIN_NAMESPACE PATHNODE path_nodes[MAXPATHNODES]
Notes visisted by the path finding algorithm.
Definition: path.cpp:11
MAX_PATH_LENGTH
#define MAX_PATH_LENGTH
Definition: defs.h:74
PATHNODE::Child
struct PATHNODE * Child[8]
Definition: structs.h:1429
all.h
PATHNODE
Definition: structs.h:1422
pathxdir
const char pathxdir[8]
For iterating over the 8 possible movement directions.
Definition: path.cpp:30
PATHNODE::h
char h
Definition: structs.h:1424
pathydir
const char pathydir[8]
Definition: path.cpp:31
gdwCurNodes
int gdwCurNodes
the number of in-use nodes in path_nodes
Definition: path.cpp:15
path_pop_active_step
PATHNODE * path_pop_active_step()
pop and return a node from the pnode_tblptr stack
Definition: path.cpp:356
path_get_h_cost
int path_get_h_cost(int sx, int sy, int dx, int dy)
heuristic, estimated cost from (sx,sy) to (dx,dy)
Definition: path.cpp:98
dPiece
int dPiece[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:26
nSolidTable
BOOLEAN nSolidTable[2049]
List of path blocking dPieces.
Definition: gendung.cpp:45
path_directions
char path_directions[9]
each step direction is assigned a number like this: dx -1 0 1 +--— -1|5 1 6 dy 0|2 0 3 1|8 4 7
Definition: path.cpp:44
path_parent_path
BOOL path_parent_path(PATHNODE *pPath, int dx, int dy, int sx, int sy)
add a step from pPath to (dx,dy), return 1 if successful, and update the frontier/visited nodes accor...
Definition: path.cpp:202
PATHNODE::f
char f
Definition: structs.h:1423
DEVILUTION_END_NAMESPACE
#define DEVILUTION_END_NAMESPACE
Definition: types.h:10
path_get_path
BOOL path_get_path(BOOL(*PosOk)(int, int, int), int PosOkArg, PATHNODE *pPath, int x, int y)
perform a single step of A* bread-first search by trying to step in every possible direction from pPa...
Definition: path.cpp:178
PATHNODE::NextNode
struct PATHNODE * NextNode
Definition: structs.h:1430
path_get_node1
PATHNODE * path_get_node1(int dx, int dy)
return a node for (dx,dy) on the frontier, or NULL if not found
Definition: path.cpp:271
path_solid_pieces
BOOL path_solid_pieces(PATHNODE *pPath, int dx, int dy)
check if stepping from pPath to (dx,dy) cuts a corner.
Definition: path.cpp:153
PATHNODE::Parent
struct PATHNODE * Parent
Definition: structs.h:1428
gdwCurPathStep
int gdwCurPathStep
size of the pnode_tblptr stack
Definition: path.cpp:13
path_get_node2
PATHNODE * path_get_node2(int dx, int dy)
return a node for (dx,dy) if it was visited, or NULL if not found
Definition: path.cpp:282
FindPath
int FindPath(BOOL(*PosOk)(int, int, int), int PosOkArg, int sx, int sy, int dx, int dy, char *path)
find the shortest path from (sx,sy) to (dx,dy), using PosOk(PosOkArg,x,y) to check that each step is ...
Definition: path.cpp:51
path_check_equal
int path_check_equal(PATHNODE *pPath, int dx, int dy)
return 2 if pPath is horizontally/vertically aligned with (dx,dy), else 3
Definition: path.cpp:117
pnode_tblptr
PATHNODE * pnode_tblptr[MAXPATHNODES]
A stack for recursively searching nodes.
Definition: path.cpp:24
pnode_vals
int pnode_vals[MAX_PATH_LENGTH]
for reconstructing the path after the A* search is done.
Definition: path.cpp:20
path_new_step
PATHNODE * path_new_step()
zero one of the preallocated nodes and return a pointer to it, or NULL if none are available
Definition: path.cpp:365
DEVILUTION_BEGIN_NAMESPACE
Definition: sha.cpp:10
path_unusednodes
PATHNODE path_unusednodes[MAXPATHNODES]
Definition: path.cpp:27
path_set_coords
void path_set_coords(PATHNODE *pPath)
update all path costs using depth-first search starting at pPath
Definition: path.cpp:317
path_2_nodes
PATHNODE * path_2_nodes
A linked list of the A* frontier, sorted by distance.
Definition: path.cpp:26
PATHNODE::g
char g
Definition: structs.h:1425
path_next_node
void path_next_node(PATHNODE *pPath)
insert pPath into the frontier (keeping the frontier sorted by total distance)
Definition: path.cpp:293
PATHNODE::x
int x
Definition: structs.h:1426
pnode_ptr
PATHNODE * pnode_ptr
A linked list of all visited nodes.
Definition: path.cpp:22
GetNextPath
PATHNODE * GetNextPath()
get the next node on the A* frontier to explore (estimated to be closest to the goal),...
Definition: path.cpp:128