|
|
1.1 ! root 1: // LifeView by sam_s 910926 ! 2: // ! 3: // Life is the classical demonstration of cellular automata. ! 4: // It was originally created as a simplisting simulation of the dynamics ! 5: // of living communities. I've always thought these things are pretty ! 6: // cool; though the algorithm behind Life is exceedingly simple, ! 7: // getting good performance seems to require different hacks for ! 8: // the display architecture of every machine. ! 9: ! 10: // This one is optimized for a computation client / display server ! 11: // architecture where the cells are drawn in color to denote their ! 12: // age. New life, and thus dynamic communites, are drawn in red, while ! 13: // stable communities tend towards blue with age. I use an unsigned ! 14: // character for each cell, where the lower 7 bits store the age of ! 15: // the cell and the high bit indicates whether the cell has changed ! 16: // since the last iteration. The change bit allows me to only redisplay ! 17: // changed cells, and I iterate through the grid, displaying all the cells ! 18: // of a single color before moving to the next color. ! 19: ! 20: // This algorithm could be more space efficient; I keep 2 grids, one for the ! 21: // last state and one to create the current state. In actuality you only ! 22: // need to buffer one line from the old state, but that makes for kind ! 23: // of wierd starting and ending code in the iteration loop. Hey, this is ! 24: // only a quick hack! ! 25: ! 26: // The ruleset I chose I suspect to be the classic, and it goes like this: ! 27: ! 28: // Living cell with < 2 neighbors -> dies of isolation ! 29: // Living cell with 2 or 3 neighbors -> lives ! 30: // Living cell with > 3 neighbors -> dies of overcrowding ! 31: // empty cell with 3 neighbors -> life is created (reproduction) ! 32: ! 33: // In my model, a new cell is red and changes to blue as it ages to indicate ! 34: // stable colonies. My model also detects stasis where the entire colony ! 35: // is no longer dynamic, and it nukes them and starts again. Stasis of ! 36: // period 1,2,3,4,and 6 are detected (there is a very small chance ! 37: // of false stasis detection). ! 38: ! 39: #import <libc.h> ! 40: #import <appkit/appkit.h> ! 41: ! 42: #import "LifeView.h" ! 43: #import "Thinker.h" ! 44: ! 45: #define ITERATIONS 2200 ! 46: ! 47: // This file constains the definition for the LifeView and StaticLifeView ! 48: // classes. I have to define the subclass first for this to work, so ! 49: // perhaps ld'ing them together is a better way to take care of ordering ! 50: // in the object file? ! 51: ! 52: // the StaticLifeView animates only when it draw itself; ! 53: // it's used in the inspector ! 54: @implementation StaticLifeView ! 55: ! 56: - drawSelf:(const NXRect *)rects :(int)rectCount ! 57: { ! 58: int i; ! 59: ! 60: if (!rects || !rectCount) return self; ! 61: ! 62: PSsetgray(NX_BLACK); ! 63: NXRectFill(rects); ! 64: ! 65: [self initLife]; ! 66: [self translate:-40 :-40]; ! 67: for (i=0; i<15; i++) ! 68: { ! 69: [self oneStep]; ! 70: [[self window] flushWindow]; ! 71: NXPing(); ! 72: } ! 73: [self translate:40 :40]; ! 74: ! 75: return self; ! 76: } ! 77: ! 78: - initLife ! 79: { ! 80: int x,y; ! 81: ! 82: oldGrid = &g1[0]; ! 83: grid = &g2[0]; ! 84: ! 85: ncols = MIN((bounds.size.width/8 + 10),MAXCOLS); ! 86: nrows = MIN((bounds.size.height/8 + 10),MAXROWS); ! 87: ! 88: for (x=0; x < ncols; x++) ! 89: for (y=0; y < nrows; y++) ! 90: { ! 91: if ((random() & 1) || x==0 || y==0 || ! 92: x == ncols-1 || ! 93: y == nrows-1) ! 94: grid[x][y] = 0; ! 95: else grid[x][y] = 1; ! 96: ! 97: oldGrid[x][y] = grid[x][y]; ! 98: } ! 99: ! 100: countDown = 1000; ! 101: ! 102: // init stasis array ! 103: for (x=0; x<24; x++) stasis[x] = x; ! 104: sindex = 0; ! 105: ! 106: return self; ! 107: } ! 108: ! 109: @end ! 110: ! 111: ! 112: ! 113: ! 114: ! 115: ! 116: @implementation LifeView ! 117: ! 118: - oneStep ! 119: { ! 120: int x,y,siblings; ! 121: unsigned char (*t)[MAXROWS]; ! 122: int counter = 0, checksum = 0; ! 123: ! 124: if (--countDown < 0) ! 125: { [self initLife]; ! 126: [self display]; ! 127: } ! 128: ! 129: t = grid; grid = oldGrid; oldGrid = t; ! 130: ! 131: // calculate the color for each square ! 132: for (x=1; x < (ncols-1); x++) ! 133: for (y=1; y < (nrows-1); y++) ! 134: { ! 135: counter++; ! 136: siblings = 0; ! 137: ! 138: if (oldGrid[x-1][y-1]) siblings++; ! 139: if (oldGrid[x-1][y]) siblings++; ! 140: if (oldGrid[x-1][y+1]) siblings++; ! 141: ! 142: if (oldGrid[x][y-1]) siblings++; ! 143: if (oldGrid[x][y+1]) siblings++; ! 144: ! 145: if (oldGrid[x+1][y-1]) siblings++; ! 146: if (oldGrid[x+1][y]) siblings++; ! 147: if (oldGrid[x+1][y+1]) siblings++; ! 148: ! 149: if ((siblings < 2) || (siblings > 3)) ! 150: { ! 151: grid[x][y] = 0; ! 152: if (oldGrid[x][y]) grid[x][y] = 0x80; ! 153: } ! 154: else ! 155: { ! 156: if (oldGrid[x][y]) ! 157: { ! 158: grid[x][y] = MIN(((oldGrid[x][y])+1), COLORS); ! 159: if (oldGrid[x][y] != grid[x][y]) grid[x][y] |= 0x80; ! 160: } ! 161: else if (siblings == 3) grid[x][y] = 0x81; ! 162: else grid[x][y] = 0; ! 163: } ! 164: ! 165: checksum += (grid[x][y] & 0x7f) * counter; ! 166: } ! 167: ! 168: [self drawSquares]; ! 169: ! 170: [self checkStasis:checksum]; ! 171: ! 172: return self; ! 173: } ! 174: ! 175: - drawSquares ! 176: { ! 177: int x,y; ! 178: int count; ! 179: BOOL skippedChange; ! 180: BOOL foundColor; ! 181: int currentColorIndex = 0; ! 182: ! 183: // iterate as long as there are changed rects to draw ! 184: // (yuck! the things I put up with in a client server model!) ! 185: ! 186: do { ! 187: skippedChange = NO; ! 188: foundColor = NO; ! 189: count = 0; ! 190: ! 191: for (x=1; x<ncols-1; x++) ! 192: for (y=1; y<nrows-1; y++) ! 193: { ! 194: if (grid[x][y] & 0x80) ! 195: { ! 196: if (foundColor) ! 197: { ! 198: if ((grid[x][y] & 0x7f) == currentColorIndex) ! 199: { ! 200: grid[x][y] &= 0x7f; ! 201: changed[count].origin.x = x * 8; ! 202: changed[count].origin.y = y * 8; ! 203: count++; ! 204: } ! 205: else skippedChange = YES; ! 206: } ! 207: else ! 208: { ! 209: foundColor = YES; ! 210: grid[x][y] &= 0x7f; ! 211: currentColorIndex = grid[x][y]; ! 212: if (currentColorIndex) ! 213: PSsethsbcolor(colorTable[currentColorIndex-1],.82,1); ! 214: else PSsetgray(NX_BLACK); ! 215: ! 216: changed[count].origin.x = x * 8; ! 217: changed[count].origin.y = y * 8; ! 218: count++; ! 219: } ! 220: ! 221: if (count >= CHANGECOUNT) ! 222: { ! 223: // show if reached rect capacity ! 224: if (foundColor) ! 225: { NXRectFillList(&changed[0], count); ! 226: count = 0; ! 227: } ! 228: } ! 229: } ! 230: } ! 231: ! 232: if (foundColor && count) NXRectFillList(&changed[0], count); ! 233: ! 234: } while (skippedChange); ! 235: ! 236: return self; ! 237: } ! 238: ! 239: ! 240: - drawSelf:(const NXRect *)rects :(int)rectCount ! 241: { ! 242: int i,j,x,y,x2,y2; ! 243: ! 244: if (!rects || !rectCount) return self; ! 245: PSsetgray(0); ! 246: // NXRectFill(rects); ! 247: ! 248: x = MAX((rects->origin.x/8),1); ! 249: y = MAX((rects->origin.y/8),1); ! 250: x2 = MIN(((rects->origin.x + rects->size.width)/8),MAXCOLS); ! 251: y2 = MIN(((rects->origin.y + rects->size.height)/8),MAXROWS); ! 252: ! 253: for (i=x; i < x2; i++) ! 254: for (j=y; j < y2; j++) ! 255: { ! 256: grid[i][j] |= 0x80; ! 257: } ! 258: ! 259: [self drawSquares]; ! 260: ! 261: for (x=0; x < ncols; x++) ! 262: { grid[x][0] = grid[x][nrows-1] = 0; ! 263: } ! 264: for (y=0; y < nrows; y++) ! 265: { grid[0][y] = grid[ncols-1][y] = 0; ! 266: } ! 267: ! 268: return self; ! 269: } ! 270: ! 271: - (const char *) windowTitle ! 272: { return "Life"; ! 273: } ! 274: ! 275: - initFrame:(const NXRect *)frameRect ! 276: { ! 277: int i; ! 278: ! 279: [super initFrame:frameRect]; ! 280: for (i=0; i< CHANGECOUNT; i++) ! 281: { ! 282: changed[i].size.width = changed[i].size.height = 8; ! 283: } ! 284: ! 285: for (i=0; i<COLORS; i++) ! 286: { ! 287: colorTable[i] = ((float)i) / (COLORS-1) * 2.0/3.0; ! 288: } ! 289: [self initLife]; ! 290: return self; ! 291: } ! 292: ! 293: - sizeTo:(NXCoord)width :(NXCoord)height ! 294: { ! 295: [super sizeTo:width :height]; ! 296: [self initLife]; ! 297: return self; ! 298: } ! 299: ! 300: - initLife ! 301: { ! 302: int x,y; ! 303: ! 304: oldGrid = &g1[0]; ! 305: grid = &g2[0]; ! 306: ! 307: ncols = MIN((bounds.size.width/8),MAXCOLS); ! 308: nrows = MIN((bounds.size.height/8),MAXROWS); ! 309: ! 310: for (x=0; x < ncols; x++) ! 311: for (y=0; y < nrows; y++) ! 312: { ! 313: if ((random() & 3) || x==0 || y==0 || ! 314: x == ncols-1 || ! 315: y == nrows-1) ! 316: grid[x][y] = 0; ! 317: else grid[x][y] = 1; ! 318: ! 319: oldGrid[x][y] = grid[x][y]; ! 320: } ! 321: ! 322: countDown = ITERATIONS; ! 323: ! 324: // init stasis array ! 325: for (x=0; x<24; x++) stasis[x] = x; ! 326: sindex = 0; ! 327: ! 328: return self; ! 329: } ! 330: ! 331: // detect stasis of period 1,2,3,or 4 ! 332: // should really use a CRC if guaranteed unique results are required! ! 333: - checkStasis:(int)checksum ! 334: { ! 335: int i; ! 336: BOOL stasisAcheived = YES; ! 337: ! 338: stasis[sindex++] = checksum; ! 339: if (sindex >=24) sindex = 0; ! 340: ! 341: for (i=0; i<12; i++) ! 342: { ! 343: if (stasis[i] != stasis[i+12]) ! 344: { stasisAcheived = NO; ! 345: break; ! 346: } ! 347: } ! 348: ! 349: if (stasisAcheived) countDown = 0; ! 350: ! 351: return self; ! 352: } ! 353: ! 354: - inspector:sender ! 355: { ! 356: char buf[MAXPATHLEN]; ! 357: ! 358: if (!sharedInspectorPanel) ! 359: { ! 360: [NXBundle getPath:buf forResource:"Life" ofType:"nib" inDirectory:[sender moduleDirectory:"Life"] withVersion:0]; ! 361: [NXApp loadNibFile:buf owner:self withNames:NO]; ! 362: } ! 363: return sharedInspectorPanel; ! 364: } ! 365: ! 366: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.