Annotation of Examples/AppKit/BackspaceViews/Life/LifeView.m, revision 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.