This is LifeViewPart.m in view mode; [Download] [Up]
// LifeView by sam_s 910926 // // Life is the classical demonstration of cellular automata. // It was originally created as a simplisting simulation of the dynamics // of living communities. I've always thought these things are pretty // cool; though the algorithm behind Life is exceedingly simple, // getting good performance seems to require different hacks for // the display architecture of every machine. // This one is optimized for a computation client / display server // architecture where the cells are drawn in color to denote their // age. New life, and thus dynamic communites, are drawn in red, while // stable communities tend towards blue with age. I use an unsigned // character for each cell, where the lower 7 bits store the age of // the cell and the high bit indicates whether the cell has changed // since the last iteration. The change bit allows me to only redisplay // changed cells, and I iterate through the grid, displaying all the cells // of a single color before moving to the next color. // This algorithm could be more space efficient; I keep 2 grids, one for the // last state and one to create the current state. In actuality you only // need to buffer one line from the old state, but that makes for kind // of wierd starting and ending code in the iteration loop. Hey, this is // only a quick hack! #import "LifeViewPart.h" #import <appkit/graphics.h> #import <appkit/color.h> #import <libc.h> #import <dpsclient/wraps.h> #define ITERATIONS 2200 //#define ITERATIONS 1500 @implementation LifeView - oneStep { int x,y,siblings; unsigned char (*t)[MAXROWS]; int counter = 0, checksum = 0; if (--countDown < 0) { [self initLife]; [self display]; } t = grid; grid = oldGrid; oldGrid = t; // calculate the color for each square for (x=1; x < (ncols-1); x++) for (y=1; y < (nrows-1); y++) { counter++; siblings = 0; if (oldGrid[x-1][y-1]) siblings++; if (oldGrid[x-1][y]) siblings++; if (oldGrid[x-1][y+1]) siblings++; if (oldGrid[x][y-1]) siblings++; if (oldGrid[x][y+1]) siblings++; if (oldGrid[x+1][y-1]) siblings++; if (oldGrid[x+1][y]) siblings++; if (oldGrid[x+1][y+1]) siblings++; if ((siblings < 2) || (siblings > 3)) { grid[x][y] = 0; if (oldGrid[x][y]) grid[x][y] = 0x80; } else { if (oldGrid[x][y]) { grid[x][y] = MIN(((oldGrid[x][y])+1), COLORS); if (oldGrid[x][y] != grid[x][y]) grid[x][y] |= 0x80; } else if (siblings == 3) grid[x][y] = 0x81; else grid[x][y] = 0; } checksum += (grid[x][y] & 0x7f) * counter; } [self drawSquares]; [self checkStasis:checksum]; return self; } - drawSquares { int x,y; int count; BOOL skippedChange; BOOL foundColor; int currentColorIndex = 0; // iterate as long as there are changed rects to draw // (yuck! the things I put up with in a client server model!) do { skippedChange = NO; foundColor = NO; count = 0; for (x=1; x<ncols-1; x++) for (y=1; y<nrows-1; y++) { if (grid[x][y] & 0x80) { if (foundColor) { if ((grid[x][y] & 0x7f) == currentColorIndex) { grid[x][y] &= 0x7f; changed[count].origin.x = x * 8; changed[count].origin.y = y * 8; count++; } else skippedChange = YES; } else { foundColor = YES; grid[x][y] &= 0x7f; currentColorIndex = grid[x][y]; if (currentColorIndex) PSsethsbcolor(colorTable[currentColorIndex-1],.82,1); else PSsetgray(NX_BLACK); changed[count].origin.x = x * 8; changed[count].origin.y = y * 8; count++; } if (count >= CHANGECOUNT) { // show if reached rect capacity if (foundColor) { NXRectFillList(&changed[0], count); count = 0; } } } } if (foundColor && count) NXRectFillList(&changed[0], count); } while (skippedChange); return self; } - drawSelf:(const NXRect *)rects :(int)rectCount { int i,j,x,y,x2,y2; if (!rects || !rectCount) return self; PSsetgray(0); // NXRectFill(rects); x = MAX((rects->origin.x/8),1); y = MAX((rects->origin.y/8),1); x2 = MIN(((rects->origin.x + rects->size.width)/8),MAXCOLS); y2 = MIN(((rects->origin.y + rects->size.height)/8),MAXROWS); for (i=x; i < x2; i++) for (j=y; j < y2; j++) { grid[i][j] |= 0x80; } [self drawSquares]; for (x=0; x < ncols; x++) { grid[x][0] = grid[x][nrows-1] = 0; } for (y=0; y < nrows; y++) { grid[0][y] = grid[ncols-1][y] = 0; } return self; } - (const char *) windowTitle { return "Life"; } - initFrame:(const NXRect *)frameRect { int i; [super initFrame:frameRect]; for (i=0; i< CHANGECOUNT; i++) { changed[i].size.width = changed[i].size.height = 8; } for (i=0; i<COLORS; i++) { colorTable[i] = ((float)i) / (COLORS-1) * 2.0/3.0; } [self initLife]; return self; } - sizeTo:(NXCoord)width :(NXCoord)height { [super sizeTo:width :height]; [self initLife]; return self; } - initLife { int x,y; oldGrid = &g1[0]; grid = &g2[0]; ncols = MIN((bounds.size.width/8),MAXCOLS); nrows = MIN((bounds.size.height/8),MAXROWS); for (x=0; x < ncols; x++) for (y=0; y < nrows; y++) { if ((random() & 3) || x==0 || y==0 || x == ncols-1 || y == nrows-1) grid[x][y] = 0; else grid[x][y] = 1; oldGrid[x][y] = grid[x][y]; } countDown = ITERATIONS; // init stasis array for (x=0; x<24; x++) stasis[x] = x; sindex = 0; return self; } // detect stasis of period 1,2,3,or 4 // should really use a CRC if guaranteed unique results are required! - checkStasis:(int)checksum { int i; BOOL stasisAcheived = YES; stasis[sindex++] = checksum; if (sindex >=24) sindex = 0; for (i=0; i<12; i++) { if (stasis[i] != stasis[i+12]) { stasisAcheived = NO; break; } } if (stasisAcheived) countDown = 0; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.