Annotation of brogue-ce/src/brogue/Grid.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * Grid
! 3: * Brogue
! 4: *
! 5: * Created by Brian Walker on 12/7/12.
! 6: * Copyright 2012. All rights reserved.
! 7: *
! 8: * This file is part of Brogue.
! 9: *
! 10: * This program is free software: you can redistribute it and/or modify
! 11: * it under the terms of the GNU Affero General Public License as
! 12: * published by the Free Software Foundation, either version 3 of the
! 13: * License, or (at your option) any later version.
! 14: *
! 15: * This program is distributed in the hope that it will be useful,
! 16: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 17: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 18: * GNU Affero General Public License for more details.
! 19: *
! 20: * You should have received a copy of the GNU Affero General Public License
! 21: * along with this program. If not, see <http://www.gnu.org/licenses/>.
! 22: */
! 23:
! 24: #include "Rogue.h"
! 25: #include "IncludeGlobals.h"
! 26:
! 27:
! 28: // mallocing two-dimensional arrays! dun dun DUN!
! 29: short **allocGrid() {
! 30: short i;
! 31: short **array = malloc(DCOLS * sizeof(short *));
! 32:
! 33: array[0] = malloc(DROWS * DCOLS * sizeof(short));
! 34: for(i = 1; i < DCOLS; i++) {
! 35: array[i] = array[0] + i * DROWS;
! 36: }
! 37: return array;
! 38: }
! 39:
! 40: void freeGrid(short **array) {
! 41: free(array[0]);
! 42: free(array);
! 43: }
! 44:
! 45: void copyGrid(short **to, short **from) {
! 46: short i, j;
! 47:
! 48: for(i = 0; i < DCOLS; i++) {
! 49: for(j = 0; j < DROWS; j++) {
! 50: to[i][j] = from[i][j];
! 51: }
! 52: }
! 53: }
! 54:
! 55: void fillGrid(short **grid, short fillValue) {
! 56: short i, j;
! 57:
! 58: for(i = 0; i < DCOLS; i++) {
! 59: for(j = 0; j < DROWS; j++) {
! 60: grid[i][j] = fillValue;
! 61: }
! 62: }
! 63: }
! 64:
! 65: // Highlight the portion indicated by hiliteCharGrid with the hiliteColor at the hiliteStrength -- both latter arguments are optional.
! 66: void hiliteGrid(short **grid, color *hiliteColor, short hiliteStrength) {
! 67: short i, j, x, y;
! 68: color hCol;
! 69:
! 70: assureCosmeticRNG;
! 71:
! 72: if (hiliteColor) {
! 73: hCol = *hiliteColor;
! 74: } else {
! 75: hCol = yellow;
! 76: }
! 77:
! 78: bakeColor(&hCol);
! 79:
! 80: if (!hiliteStrength) {
! 81: hiliteStrength = 75;
! 82: }
! 83:
! 84: for (i=0; i<DCOLS; i++) {
! 85: for (j=0; j<DROWS; j++) {
! 86: if (grid[i][j]) {
! 87: x = mapToWindowX(i);
! 88: y = mapToWindowY(j);
! 89:
! 90: displayBuffer[x][y].needsUpdate = true;
! 91: displayBuffer[x][y].backColorComponents[0] = clamp(displayBuffer[x][y].backColorComponents[0] + hCol.red * hiliteStrength / 100, 0, 100);
! 92: displayBuffer[x][y].backColorComponents[1] = clamp(displayBuffer[x][y].backColorComponents[1] + hCol.green * hiliteStrength / 100, 0, 100);
! 93: displayBuffer[x][y].backColorComponents[2] = clamp(displayBuffer[x][y].backColorComponents[2] + hCol.blue * hiliteStrength / 100, 0, 100);
! 94: displayBuffer[x][y].foreColorComponents[0] = clamp(displayBuffer[x][y].foreColorComponents[0] + hCol.red * hiliteStrength / 100, 0, 100);
! 95: displayBuffer[x][y].foreColorComponents[1] = clamp(displayBuffer[x][y].foreColorComponents[1] + hCol.green * hiliteStrength / 100, 0, 100);
! 96: displayBuffer[x][y].foreColorComponents[2] = clamp(displayBuffer[x][y].foreColorComponents[2] + hCol.blue * hiliteStrength / 100, 0, 100);
! 97: }
! 98: }
! 99: }
! 100: restoreRNG;
! 101: }
! 102:
! 103: void findReplaceGrid(short **grid, short findValueMin, short findValueMax, short fillValue) {
! 104: short i, j;
! 105:
! 106: for(i = 0; i < DCOLS; i++) {
! 107: for(j = 0; j < DROWS; j++) {
! 108: if (grid[i][j] >= findValueMin && grid[i][j] <= findValueMax) {
! 109: grid[i][j] = fillValue;
! 110: }
! 111: }
! 112: }
! 113: }
! 114:
! 115: // Flood-fills the grid from (x, y) along cells that are within the eligible range.
! 116: // Returns the total count of filled cells.
! 117: short floodFillGrid(short **grid, short x, short y, short eligibleValueMin, short eligibleValueMax, short fillValue) {
! 118: enum directions dir;
! 119: short newX, newY, fillCount = 1;
! 120:
! 121: brogueAssert(fillValue < eligibleValueMin || fillValue > eligibleValueMax);
! 122:
! 123: grid[x][y] = fillValue;
! 124: for (dir = 0; dir < 4; dir++) {
! 125: newX = x + nbDirs[dir][0];
! 126: newY = y + nbDirs[dir][1];
! 127: if (coordinatesAreInMap(newX, newY)
! 128: && grid[newX][newY] >= eligibleValueMin
! 129: && grid[newX][newY] <= eligibleValueMax) {
! 130: fillCount += floodFillGrid(grid, newX, newY, eligibleValueMin, eligibleValueMax, fillValue);
! 131: }
! 132: }
! 133: return fillCount;
! 134: }
! 135:
! 136: void drawRectangleOnGrid(short **grid, short x, short y, short width, short height, short value) {
! 137: short i, j;
! 138:
! 139: for (i=x; i < x+width; i++) {
! 140: for (j=y; j<y+height; j++) {
! 141: grid[i][j] = value;
! 142: }
! 143: }
! 144: }
! 145:
! 146: void drawCircleOnGrid(short **grid, short x, short y, short radius, short value) {
! 147: short i, j;
! 148:
! 149: for (i=max(0, x - radius - 1); i < max(DCOLS, x + radius); i++) {
! 150: for (j=max(0, y - radius - 1); j < max(DROWS, y + radius); j++) {
! 151: if ((i-x)*(i-x) + (j-y)*(j-y) < radius * radius + radius) {
! 152: grid[i][j] = value;
! 153: }
! 154: }
! 155: }
! 156: }
! 157:
! 158: void intersectGrids(short **onto, short **from) {
! 159: short i, j;
! 160: for(i = 0; i < DCOLS; i++) {
! 161: for(j = 0; j < DROWS; j++) {
! 162: if (onto[i][j] && from[i][j]) {
! 163: onto[i][j] = true;
! 164: } else {
! 165: onto[i][j] = false;
! 166: }
! 167: }
! 168: }
! 169: }
! 170:
! 171: void uniteGrids(short **onto, short **from) {
! 172: short i, j;
! 173: for(i = 0; i < DCOLS; i++) {
! 174: for(j = 0; j < DROWS; j++) {
! 175: if (!onto[i][j] && from[i][j]) {
! 176: onto[i][j] = from[i][j];
! 177: }
! 178: }
! 179: }
! 180: }
! 181:
! 182: void invertGrid(short **grid) {
! 183: short i, j;
! 184: for(i = 0; i < DCOLS; i++) {
! 185: for(j = 0; j < DROWS; j++) {
! 186: grid[i][j] = !grid[i][j];
! 187: }
! 188: }
! 189: }
! 190:
! 191: // Fills grid locations with the given value if they match any terrain flags or map flags.
! 192: // Otherwise does not change the grid location.
! 193: void getTerrainGrid(short **grid, short value, unsigned long terrainFlags, unsigned long mapFlags) {
! 194: short i, j;
! 195: for(i = 0; i < DCOLS; i++) {
! 196: for(j = 0; j < DROWS; j++) {
! 197: if (grid[i][j] != value && cellHasTerrainFlag(i, j, terrainFlags) || (pmap[i][j].flags & mapFlags)) {
! 198: grid[i][j] = value;
! 199: }
! 200: }
! 201: }
! 202: }
! 203:
! 204: void getTMGrid(short **grid, short value, unsigned long TMflags) {
! 205: short i, j;
! 206: for(i = 0; i < DCOLS; i++) {
! 207: for(j = 0; j < DROWS; j++) {
! 208: if (grid[i][j] != value && cellHasTMFlag(i, j, TMflags)) {
! 209: grid[i][j] = value;
! 210: }
! 211: }
! 212: }
! 213: }
! 214:
! 215: void getPassableArcGrid(short **grid, short minPassableArc, short maxPassableArc, short value) {
! 216: short i, j, count;
! 217: for(i = 0; i < DCOLS; i++) {
! 218: for(j = 0; j < DROWS; j++) {
! 219: if (grid[i][j] != value) {
! 220: count = passableArcCount(i, j);
! 221: if (count >= minPassableArc && count <= maxPassableArc) {
! 222: grid[i][j] = value;
! 223: }
! 224: }
! 225: }
! 226: }
! 227: }
! 228:
! 229: short validLocationCount(short **grid, short validValue) {
! 230: short i, j, count;
! 231: count = 0;
! 232: for(i = 0; i < DCOLS; i++) {
! 233: for(j = 0; j < DROWS; j++) {
! 234: if (grid[i][j] == validValue) {
! 235: count++;
! 236: }
! 237: }
! 238: }
! 239: return count;
! 240: }
! 241:
! 242: short leastPositiveValueInGrid(short **grid) {
! 243: short i, j, leastPositiveValue = 0;
! 244: for(i = 0; i < DCOLS; i++) {
! 245: for(j = 0; j < DROWS; j++) {
! 246: if (grid[i][j] > 0 && (leastPositiveValue == 0 || grid[i][j] < leastPositiveValue)) {
! 247: leastPositiveValue = grid[i][j];
! 248: }
! 249: }
! 250: }
! 251: return leastPositiveValue;
! 252: }
! 253:
! 254: // Takes a grid as a mask of valid locations, chooses one randomly and returns it as (x, y).
! 255: // If there are no valid locations, returns (-1, -1).
! 256: void randomLocationInGrid(short **grid, short *x, short *y, short validValue) {
! 257: const short locationCount = validLocationCount(grid, validValue);
! 258: short i, j;
! 259:
! 260: if (locationCount <= 0) {
! 261: *x = *y = -1;
! 262: return;
! 263: }
! 264: short index = rand_range(0, locationCount - 1);
! 265: for(i = 0; i < DCOLS && index >= 0; i++) {
! 266: for(j = 0; j < DROWS && index >= 0; j++) {
! 267: if (grid[i][j] == validValue) {
! 268: if (index == 0) {
! 269: *x = i;
! 270: *y = j;
! 271: }
! 272: index--;
! 273: }
! 274: }
! 275: }
! 276: return;
! 277: }
! 278:
! 279: // Finds the lowest positive number in a grid, chooses one location with that number randomly and returns it as (x, y).
! 280: // If there are no valid locations, returns (-1, -1).
! 281: void randomLeastPositiveLocationInGrid(short **grid, short *x, short *y, boolean deterministic) {
! 282: const short targetValue = leastPositiveValueInGrid(grid);
! 283: short locationCount;
! 284: short i, j, index;
! 285:
! 286: if (targetValue == 0) {
! 287: *x = *y = -1;
! 288: return;
! 289: }
! 290:
! 291: locationCount = 0;
! 292: for(i = 0; i < DCOLS; i++) {
! 293: for(j = 0; j < DROWS; j++) {
! 294: if (grid[i][j] == targetValue) {
! 295: locationCount++;
! 296: }
! 297: }
! 298: }
! 299:
! 300: if (deterministic) {
! 301: index = locationCount / 2;
! 302: } else {
! 303: index = rand_range(0, locationCount - 1);
! 304: }
! 305:
! 306: for(i = 0; i < DCOLS && index >= 0; i++) {
! 307: for(j = 0; j < DROWS && index >= 0; j++) {
! 308: if (grid[i][j] == targetValue) {
! 309: if (index == 0) {
! 310: *x = i;
! 311: *y = j;
! 312: }
! 313: index--;
! 314: }
! 315: }
! 316: }
! 317: return;
! 318: }
! 319:
! 320: boolean getQualifyingPathLocNear(short *retValX, short *retValY,
! 321: short x, short y,
! 322: boolean hallwaysAllowed,
! 323: unsigned long blockingTerrainFlags,
! 324: unsigned long blockingMapFlags,
! 325: unsigned long forbiddenTerrainFlags,
! 326: unsigned long forbiddenMapFlags,
! 327: boolean deterministic) {
! 328: short **grid, **costMap;
! 329: short loc[2];
! 330:
! 331: // First check the given location to see if it works, as an optimization.
! 332: if (!cellHasTerrainFlag(x, y, blockingTerrainFlags | forbiddenTerrainFlags)
! 333: && !(pmap[x][y].flags & (blockingMapFlags | forbiddenMapFlags))
! 334: && (hallwaysAllowed || passableArcCount(x, y) <= 1)) {
! 335:
! 336: *retValX = x;
! 337: *retValY = y;
! 338: return true;
! 339: }
! 340:
! 341: // Allocate the grids.
! 342: grid = allocGrid();
! 343: costMap = allocGrid();
! 344:
! 345: // Start with a base of a high number everywhere.
! 346: fillGrid(grid, 30000);
! 347: fillGrid(costMap, 1);
! 348:
! 349: // Block off the pathing blockers.
! 350: getTerrainGrid(costMap, PDS_FORBIDDEN, blockingTerrainFlags, blockingMapFlags);
! 351: if (blockingTerrainFlags & (T_OBSTRUCTS_DIAGONAL_MOVEMENT | T_OBSTRUCTS_PASSABILITY)) {
! 352: getTerrainGrid(costMap, PDS_OBSTRUCTION, T_OBSTRUCTS_DIAGONAL_MOVEMENT, 0);
! 353: }
! 354:
! 355: // Run the distance scan.
! 356: grid[x][y] = 1;
! 357: costMap[x][y] = 1;
! 358: dijkstraScan(grid, costMap, true);
! 359: findReplaceGrid(grid, 30000, 30000, 0);
! 360:
! 361: // Block off invalid targets that aren't pathing blockers.
! 362: getTerrainGrid(grid, 0, forbiddenTerrainFlags, forbiddenMapFlags);
! 363: if (!hallwaysAllowed) {
! 364: getPassableArcGrid(grid, 2, 10, 0);
! 365: }
! 366:
! 367: // Get the solution.
! 368: randomLeastPositiveLocationInGrid(grid, retValX, retValY, deterministic);
! 369:
! 370: // dumpLevelToScreen();
! 371: // displayGrid(grid);
! 372: // if (coordinatesAreInMap(*retValX, *retValY)) {
! 373: // hiliteCell(*retValX, *retValY, &yellow, 100, true);
! 374: // }
! 375: // temporaryMessage("Qualifying path selected:", true);
! 376:
! 377: freeGrid(grid);
! 378: freeGrid(costMap);
! 379:
! 380: // Fall back to a pathing-agnostic alternative if there are no solutions.
! 381: if (*retValX == -1 && *retValY == -1) {
! 382: if (getQualifyingLocNear(loc, x, y, hallwaysAllowed, NULL,
! 383: (blockingTerrainFlags | forbiddenTerrainFlags),
! 384: (blockingMapFlags | forbiddenMapFlags),
! 385: false, deterministic)) {
! 386: *retValX = loc[0];
! 387: *retValY = loc[1];
! 388: return true; // Found a fallback solution.
! 389: } else {
! 390: return false; // No solutions.
! 391: }
! 392: } else {
! 393: return true; // Found a primary solution.
! 394: }
! 395: }
! 396:
! 397: void cellularAutomataRound(short **grid, char birthParameters[9], char survivalParameters[9]) {
! 398: short i, j, nbCount, newX, newY;
! 399: enum directions dir;
! 400: short **buffer2;
! 401:
! 402: buffer2 = allocGrid();
! 403: copyGrid(buffer2, grid); // Make a backup of grid in buffer2, so that each generation is isolated.
! 404:
! 405: for(i=0; i<DCOLS; i++) {
! 406: for(j=0; j<DROWS; j++) {
! 407: nbCount = 0;
! 408: for (dir=0; dir< DIRECTION_COUNT; dir++) {
! 409: newX = i + nbDirs[dir][0];
! 410: newY = j + nbDirs[dir][1];
! 411: if (coordinatesAreInMap(newX, newY)
! 412: && buffer2[newX][newY]) {
! 413:
! 414: nbCount++;
! 415: }
! 416: }
! 417: if (!buffer2[i][j] && birthParameters[nbCount] == 't') {
! 418: grid[i][j] = 1; // birth
! 419: } else if (buffer2[i][j] && survivalParameters[nbCount] == 't') {
! 420: // survival
! 421: } else {
! 422: grid[i][j] = 0; // death
! 423: }
! 424: }
! 425: }
! 426:
! 427: freeGrid(buffer2);
! 428: }
! 429:
! 430: // Marks a cell as being a member of blobNumber, then recursively iterates through the rest of the blob
! 431: short fillContiguousRegion(short **grid, short x, short y, short fillValue) {
! 432: enum directions dir;
! 433: short newX, newY, numberOfCells = 1;
! 434:
! 435: grid[x][y] = fillValue;
! 436:
! 437: // Iterate through the four cardinal neighbors.
! 438: for (dir=0; dir<4; dir++) {
! 439: newX = x + nbDirs[dir][0];
! 440: newY = y + nbDirs[dir][1];
! 441: if (!coordinatesAreInMap(newX, newY)) {
! 442: break;
! 443: }
! 444: if (grid[newX][newY] == 1) { // If the neighbor is an unmarked region cell,
! 445: numberOfCells += fillContiguousRegion(grid, newX, newY, fillValue); // then recurse.
! 446: }
! 447: }
! 448: return numberOfCells;
! 449: }
! 450:
! 451: // Loads up **grid with the results of a cellular automata simulation.
! 452: void createBlobOnGrid(short **grid,
! 453: short *retMinX, short *retMinY, short *retWidth, short *retHeight,
! 454: short roundCount,
! 455: short minBlobWidth, short minBlobHeight,
! 456: short maxBlobWidth, short maxBlobHeight, short percentSeeded,
! 457: char birthParameters[9], char survivalParameters[9]) {
! 458:
! 459: short i, j, k;
! 460: short blobNumber, blobSize, topBlobNumber, topBlobSize;
! 461:
! 462: short topBlobMinX, topBlobMinY, topBlobMaxX, topBlobMaxY, blobWidth, blobHeight;
! 463: //short buffer2[maxBlobWidth][maxBlobHeight]; // buffer[][] is already a global short array
! 464: boolean foundACellThisLine;
! 465:
! 466: // Generate blobs until they satisfy the minBlobWidth and minBlobHeight restraints
! 467: do {
! 468: // Clear buffer.
! 469: fillGrid(grid, 0);
! 470:
! 471: // Fill relevant portion with noise based on the percentSeeded argument.
! 472: for(i=0; i<maxBlobWidth; i++) {
! 473: for(j=0; j<maxBlobHeight; j++) {
! 474: grid[i][j] = (rand_percent(percentSeeded) ? 1 : 0);
! 475: }
! 476: }
! 477:
! 478: // colorOverDungeon(&darkGray);
! 479: // hiliteGrid(grid, &white, 100);
! 480: // temporaryMessage("Random starting noise:", true);
! 481:
! 482: // Some iterations of cellular automata
! 483: for (k=0; k<roundCount; k++) {
! 484: cellularAutomataRound(grid, birthParameters, survivalParameters);
! 485:
! 486: // colorOverDungeon(&darkGray);
! 487: // hiliteGrid(grid, &white, 100);
! 488: // temporaryMessage("Cellular automata progress:", true);
! 489: }
! 490:
! 491: // colorOverDungeon(&darkGray);
! 492: // hiliteGrid(grid, &white, 100);
! 493: // temporaryMessage("Cellular automata result:", true);
! 494:
! 495: // Now to measure the result. These are best-of variables; start them out at worst-case values.
! 496: topBlobSize = 0;
! 497: topBlobNumber = 0;
! 498: topBlobMinX = maxBlobWidth;
! 499: topBlobMaxX = 0;
! 500: topBlobMinY = maxBlobHeight;
! 501: topBlobMaxY = 0;
! 502:
! 503: // Fill each blob with its own number, starting with 2 (since 1 means floor), and keeping track of the biggest:
! 504: blobNumber = 2;
! 505:
! 506: for(i=0; i<DCOLS; i++) {
! 507: for(j=0; j<DROWS; j++) {
! 508: if (grid[i][j] == 1) { // an unmarked blob
! 509: // Mark all the cells and returns the total size:
! 510: blobSize = fillContiguousRegion(grid, i, j, blobNumber);
! 511: if (blobSize > topBlobSize) { // if this blob is a new record
! 512: topBlobSize = blobSize;
! 513: topBlobNumber = blobNumber;
! 514: }
! 515: blobNumber++;
! 516: }
! 517: }
! 518: }
! 519:
! 520: // Figure out the top blob's height and width:
! 521: // First find the max & min x:
! 522: for(i=0; i<DCOLS; i++) {
! 523: foundACellThisLine = false;
! 524: for(j=0; j<DROWS; j++) {
! 525: if (grid[i][j] == topBlobNumber) {
! 526: foundACellThisLine = true;
! 527: break;
! 528: }
! 529: }
! 530: if (foundACellThisLine) {
! 531: if (i < topBlobMinX) {
! 532: topBlobMinX = i;
! 533: }
! 534: if (i > topBlobMaxX) {
! 535: topBlobMaxX = i;
! 536: }
! 537: }
! 538: }
! 539:
! 540: // Then the max & min y:
! 541: for(j=0; j<DROWS; j++) {
! 542: foundACellThisLine = false;
! 543: for(i=0; i<DCOLS; i++) {
! 544: if (grid[i][j] == topBlobNumber) {
! 545: foundACellThisLine = true;
! 546: break;
! 547: }
! 548: }
! 549: if (foundACellThisLine) {
! 550: if (j < topBlobMinY) {
! 551: topBlobMinY = j;
! 552: }
! 553: if (j > topBlobMaxY) {
! 554: topBlobMaxY = j;
! 555: }
! 556: }
! 557: }
! 558:
! 559: blobWidth = (topBlobMaxX - topBlobMinX) + 1;
! 560: blobHeight = (topBlobMaxY - topBlobMinY) + 1;
! 561:
! 562: } while (blobWidth < minBlobWidth
! 563: || blobHeight < minBlobHeight
! 564: || topBlobNumber == 0);
! 565:
! 566: // Replace the winning blob with 1's, and everything else with 0's:
! 567: for(i=0; i<DCOLS; i++) {
! 568: for(j=0; j<DROWS; j++) {
! 569: if (grid[i][j] == topBlobNumber) {
! 570: grid[i][j] = 1;
! 571: } else {
! 572: grid[i][j] = 0;
! 573: }
! 574: }
! 575: }
! 576:
! 577: // Populate the returned variables.
! 578: *retMinX = topBlobMinX;
! 579: *retMinY = topBlobMinY;
! 580: *retWidth = blobWidth;
! 581: *retHeight = blobHeight;
! 582: }
CVSweb