Annotation of Examples/AppKit/BackspaceViews/Life/LifeView.m, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.