home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / BackspaceViews / Life / LifeView.m < prev    next >
Text File  |  1992-02-18  |  8KB  |  369 lines

  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 ''ng 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.  
  40. #import "LifeView.h"
  41. #import "Thinker.h"
  42. #import <appkit/graphics.h> 
  43. #import <appkit/Application.h>
  44. #import <appkit/color.h>
  45. #import <libc.h>
  46. #import <dpsclient/wraps.h>
  47.  
  48. #define ITERATIONS 2200
  49.  
  50. // This file constains the definition for the LifeView and StaticLifeView
  51. // classes.  I have to define the subclass first for this to work, so
  52. // perhaps ld'ing them together is a better way to take care of ordering
  53. // in the object file?
  54.  
  55. // the StaticLifeView animates only when it draw itself;
  56. // it's used in the inspector
  57. @implementation '(icLifeView
  58.  
  59. - drawSelf:(const NXRect *)rects :(int)rectCount
  60. {
  61.     int i;
  62.     
  63.     if (!rects || !rectCount) return self;
  64.     
  65.     PSsetgray(NX_BLACK);
  66.     NXRectFill(rects);
  67.     
  68.     [self initLife];
  69.     [self translate:-40 :-40];
  70.     for (i=0; i<15; i++)
  71.     {
  72.         [self oneStep];
  73.         [[self window] flushWindow];
  74.         NXPing();
  75.     }
  76.     [self translate:40 :40];
  77.  
  78.     return self;
  79. }
  80.  
  81. - initLife
  82. {
  83.     int x,y;
  84.     
  85.     oldGrid = &g1[0];
  86.     grid = &g2[0];
  87.     
  88.     ncols = MIN((bounds.size.width/8 + 10),MAXCOLS);
  89.     nrows = MIN((bounds.size.height/8 + 10),MAXROWS);
  90.     
  91.     for (x=0; x < ncols; x++)
  92.     for (y=0; y < nrows; y++)
  93.     {
  94.         if ((random() & 1) || x==0 || y==0 ||
  95.                 x == ncols-1 ||
  96.                 y == nrows-1)
  97.              grid[x][y] = 0;
  98.         else grid[x][y] = 1;
  99.         
  100.         oldGrid[x][y] = grid[x][y];
  101.     }
  102.     
  103.     countDown = 1000;
  104.     
  105.     // init stasis array
  106.     for (x=0; x<24; x++) stasis[x] = x;
  107.     sindex = 0;
  108.     
  109.     return self;
  110. }
  111.  
  112. @end
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119. @implementation LifeView
  120.  
  121. - oneStep
  122. {
  123.     int x,y,siblings;
  124.     unsigned char (*t)[MAXROWS];
  125.     int counter = 0, checksum = 0;
  126.     
  127.     if (--countDown < 0)
  128.     {    [self initLife];
  129.         [self display];
  130.     }
  131.     
  132.     t = grid; grid = oldGrid; oldGrid = t;
  133.  
  134.     // calculate the color for each square
  135.     for (x=1; x < (ncols-1); x++)
  136.     for (y=1; y < (nrows-1); y++)
  137.     {
  138.         counter++;
  139.         siblings = 0;
  140.         
  141.         if (oldGrid[x-1][y-1]) siblings++;
  142.         if (oldGrid[x-1][y]) siblings++;
  143.         if (oldGrid[x-1][y+1]) siblings++;
  144.         
  145.         if (oldGrid[x][y-1]) siblings++;
  146.         if (oldGrid[x][y+1]) siblings++;
  147.         
  148.         if (oldGrid[x+1][y-1]) siblings++;
  149.         if (oldGrid[x+1][y]) siblings++;
  150.         if (oldGrid[x+1][y+1]) siblings++;
  151.         
  152.         if ((siblings < 2) || (siblings > 3))
  153.         {
  154.             grid[x][y] = 0;
  155.             if (oldGrid[x][y]) grid[x][y] = 0x80;
  156.         }
  157.         else 
  158.         {
  159.             if (oldGrid[x][y])
  160.             {
  161.                 grid[x][y] = MIN(((oldGrid[x][y])+1), COLORS);
  162.                 if (oldGrid[x][y] != grid[x][y]) grid[x][y] |= 0x80;
  163.             }
  164.             else if (siblings == 3) grid[x][y] = 0x81;
  165.             else grid[x][y] = 0;
  166.         }
  167.         
  168.         checksum += (grid[x][y] & 0x7f) * counter;
  169.     }
  170.     
  171.     [self drawSquares];
  172.     
  173.     [self checkStasis:checksum];
  174.  
  175.     return self;
  176. }
  177.  
  178. - drawSquares
  179. {
  180.     int x,y;
  181.     int count;
  182.     BOOL skippedChange;
  183.     BOOL foundColor;
  184.     int currentColorIndex = 0;
  185.  
  186.     // iterate as long as there are changed rects to draw
  187.     // (yuck! the things I put up with in a client server model!)
  188.     
  189.     do {
  190.         skippedChange = NO;
  191.         foundColor = NO;
  192.         count = 0;
  193.  
  194.         for (x=1; x<ncols-1; x++)
  195.         for (y=1; y<nrows-1; y++)
  196.         {
  197.             if (grid[x][y] & 0x80)
  198.             {
  199.                 if (f')Color)
  200.                 {
  201.                     if ((grid[x][y] & 0x7f) == currentColorIndex)
  202.                     {
  203.                         grid[x][y] &= 0x7f;
  204.                         changed[count].origin.x = x * 8;
  205.                         changed[count].origin.y = y * 8;
  206.                         count++;
  207.                     }
  208.                     else skippedChange = YES;
  209.                 }
  210.                 else
  211.                 {
  212.                     foundColor = YES;
  213.                     grid[x][y] &= 0x7f;
  214.                     currentColorIndex = grid[x][y];
  215.                     if (currentColorIndex) 
  216.                         PSsethsbcolor(colorTable[currentColorIndex-1],.82,1);
  217.                     else PSsetgray(NX_BLACK);
  218.  
  219.                     changed[count].origin.x = x * 8;
  220.                     changed[count].origin.y = y * 8;
  221.                     count++;
  222.                 }
  223.  
  224.                 if (count >= CHANGECOUNT)
  225.                 {
  226.                     // show if reached rect capacity
  227.                     if (foundColor)
  228.                     {    NXRectFillList(&changed[0], count);
  229.                         count = 0;
  230.                     }
  231.                 }
  232.             }
  233.         }
  234.         
  235.         if (foundColor && count) NXRectFillList(&changed[0], count);
  236.         
  237.     } while (skippedChange);
  238.     
  239.     return self;
  240. }
  241.  
  242.  
  243. - drawSelf:(const NXRect *)rects :(int)rectCount
  244. {
  245.     int i,j,x,y,x2,y2;
  246.     
  247.     if (!rects || !rectCount) return self;
  248.     PSsetgray(0);
  249.     // NXRectFill(rects);
  250.  
  251.     x = MAX((rects->origin.x/8),1);
  252.     y = MAX((rects->origin.y/8),1);
  253.     x2 = MIN(((rects->origin.x + rects->size.width)/8),MAXCOLS);
  254.     y2 = MIN(((rects->origin.y + rects->size.height)/8),MAXROWS);
  255.     
  256.     for (i=x; i < x2; i++)
  257.     for (j=y; j < y2; j++)
  258.     {
  259.         grid[i][j] |= 0x80;
  260.     }
  261.     
  262.     [self drawSquares];
  263.  
  264.     for (x=0; x < ncols; x++)
  265.     {    grid[x][0] = grid[x][nrows-1] = 0;
  266.     }
  267.     for (y=0; y < nrows; y++)
  268.     {    grid[0][y] = grid[ncols-1][y] = 0;
  269.     }
  270.  
  271.     return self;
  272. }
  273.  
  274. - (const char *) windowTitle
  275. {    return "Life";
  276. }
  277.  
  278. - initFrame:(const NXRect *)frameRect
  279. {
  280.     int i;
  281.     
  282.     [super initFrame:frameRect];
  283.     for (i=0; i< CHANGECOUNT; i++)
  284.     {
  285.         changed[i].size.width = changed[i].size.height = 8;
  286.     }
  287.     
  288.     for (i=0; i<COLORS; i++)
  289.     {
  290.         colorTable[i] = ((float)i) / (COLORS-1) * 2.0/3.0;
  291.     }
  292.     [self initLife];
  293.     return self;
  294. }
  295.  
  296. - sizeTo:(NXCoord)width :(NXCoord)height
  297. {
  298.     [super sizeTo:width :height];
  299.     [self initLife];
  300.     return self;
  301. }
  302.  
  303. - initLife
  304. {
  305.     int x,y;
  306.     
  307.     oldGrid = &g1[0];
  308.     grid = &g2[0];
  309.     
  310.     ncols = MIN((bounds.size.width/8),MAXCOLS);
  311.     nrows = MIN((bounds.size.height/8),MAXROWS);
  312.     
  313.     for (x=0; x < ncols; x++)
  314.     for (y=0; y < nrows; y++)
  315.     {
  316.         if ((random() & 3) || x==0 || y==0 ||
  317.                 x == ncols-1 ||
  318.                 y == nrows-1)
  319.              grid[x][y] = 0;
  320.         else grid[x][y] = 1;
  321.         
  322.         oldGrid[x][y] = grid[x][y];
  323.     }
  324.     
  325.     countDown = ITERATIONS;
  326.     
  327.     // init stasis array
  328.     for (x=0; x<24; x++) stasis[x] ='0    sindex = 0;
  329.     
  330.     return self;
  331. }
  332.  
  333. // detect stasis of period 1,2,3,or 4
  334. // should really use a CRC if guaranteed unique results are required!
  335. - checkStasis:(int)checksum
  336. {
  337.     int i;
  338.     BOOL stasisAcheived = YES;
  339.     
  340.     stasis[sindex++] = checksum;
  341.     if (sindex >=24) sindex = 0;
  342.     
  343.     for (i=0; i<12; i++)
  344.     {
  345.         if (stasis[i] != stasis[i+12])
  346.         {    stasisAcheived = NO;
  347.             break;
  348.         }
  349.     }
  350.     
  351.     if (stasisAcheived) countDown = 0;
  352.     
  353.     return self;
  354. }
  355.  
  356. - inspector:sender
  357. {
  358.     char buf[MAXPATHLEN];
  359.     
  360.     if (!sharedInspectorPanel)
  361.     {
  362.         sprintf(buf,"%s/Life.nib",[sender moduleDirectory:"Life"]);
  363.         [NXApp loadNibFile:buf owner:self withNames:NO];
  364.     }
  365.     return sharedInspectorPanel;
  366. }
  367.  
  368. @end
  369.