home *** CD-ROM | disk | FTP | other *** search
- // 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 ''ng 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!
-
- // The ruleset I chose I suspect to be the classic, and it goes like this:
-
- // Living cell with < 2 neighbors -> dies of isolation
- // Living cell with 2 or 3 neighbors -> lives
- // Living cell with > 3 neighbors -> dies of overcrowding
- // empty cell with 3 neighbors -> life is created (reproduction)
-
- // In my model, a new cell is red and changes to blue as it ages to indicate
- // stable colonies. My model also detects stasis where the entire colony
- // is no longer dynamic, and it nukes them and starts again. Stasis of
- // period 1,2,3,4,and 6 are detected (there is a very small chance
- // of false stasis detection).
-
-
- #import "LifeView.h"
- #import "Thinker.h"
- #import <appkit/graphics.h>
- #import <appkit/Application.h>
- #import <appkit/color.h>
- #import <libc.h>
- #import <dpsclient/wraps.h>
-
- #define ITERATIONS 2200
-
- // This file constains the definition for the LifeView and StaticLifeView
- // classes. I have to define the subclass first for this to work, so
- // perhaps ld'ing them together is a better way to take care of ordering
- // in the object file?
-
- // the StaticLifeView animates only when it draw itself;
- // it's used in the inspector
- @implementation '(icLifeView
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- int i;
-
- if (!rects || !rectCount) return self;
-
- PSsetgray(NX_BLACK);
- NXRectFill(rects);
-
- [self initLife];
- [self translate:-40 :-40];
- for (i=0; i<15; i++)
- {
- [self oneStep];
- [[self window] flushWindow];
- NXPing();
- }
- [self translate:40 :40];
-
- return self;
- }
-
- - initLife
- {
- int x,y;
-
- oldGrid = &g1[0];
- grid = &g2[0];
-
- ncols = MIN((bounds.size.width/8 + 10),MAXCOLS);
- nrows = MIN((bounds.size.height/8 + 10),MAXROWS);
-
- for (x=0; x < ncols; x++)
- for (y=0; y < nrows; y++)
- {
- if ((random() & 1) || 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 = 1000;
-
- // init stasis array
- for (x=0; x<24; x++) stasis[x] = x;
- sindex = 0;
-
- return self;
- }
-
- @end
-
-
-
-
-
-
- @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 (f')Color)
- {
- 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] ='0 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;
- }
-
- - inspector:sender
- {
- char buf[MAXPATHLEN];
-
- if (!sharedInspectorPanel)
- {
- sprintf(buf,"%s/Life.nib",[sender moduleDirectory:"Life"]);
- [NXApp loadNibFile:buf owner:self withNames:NO];
- }
- return sharedInspectorPanel;
- }
-
- @end
-