Annotation of brogue-ce/src/brogue/Grid.c, Revision 1.1.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