Annotation of brogue-ce/src/brogue/Dijkstra.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * Dijkstra.c
! 3: * Brogue
! 4: *
! 5: * Copyright 2012. All rights reserved.
! 6: *
! 7: * This file is part of Brogue.
! 8: *
! 9: * This program is free software: you can redistribute it and/or modify
! 10: * it under the terms of the GNU Affero General Public License as
! 11: * published by the Free Software Foundation, either version 3 of the
! 12: * License, or (at your option) any later version.
! 13: *
! 14: * This program is distributed in the hope that it will be useful,
! 15: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 17: * GNU Affero General Public License for more details.
! 18: *
! 19: * You should have received a copy of the GNU Affero General Public License
! 20: * along with this program. If not, see <http://www.gnu.org/licenses/>.
! 21: */
! 22:
! 23: // This code was created by Joshua Day, to replace my clunkier and slower Dijkstra scanning algorithm in Movement.c.
! 24: // Thanks very much, Joshua.
! 25:
! 26: #include "Rogue.h"
! 27: #include "IncludeGlobals.h"
! 28:
! 29: struct pdsLink {
! 30: short distance;
! 31: short cost;
! 32: pdsLink *left, *right;
! 33: };
! 34:
! 35: struct pdsMap {
! 36: boolean eightWays;
! 37:
! 38: pdsLink front;
! 39: pdsLink links[DCOLS * DROWS];
! 40: };
! 41:
! 42: void pdsUpdate(pdsMap *map) {
! 43: short dir, dirs;
! 44: pdsLink *left = NULL, *right = NULL, *link = NULL;
! 45:
! 46: dirs = map->eightWays ? 8 : 4;
! 47:
! 48: pdsLink *head = map->front.right;
! 49: map->front.right = NULL;
! 50:
! 51: while (head != NULL) {
! 52: for (dir = 0; dir < dirs; dir++) {
! 53: link = head + (nbDirs[dir][0] + DCOLS * nbDirs[dir][1]);
! 54: if (link < map->links || link >= map->links + DCOLS * DROWS) continue;
! 55:
! 56: // verify passability
! 57: if (link->cost < 0) continue;
! 58: if (dir >= 4) {
! 59: pdsLink *way1, *way2;
! 60: way1 = head + nbDirs[dir][0];
! 61: way2 = head + DCOLS * nbDirs[dir][1];
! 62: if (way1->cost == PDS_OBSTRUCTION || way2->cost == PDS_OBSTRUCTION) continue;
! 63: }
! 64:
! 65: if (head->distance + link->cost < link->distance) {
! 66: link->distance = head->distance + link->cost;
! 67:
! 68: // reinsert the touched cell; it'll be close to the beginning of the list now, so
! 69: // this will be very fast. start by removing it.
! 70:
! 71: if (link->right != NULL) link->right->left = link->left;
! 72: if (link->left != NULL) link->left->right = link->right;
! 73:
! 74: left = head;
! 75: right = head->right;
! 76: while (right != NULL && right->distance < link->distance) {
! 77: left = right;
! 78: right = right->right;
! 79: }
! 80: if (left != NULL) left->right = link;
! 81: link->right = right;
! 82: link->left = left;
! 83: if (right != NULL) right->left = link;
! 84: }
! 85: }
! 86:
! 87: right = head->right;
! 88:
! 89: head->left = NULL;
! 90: head->right = NULL;
! 91:
! 92: head = right;
! 93: }
! 94: }
! 95:
! 96: void pdsClear(pdsMap *map, short maxDistance, boolean eightWays) {
! 97: short i;
! 98:
! 99: map->eightWays = eightWays;
! 100:
! 101: map->front.right = NULL;
! 102:
! 103: for (i=0; i < DCOLS*DROWS; i++) {
! 104: map->links[i].distance = maxDistance;
! 105: map->links[i].left = map->links[i].right = NULL;
! 106: }
! 107: }
! 108:
! 109: short pdsGetDistance(pdsMap *map, short x, short y) {
! 110: pdsUpdate(map);
! 111: return PDS_CELL(map, x, y)->distance;
! 112: }
! 113:
! 114: void pdsSetDistance(pdsMap *map, short x, short y, short distance) {
! 115: pdsLink *left, *right, *link;
! 116:
! 117: if (x > 0 && y > 0 && x < DCOLS - 1 && y < DROWS - 1) {
! 118: link = PDS_CELL(map, x, y);
! 119: if (link->distance > distance) {
! 120: link->distance = distance;
! 121:
! 122: if (link->right != NULL) link->right->left = link->left;
! 123: if (link->left != NULL) link->left->right = link->right;
! 124:
! 125: left = &map->front;
! 126: right = map->front.right;
! 127:
! 128: while (right != NULL && right->distance < link->distance) {
! 129: left = right;
! 130: right = right->right;
! 131: }
! 132:
! 133: link->right = right;
! 134: link->left = left;
! 135: left->right = link;
! 136: if (right != NULL) right->left = link;
! 137: }
! 138: }
! 139: }
! 140:
! 141: void pdsSetCosts(pdsMap *map, short **costMap) {
! 142: short i, j;
! 143:
! 144: for (i=0; i<DCOLS; i++) {
! 145: for (j=0; j<DROWS; j++) {
! 146: if (i != 0 && j != 0 && i < DCOLS - 1 && j < DROWS - 1) {
! 147: PDS_CELL(map, i, j)->cost = costMap[i][j];
! 148: } else {
! 149: PDS_CELL(map, i, j)->cost = PDS_FORBIDDEN;
! 150: }
! 151: }
! 152: }
! 153: }
! 154:
! 155: void pdsBatchInput(pdsMap *map, short **distanceMap, short **costMap, short maxDistance, boolean eightWays) {
! 156: short i, j;
! 157: pdsLink *left, *right;
! 158:
! 159: map->eightWays = eightWays;
! 160:
! 161: left = NULL;
! 162: right = NULL;
! 163:
! 164: map->front.right = NULL;
! 165: for (i=0; i<DCOLS; i++) {
! 166: for (j=0; j<DROWS; j++) {
! 167: pdsLink *link = PDS_CELL(map, i, j);
! 168:
! 169: if (distanceMap != NULL) {
! 170: link->distance = distanceMap[i][j];
! 171: } else {
! 172: if (costMap != NULL) {
! 173: // totally hackish; refactor
! 174: link->distance = maxDistance;
! 175: }
! 176: }
! 177:
! 178: int cost;
! 179:
! 180: if (i == 0 || j == 0 || i == DCOLS - 1 || j == DROWS - 1) {
! 181: cost = PDS_OBSTRUCTION;
! 182: } else if (costMap == NULL) {
! 183: if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY) && cellHasTerrainFlag(i, j, T_OBSTRUCTS_DIAGONAL_MOVEMENT)) cost = PDS_OBSTRUCTION;
! 184: else cost = PDS_FORBIDDEN;
! 185: } else {
! 186: cost = costMap[i][j];
! 187: }
! 188:
! 189: link->cost = cost;
! 190:
! 191: if (cost > 0) {
! 192: if (link->distance < maxDistance) {
! 193: if (right == NULL || right->distance > link->distance) {
! 194: // left and right are used to traverse the list; if many cells have similar values,
! 195: // some time can be saved by not clearing them with each insertion. this time,
! 196: // sadly, we have to start from the front.
! 197:
! 198: left = &map->front;
! 199: right = map->front.right;
! 200: }
! 201:
! 202: while (right != NULL && right->distance < link->distance) {
! 203: left = right;
! 204: right = right->right;
! 205: }
! 206:
! 207: link->right = right;
! 208: link->left = left;
! 209: left->right = link;
! 210: if (right != NULL) right->left = link;
! 211:
! 212: left = link;
! 213: } else {
! 214: link->right = NULL;
! 215: link->left = NULL;
! 216: }
! 217: } else {
! 218: link->right = NULL;
! 219: link->left = NULL;
! 220: }
! 221: }
! 222: }
! 223: }
! 224:
! 225: void pdsBatchOutput(pdsMap *map, short **distanceMap) {
! 226: short i, j;
! 227:
! 228: pdsUpdate(map);
! 229: // transfer results to the distanceMap
! 230: for (i=0; i<DCOLS; i++) {
! 231: for (j=0; j<DROWS; j++) {
! 232: distanceMap[i][j] = PDS_CELL(map, i, j)->distance;
! 233: }
! 234: }
! 235: }
! 236:
! 237: void pdsInvalidate(pdsMap *map, short maxDistance) {
! 238: pdsBatchInput(map, NULL, NULL, maxDistance, map->eightWays);
! 239: }
! 240:
! 241: void dijkstraScan(short **distanceMap, short **costMap, boolean useDiagonals) {
! 242: static pdsMap map;
! 243:
! 244: pdsBatchInput(&map, distanceMap, costMap, 30000, useDiagonals);
! 245: pdsBatchOutput(&map, distanceMap);
! 246: }
! 247:
! 248: void calculateDistances(short **distanceMap,
! 249: short destinationX, short destinationY,
! 250: unsigned long blockingTerrainFlags,
! 251: creature *traveler,
! 252: boolean canUseSecretDoors,
! 253: boolean eightWays) {
! 254: creature *monst;
! 255: static pdsMap map;
! 256:
! 257: short i, j;
! 258:
! 259: for (i=0; i<DCOLS; i++) {
! 260: for (j=0; j<DROWS; j++) {
! 261: char cost;
! 262: monst = monsterAtLoc(i, j);
! 263: if (monst
! 264: && (monst->info.flags & (MONST_IMMUNE_TO_WEAPONS | MONST_INVULNERABLE))
! 265: && (monst->info.flags & (MONST_IMMOBILE | MONST_GETS_TURN_ON_ACTIVATION))) {
! 266:
! 267: // Always avoid damage-immune stationary monsters.
! 268: cost = PDS_FORBIDDEN;
! 269: } else if (canUseSecretDoors
! 270: && cellHasTMFlag(i, j, TM_IS_SECRET)
! 271: && cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)
! 272: && !(discoveredTerrainFlagsAtLoc(i, j) & T_OBSTRUCTS_PASSABILITY)) {
! 273:
! 274: cost = 1;
! 275: } else if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)
! 276: || (traveler && traveler == &player && !(pmap[i][j].flags & (DISCOVERED | MAGIC_MAPPED)))) {
! 277:
! 278: cost = cellHasTerrainFlag(i, j, T_OBSTRUCTS_DIAGONAL_MOVEMENT) ? PDS_OBSTRUCTION : PDS_FORBIDDEN;
! 279: } else if ((traveler && monsterAvoids(traveler, i, j)) || cellHasTerrainFlag(i, j, blockingTerrainFlags)) {
! 280: cost = PDS_FORBIDDEN;
! 281: } else {
! 282: cost = 1;
! 283: }
! 284:
! 285: PDS_CELL(&map, i, j)->cost = cost;
! 286: }
! 287: }
! 288:
! 289: pdsClear(&map, 30000, eightWays);
! 290: pdsSetDistance(&map, destinationX, destinationY, 0);
! 291: pdsBatchOutput(&map, distanceMap);
! 292: }
! 293:
! 294: short pathingDistance(short x1, short y1, short x2, short y2, unsigned long blockingTerrainFlags) {
! 295: short retval, **distanceMap = allocGrid();
! 296: calculateDistances(distanceMap, x2, y2, blockingTerrainFlags, NULL, true, true);
! 297: retval = distanceMap[x1][y1];
! 298: freeGrid(distanceMap);
! 299: return retval;
! 300: }
! 301:
CVSweb