/*
      AutomaTron.c         Gary Teachout     August  1989

      lc -L AutomaTron     To compile and link with Lattice 5.0
*/

 #include <intuition/intuition.h>

 #define FPEN        7
 #define DPEN        7
 #define BPEN        0

 #define CMAX        8
 #define SWIDTH      320
 #define SHEIGHT     200
 #define PLANES      3

 #define RULEMAX     ( 7 * 7 ) + 1
 #define SEEDMAX     80

 #define STOPMODE    1 
 #define RULEMODE    2 
 #define SEEDMODE    4 

 struct menubox
 {
   struct MenuItem      item  ;
   struct IntuiText     text  ;
 }  ;

 struct IntuitionBase      *IntuitionBase  ;
 struct GfxBase            *GfxBase  ;

 struct Screen             *screen  ;
 struct Window             *window  ;
 struct IntuiMessage       *mes  ;

 ULONG   class  ;
 USHORT  code  ;

 struct NewScreen   ns =
 {
   0 , 0 , SWIDTH , SHEIGHT , PLANES , DPEN , BPEN , 0 ,
   CUSTOMSCREEN , NULL , "  AutomaTron" , NULL , NULL
 }  ;

 struct TextAttr     stext = { "topaz.font" , 8 , 0 , 0 }  ;

 struct NewWindow   
   nw =                 /* for display window */
   {
      0 , 0 , SWIDTH , SHEIGHT , DPEN , BPEN ,
      MENUPICK | MENUVERIFY ,
      SMART_REFRESH | ACTIVATE | BACKDROP | BORDERLESS ,
      NULL , NULL , NULL , 
      NULL , NULL , 0 , 0 , 0 , 0 , CUSTOMSCREEN
   } ,
   rnw =                /* for string request window */
   {
      0 , 30 , 320 , 38 , DPEN , 3 ,
      GADGETUP ,
      SMART_REFRESH | ACTIVATE | WINDOWDRAG ,
      NULL , NULL , NULL , 
      NULL , NULL , 0 , 0 , 0 , 0 , CUSTOMSCREEN
   }  ;

/* string request gadgets and stuff */
 struct Gadget    rgadget[ 3 ] =
 {
   {  NULL , 4 , 13 , 312 , 8 ,
      GADGHCOMP , TOGGLESELECT | RELVERIFY | STRINGCENTER , STRGADGET , 
      NULL , NULL , NULL , 0 , NULL , 1 , NULL } ,
   {  NULL , 8 , 25 , 100 , 10 ,
      GADGHCOMP , RELVERIFY , BOOLGADGET , 
      NULL , NULL , NULL , 0 , NULL , 2 , NULL } ,
   {  NULL , 212 , 25 , 100 , 10 ,
      GADGHCOMP , RELVERIFY , BOOLGADGET , 
      NULL , NULL , NULL , 0 , NULL , 3 , NULL }
 }  ;

 struct StringInfo   reqinfo  ;

 UBYTE   buff[ SEEDMAX + 1 ] ,
         ubuff[ SEEDMAX + 1 ]  ;

 struct Border    borders[ 3 ] =
 {
   {  -2 , -2 , 4 , 0 , JAM1 , 5 } ,
   {  -1 , -1 , 2 , 0 , JAM1 , 5 } ,
   {  -1 , -1 , 1 , 0 , JAM1 , 5 } ,
 }  ;

 short   xyborders[ 2 ][ 10 ] =
 {
   {  0 , 0 , 315 , 0 , 315 , 11 , 0 , 11 , 0 , 0  } ,
   {  0 , 0 , 101 , 0 , 101 , 11 , 0 , 11 , 0 , 0  } ,
 } ;

 struct IntuiText    gtext[ 2 ] =
 {
   {  FPEN , BPEN , JAM1 , 42 , 2 , NULL , "OK" , NULL  } ,
   {  FPEN , BPEN , JAM1 , 26 , 2 , NULL , "CANCEL" , NULL  }
 }  ;

/* display window menus */
 struct Menu      menulist[ 3 ] =
 {
   {  NULL , 1   , 0 , 90 , 8  , MENUENABLED , " Control" , NULL } ,
   {  NULL , 91  , 0 , 90 , 10 , MENUENABLED , " Rules" , NULL } ,
   {  NULL , 181 , 0 , 90 , 10 , MENUENABLED , " Seeds" , NULL } 
 }  ;

 struct menubox 
   controlmenu[ 6 ] =
   {
      {
         {  NULL , 0 , 0  , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'S' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Stop" , NULL } 
      } ,
      {
         {  NULL , 0 , 11 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'C' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Continue" , NULL } 
      } ,
      {
         {  NULL , 0 , 22 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Precision" , NULL } 
      } ,
      {
         {  NULL , 0 , 33 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Neighborhood" , NULL } 
      } ,
      {
         {  NULL , 0 , 44 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Modes" , NULL } 
      } ,
      {
         {  NULL , 0 , 66 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'Q' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Quit" , NULL } 
      } 
   } ,
   rulesmenu[ 2 ] =
   {
      {
         {  NULL , 0 , 0  , 110 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'R' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Random" , NULL } 
      } ,
      {
         {  NULL , 0 , 11 , 110 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Custom" , NULL } 
      }
   } ,
   seedsmenu[ 3 ] =
   {
      {
         {  NULL , 0 , 0  , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'P' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Random" , NULL } 
      } ,
      {
         {  NULL , 0 , 11 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Custom" , NULL } 
      } ,
      {
         {  NULL , 0 , 22 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'O' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Old Seed" , NULL } 
      }
   } ,
   precisionsub[ 3 ] =
   {
      {
         {  NULL , 130 , 0  , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "160 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 11 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT | CHECKED ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "320 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 22 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "640 Cells" , NULL } 
      }
   } ,
   neighborsub[ 6 ] =
   {
      {
         {  NULL , 130 , 0  , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0x3e , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "2 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 11 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT | CHECKED ,
            0x3d , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "3 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 22 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0x3b , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "4 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 33 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0x37 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "5 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 44 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0x2f , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "6 Cells" , NULL } 
      } ,
      {
         {  NULL , 130 , 55 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKIT ,
            0x1f , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "7 Cells" , NULL } 
      }
   } ,
   modesub[ 3 ] =
   {
      {
         {  NULL , 130 , 0  , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | MENUTOGGLE | CHECKIT ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "Stop" , NULL } 
      } ,
      {
         {  NULL , 130 , 11 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP
            | MENUTOGGLE | CHECKIT | CHECKED ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "New Seed" , NULL } 
      } ,
      {
         {  NULL , 130 , 22 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP
            | MENUTOGGLE | CHECKIT | CHECKED ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "New Rule" , NULL } 
      }
   }  ;

/* assorted global variables */
 USHORT  line ,                  /* display y */
         stopflag = 0 ,
         neighborhood = 3 ,
         precision = SWIDTH ,
         modes = SEEDMODE | RULEMODE  ;

 UBYTE   colors = 4 ,
         cells1[ 640 ] ,         /* cell array 1 */
         cells2[ 640 ] ,         /* cell array 2 */
         *old ,                  /* pointer to old cells */
         *new ,                  /* pointer to new cells */
         oldseed[ SEEDMAX + 1 ] = { 1 , 255 } ,
         rule[ RULEMAX ] = { 0 , 1 , 3 , 1 , 1 , 2 , 3 , 0 , 0 }  ;

 UBYTE   ctab[ CMAX ][ 3 ] =     /* screen colors */
 {
   { 0 , 0 , 0 } ,
   { 15 , 0 , 0 } ,
   { 0 , 15 , 0 } ,
   { 0 , 0 , 15 } ,

   { 14 , 14 , 0 } ,
   { 14 , 0 , 14 } ,
   { 0 , 14 , 14 } ,
   { 14 , 14 , 14 } 
 }  ;


 char                   *AllocMem()  ;
 struct Screen          *OpenScreen()  ;
 struct Window          *OpenWindow()  ;
 struct IntuiMessage    *GetMsg()  ;

 void    cleanup( void )  ;
 void    stepauto( void )  ;
 void    display( void )  ;
 UBYTE   random( UBYTE )  ;
 void    randrule( void )  ;
 void    randseed( void )  ;
 void    handlemsg( void )  ;
 void    handlemenu( void )  ;
 void    stoploop( void )  ;
 short   stringreq( UBYTE * , short )  ;


 void main()
 {
   short    i  ;

   IntuitionBase = ( struct IntuitionBase * )
                   OpenLibrary( "intuition.library" , 33 )  ;
   if ( ! IntuitionBase )
      cleanup()  ;

   GfxBase = ( struct GfxBase * )
             OpenLibrary( "graphics.library" , 33 )  ;
   if ( ! GfxBase )
      cleanup()  ;

   ns.Font = &stext  ;
   screen = OpenScreen( &ns )  ;
   if ( ! screen )
      cleanup()  ;

   for ( i = 0 ; i < CMAX ; i ++ )
      SetRGB4( &screen->ViewPort , i , ctab[ i ][ 0 ] ,
                                       ctab[ i ][ 1 ] ,
                                       ctab[ i ][ 2 ] )  ;

   rgadget[ 0 ].NextGadget = &rgadget[ 1 ]  ;
   rgadget[ 1 ].NextGadget = &rgadget[ 2 ]  ;
   rgadget[ 0 ].SpecialInfo = ( APTR ) &reqinfo  ;
   reqinfo.Buffer = buff  ;
   reqinfo.UndoBuffer = ubuff  ;
   rgadget[ 0 ].GadgetRender = ( APTR ) &borders[ 0 ]  ;
   rgadget[ 1 ].GadgetRender = ( APTR ) &borders[ 1 ]  ;
   rgadget[ 2 ].GadgetRender = ( APTR ) &borders[ 2 ]  ;
   borders[ 0 ].XY = &xyborders[ 0 ][ 0 ]  ;
   borders[ 1 ].XY = &xyborders[ 1 ][ 0 ]  ;
   borders[ 2 ].XY = &xyborders[ 1 ][ 0 ]  ;
   rgadget[ 1 ].GadgetText = &gtext[ 0 ]  ;
   rgadget[ 2 ].GadgetText = &gtext[ 1 ]  ;
   rnw.FirstGadget = &rgadget[ 0 ]  ;

   nw.Screen = screen  ;
   window = OpenWindow( &nw )  ;
   if ( ! window )
      cleanup()  ;
 
   SetAPen( window->RPort , 7 )  ;
   Move( window->RPort , 10 , 140 )  ;
   Text( window->RPort , "One Dimensional Cellular Automation" , 35 )  ;
   Move( window->RPort , 10 , 152 )  ;
   Text( window->RPort , "by  Gary Teachout" , 17 )  ;

   menulist[ 0 ].NextMenu = &menulist[ 1 ]  ;
   menulist[ 0 ].FirstItem = &controlmenu[ 0 ].item  ;
   menulist[ 1 ].NextMenu = &menulist[ 2 ]  ;
   menulist[ 1 ].FirstItem = &rulesmenu[ 0 ].item  ;
   menulist[ 2 ].FirstItem = &seedsmenu[ 0 ].item  ;

   controlmenu[ 0 ].item.ItemFill = ( APTR ) &controlmenu[ 0 ].text  ;
   controlmenu[ 0 ].item.NextItem = &controlmenu[ 1 ].item  ;
   controlmenu[ 1 ].item.ItemFill = ( APTR ) &controlmenu[ 1 ].text  ;
   controlmenu[ 1 ].item.NextItem = &controlmenu[ 2 ].item  ;
   controlmenu[ 2 ].item.ItemFill = ( APTR ) &controlmenu[ 2 ].text  ;
   controlmenu[ 2 ].item.NextItem = &controlmenu[ 3 ].item  ;
   controlmenu[ 3 ].item.ItemFill = ( APTR ) &controlmenu[ 3 ].text  ;
   controlmenu[ 3 ].item.NextItem = &controlmenu[ 4 ].item  ;
   controlmenu[ 4 ].item.ItemFill = ( APTR ) &controlmenu[ 4 ].text  ;
   controlmenu[ 4 ].item.NextItem = &controlmenu[ 5 ].item  ;
   controlmenu[ 5 ].item.ItemFill = ( APTR ) &controlmenu[ 5 ].text  ;

   controlmenu[ 2 ].item.SubItem = &precisionsub[ 0 ].item  ;
      precisionsub[ 0 ].item.ItemFill = ( APTR ) &precisionsub[ 0 ].text  ;
      precisionsub[ 0 ].item.NextItem = &precisionsub[ 1 ].item  ;
      precisionsub[ 1 ].item.ItemFill = ( APTR ) &precisionsub[ 1 ].text  ;
      precisionsub[ 1 ].item.NextItem = &precisionsub[ 2 ].item  ;
      precisionsub[ 2 ].item.ItemFill = ( APTR ) &precisionsub[ 2 ].text  ;

   controlmenu[ 3 ].item.SubItem = &neighborsub[ 0 ].item  ;
      neighborsub[ 0 ].item.ItemFill = ( APTR ) &neighborsub[ 0 ].text  ;
      neighborsub[ 0 ].item.NextItem = &neighborsub[ 1 ].item  ;
      neighborsub[ 1 ].item.ItemFill = ( APTR ) &neighborsub[ 1 ].text  ;
      neighborsub[ 1 ].item.NextItem = &neighborsub[ 2 ].item  ;
      neighborsub[ 2 ].item.ItemFill = ( APTR ) &neighborsub[ 2 ].text  ;
      neighborsub[ 2 ].item.NextItem = &neighborsub[ 3 ].item  ;
      neighborsub[ 3 ].item.ItemFill = ( APTR ) &neighborsub[ 3 ].text  ;
      neighborsub[ 3 ].item.NextItem = &neighborsub[ 4 ].item  ;
      neighborsub[ 4 ].item.ItemFill = ( APTR ) &neighborsub[ 4 ].text  ;
      neighborsub[ 4 ].item.NextItem = &neighborsub[ 5 ].item  ;
      neighborsub[ 5 ].item.ItemFill = ( APTR ) &neighborsub[ 5 ].text  ;

   controlmenu[ 4 ].item.SubItem = &modesub[ 0 ].item  ;
      modesub[ 0 ].item.ItemFill = ( APTR ) &modesub[ 0 ].text  ;
      modesub[ 0 ].item.NextItem = &modesub[ 1 ].item  ;
      modesub[ 1 ].item.ItemFill = ( APTR ) &modesub[ 1 ].text  ;
      modesub[ 1 ].item.NextItem = &modesub[ 2 ].item  ;
      modesub[ 2 ].item.ItemFill = ( APTR ) &modesub[ 2 ].text  ;

   rulesmenu[ 0 ].item.ItemFill = ( APTR ) &rulesmenu[ 0 ].text  ;
   rulesmenu[ 0 ].item.NextItem = &rulesmenu[ 1 ].item  ;
   rulesmenu[ 1 ].item.ItemFill = ( APTR ) &rulesmenu[ 1 ].text  ;

   seedsmenu[ 0 ].item.ItemFill = ( APTR ) &seedsmenu[ 0 ].text  ;
   seedsmenu[ 0 ].item.NextItem = &seedsmenu[ 1 ].item  ;
   seedsmenu[ 1 ].item.ItemFill = ( APTR ) &seedsmenu[ 1 ].text  ;
   seedsmenu[ 1 ].item.NextItem = &seedsmenu[ 2 ].item  ;
   seedsmenu[ 2 ].item.ItemFill = ( APTR ) &seedsmenu[ 2 ].text  ;

   SetMenuStrip( window , menulist ) ;

   ShowTitle( screen , TRUE )  ;

   old = cells1  ;
   new = cells2  ;

   randseed()  ;

   for ( ; ; )                   /* loop for each line */
   {
      stepauto()  ;

      display()  ;

      if ( old == cells2 )
      {
         old = cells1  ;
         new = cells2  ;
      }
      else
      {
         old = cells2  ;
         new = cells1  ;
      }

      if ( line == 12 )      
      {
         if ( modes & STOPMODE )
            stoploop()  ;
         if ( ( modes & RULEMODE ) && ( line == 12 ) )
            randrule()  ;
         if ( ( modes & SEEDMODE ) && ( line == 12 ) )
            randseed()  ;
      }

      handlemsg()  ;
   }
 }


 void cleanup()  
 {
   if ( window )
   {
      ClearMenuStrip( window )  ;
      CloseWindow( window )  ;
   }

   if ( screen )
      CloseScreen( screen )  ;

   if ( GfxBase )
      CloseLibrary( GfxBase )  ;

   if ( IntuitionBase )
      CloseLibrary( IntuitionBase )  ;

   exit()  ;
 }


 void stepauto()     /* this is the actual line automation step */
 {
   short    x , t  ;
   UBYTE    *n , *or , *ol , *oc  ;

   n = new  ;

   switch ( neighborhood )
   {
   case 2 :
      ol = old + precision - 1  ;
      or = old + 1  ;
      *( n ++ ) = rule[ *( or ++ ) + *ol ]  ; 
      ol = old  ;
      for ( x = 2 ; x < precision ; x ++ )
         *( n ++ ) = rule[ *( or ++ ) + *( ol ++ ) ]  ; 
      or = old  ;
      *n = rule[ *or + *ol ]  ; 
      break  ;
   case 3 :
      t = *( old + precision - 1 ) + *( old + 1 ) + *old  ;
      *( n ++ ) = rule[ t ]  ; 
      or = old + 2  ;
      ol = old + precision - 1  ;
      t = t - *ol + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      ol = old  ;
      for ( x = 3 ; x < precision ; x ++ )
      {
         t = t - *( ol ++ ) + *( or ++ )  ;
         *( n ++ ) = rule[ t ]  ; 
      }
      or = old  ;
      t = t - *ol + *or  ;
      *n = rule[ t ]  ; 
      break  ;
   case 4 :
      oc = old  ;
      t =      *( old + precision - 1 )
            +  *( old + precision - 2 )
            +  *( old + 2 )
            +  *( old + 1 )
            +  *old  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      or = old + 3  ;
      ol = old + precision - 2  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      t = t - *ol + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      ol = old  ;
      for ( x = 5 ; x < precision ; x ++ )
      {
         t = t - *( ol ++ ) + *( or ++ )  ;
         *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      }
      or = old  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      t = t - *ol + *or  ;
      *n = rule[ t - *oc ]  ; 

      break  ;
   case 5 :
      t =      *( old + precision - 1 )
            +  *( old + precision - 2 )
            +  *( old + 2 )
            +  *( old + 1 )
            +  *old  ;
      *( n ++ ) = rule[ t ]  ; 
      or = old + 3  ;
      ol = old + precision - 2  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      t = t - *ol + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      ol = old  ;
      for ( x = 5 ; x < precision ; x ++ )
      {
         t = t - *( ol ++ ) + *( or ++ )  ;
         *( n ++ ) = rule[ t ]  ; 
      }
      or = old  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      t = t - *ol + *or  ;
      *n = rule[ t ]  ; 
      break  ;
   case 6 :
      oc = old  ;
      t =      *( old + precision - 1 )
            +  *( old + precision - 2 )
            +  *( old + precision - 3 )
            +  *( old + 3 )
            +  *( old + 2 )
            +  *( old + 1 )
            +  *( old )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      or = old + 4  ;
      ol = old + precision - 3  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      t = t - *ol + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      ol = old  ;
      for ( x = 7 ; x < precision ; x ++ )
      {
         t = t - *( ol ++ ) + *( or ++ )  ;
         *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      }
      or = old  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t - *( oc ++ ) ]  ; 
      t = t - *ol + *or  ;
      *n = rule[ t - *oc ]  ; 
      break  ;
   case 7 :
      t =      *( old + precision - 1 )
            +  *( old + precision - 2 )
            +  *( old + precision - 3 )
            +  *( old + 3 )
            +  *( old + 2 )
            +  *( old + 1 )
            +  *( old )  ;
      *( n ++ ) = rule[ t ]  ; 
      or = old + 4  ;
      ol = old + precision - 3  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      t = t - *ol + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      ol = old  ;
      for ( x = 7 ; x < precision ; x ++ )
      {
         t = t - *( ol ++ ) + *( or ++ )  ;
         *( n ++ ) = rule[ t ]  ; 
      }
      or = old  ;
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      t = t - *( ol ++ ) + *( or ++ )  ;
      *( n ++ ) = rule[ t ]  ; 
      t = t - *ol + *or  ;
      *n = rule[ t ]  ; 
      break  ;
   }
 }


 void display()      /* render cells directly to screens bit map */
 {
   USHORT   d , i , j , k , m , *wp[ PLANES ] , *wpc[ PLANES ]  ;
   UBYTE    cm , *c ;

   if ( precision == 160 )
   {
      i = ( line * screen->BitMap.BytesPerRow )  ;
      wp[ 0 ] = ( USHORT * ) ( screen->BitMap.Planes[ 0 ] + i )  ;
      wp[ 1 ] = ( USHORT * ) ( screen->BitMap.Planes[ 1 ] + i )  ;
      wp[ 2 ] = ( USHORT * ) ( screen->BitMap.Planes[ 2 ] + i )  ;
      i = ( ( line + 1 ) * screen->BitMap.BytesPerRow )  ;
      wpc[ 0 ] = ( USHORT * ) ( screen->BitMap.Planes[ 0 ] + i )  ;
      wpc[ 1 ] = ( USHORT * ) ( screen->BitMap.Planes[ 1 ] + i )  ;
      wpc[ 2 ] = ( USHORT * ) ( screen->BitMap.Planes[ 2 ] + i )  ;
      c = new  ;

      for ( i = 0 ; i < precision ; i += 8 )
      {
         for ( cm = 1 , j = 0 ; j < PLANES ; j ++ , cm = cm << 1 )
         {
            d = 0  ;
            for ( m = 0xc000 , k = 0 ; k < 8 ; m = m >> 2 , k ++ )
            {
               if ( *( c + k ) & cm )
                  d |= m  ;
            }
            *wp[ j ] = d  ;
            wp[ j ] ++  ;
            *wpc[ j ] = d  ;
            wpc[ j ] ++  ;
         }
         c += 8  ;
      }

      line += 2  ;
      if ( line >= window->Height )
         line = 12  ;
      else
      {
         i = ( line * screen->BitMap.BytesPerRow )  ;
         wp[ 0 ] = ( USHORT * ) ( screen->BitMap.Planes[ 0 ] + i )  ;
         wp[ 1 ] = ( USHORT * ) ( screen->BitMap.Planes[ 1 ] + i )  ;
         wp[ 2 ] = ( USHORT * ) ( screen->BitMap.Planes[ 2 ] + i )  ;
         i = ( ( line + 1 ) * screen->BitMap.BytesPerRow )  ;
         wpc[ 0 ] = ( USHORT * ) ( screen->BitMap.Planes[ 0 ] + i )  ;
         wpc[ 1 ] = ( USHORT * ) ( screen->BitMap.Planes[ 1 ] + i )  ;
         wpc[ 2 ] = ( USHORT * ) ( screen->BitMap.Planes[ 2 ] + i )  ;

         for ( i = 0 ; i < precision ; i += 8 )
         {
            for ( j = 0 ; j < PLANES ; j ++ )
            {
               *wp[ j ] = 0  ;
               wp[ j ] ++  ;
               *wpc[ j ] = 0  ;
               wpc[ j ] ++  ;
            }
         }
      }
   }
   else
   {
      i = ( line * screen->BitMap.BytesPerRow )  ;
      wp[ 0 ] = ( USHORT * ) ( screen->BitMap.Planes[ 0 ] + i )  ;
      wp[ 1 ] = ( USHORT * ) ( screen->BitMap.Planes[ 1 ] + i )  ;
      wp[ 2 ] = ( USHORT * ) ( screen->BitMap.Planes[ 2 ] + i )  ;
      c = new  ;

      for ( i = 0 ; i < precision ; i += 16 )
      {
         for ( cm = 1 , j = 0 ; j < PLANES ; j ++ , cm = cm << 1 )
         {
            d = 0  ;
            for ( m = 0x8000 , k = 0 ; k < 16 ; m = m >> 1 , k ++ )
            {
               if ( *( c + k ) & cm )
                  d |= m  ;
            }
            *wp[ j ] = d  ;
            wp[ j ] ++  ;
         }
         c += 16  ;
      }

      line ++  ;
      if ( line == window->Height )
         line = 12  ;
      else
      {
         i = ( line * screen->BitMap.BytesPerRow )  ;
         wp[ 0 ] = ( USHORT * ) ( screen->BitMap.Planes[ 0 ] + i )  ;
         wp[ 1 ] = ( USHORT * ) ( screen->BitMap.Planes[ 1 ] + i )  ;
         wp[ 2 ] = ( USHORT * ) ( screen->BitMap.Planes[ 2 ] + i )  ;

         for ( i = 0 ; i < precision ; i += 16 )
         {
            for ( j = 0 ; j < PLANES ; j ++ )
            {
               *wp[ j ] = 0  ;
               wp[ j ] ++  ;
            }
         }
      }
   }
 }


 UBYTE random( a )
   UBYTE    a  ;
 {
   #define RANDSHIFT      8
   #define RANDTAB        23
   #define RANDCOMP       8388608
   static UBYTE   fp = 1  ;
   static long    v[ RANDTAB ] , rr ;
   short          vi  ;

   if ( fp )
   {
      CurrentTime( &v[ 0 ] , &v[ 1 ] )  ;
      srand( v[ 1 ] )  ;
      for ( vi = 0 ; vi < RANDTAB ; vi ++ )
         v[ vi ] = rand() >> RANDSHIFT  ;
      rr = rand() >> RANDSHIFT  ;
      fp = 0  ;
   }

   vi = RANDTAB * rr / RANDCOMP  ;
   rr = v[ vi ]  ;
   v[ vi ] = rand() >> RANDSHIFT  ;

   return ( UBYTE ) ( ( a * rr ) / RANDCOMP )  ;
 }


 void randrule()
 {
   short    i , s  ;
   UBYTE    c  ;


   if ( random( 2 ) )
      c = 2 + random( 6 )  ;          /* 2 - 7  colors */
   else
      c = 3 + random( 3 )  ;          /* 3 - 5  colors */
   s = ( ( c - 1 ) * neighborhood ) + 1  ;
   do
   {
      for ( i = 0 ; i < s ; i ++ )
         rule[ i ] = random( c )  ;
      for ( ; i < RULEMAX ; i ++ )
         rule[ i ] = 0  ;
      if ( random( 2 ) )
         rule[ 0 ] = 0  ;
      for ( colors = 0 , i = 0 ; i < RULEMAX ; i ++ )
         if ( rule[ i ] >= colors )
            colors = rule[ i ] + 1  ;
   } while ( colors <= 1 )  ;
 }


 void randseed()
 {
   short    x   ;

   line = 12  ;
   for ( x = 0 ; x < precision ; x ++ )
      *( new + x ) = random( colors )  ;

   display()  ;

   if ( old == cells2 )
   {
      old = cells1  ;
      new = cells2  ;
   }
   else
   {
      old = cells2  ;
      new = cells1  ;
   }
 }


 void handlemsg()
 {
   while ( mes = GetMsg( window->UserPort ) )
   {
      class = mes->Class  ;
      code = mes->Code  ;
      ReplyMsg( mes )  ;
      switch ( class )
      {
      case MENUVERIFY :
         Wait( 1 << window->UserPort->mp_SigBit )  ;
         while ( class != MENUPICK )
         {
            if ( mes = GetMsg( window->UserPort ) )
            {
               class = mes->Class  ;
               code = mes->Code  ;
               ReplyMsg( mes )  ;
            }
         }
      case MENUPICK :
         handlemenu()  ;
         break  ;
      }
   }
 }


 void handlemenu()
 {
   short    i , j  ;

   switch ( MENUNUM( code ) )
   {
   case 0 : /* control */
      switch ( ITEMNUM( code ) )
      {
      case 0 : /* stop */
         stoploop()  ;
         break  ;
      case 1 : /* continue */
         stopflag = 0  ;
         break  ;
      case 2 : /* precision */
         switch ( SUBNUM( code ) )
         {
         case 0 :
            switch ( precision )
            {
            case 160 :
               break  ;
            case 320 :
               line = 12  ;
               break ;
            case 640 :
               line = 12  ;
               ns.Width = 320  ;
               ns.Height = 200  ;
               ns.ViewModes = 0  ;
               nw.Width = 320  ;
               nw.Height = 200  ;
               ClearMenuStrip( window )  ;
               CloseWindow( window )  ;
               CloseScreen( screen )  ;
               window = NULL  ;
               screen = OpenScreen( &ns )  ;
               if ( screen )
                  window = OpenWindow( &nw )  ;
               if ( window )
               {
                  SetMenuStrip( window , menulist ) ;
                  for ( i = 0 ; i < CMAX ; i ++ )
                     SetRGB4( &screen->ViewPort , i , ctab[ i ][ 0 ] ,
                                                      ctab[ i ][ 1 ] ,
                                                      ctab[ i ][ 2 ] )  ;
               }
               else
               {
                  cleanup()  ;
               }
               break  ;
            }
            precision = 160  ;
            precisionsub[ 1 ].item.Flags =      precisionsub[ 1 ].item.Flags
                                             &  ( ~ CHECKED )  ;
            precisionsub[ 2 ].item.Flags =      precisionsub[ 2 ].item.Flags
                                             &  ( ~ CHECKED )  ;
            break  ;
         case 1 :
            switch ( precision )
            {
            case 160 :
               line = 12  ;
               for ( i = 0 ; i < 160 ; i ++ )
                  *( old + 160 + i ) = *( old + i )  ;
               break  ;
            case 320 :
               break ;
            case 640 :
               line = 12  ;
               ns.Width = 320  ;
               ns.Height = 200  ;
               ns.ViewModes = 0  ;
               nw.Width = 320  ;
               nw.Height = 200  ;
               ClearMenuStrip( window )  ;
               CloseWindow( window )  ;
               CloseScreen( screen )  ;
               window = NULL  ;
               screen = OpenScreen( &ns )  ;
               if ( screen )
                  window = OpenWindow( &nw )  ;
               if ( window )
                  SetMenuStrip( window , menulist ) ;
               else
                  cleanup()  ;
               for ( i = 0 ; i < CMAX ; i ++ )
                  SetRGB4( &screen->ViewPort , i , ctab[ i ][ 0 ] ,
                                                   ctab[ i ][ 1 ] ,
                                                   ctab[ i ][ 2 ] )  ;
               break  ;
            }
            precision = 320  ;
            precisionsub[ 0 ].item.Flags =      precisionsub[ 0 ].item.Flags
                                             &  ( ~ CHECKED )  ;
            precisionsub[ 2 ].item.Flags =      precisionsub[ 2 ].item.Flags
                                             &  ( ~ CHECKED )  ;
            break  ;
         case 2 :
            switch ( precision )
            {
            case 160 :
               for ( i = 0 ; i < 160 ; i ++ )
                  *( old + 160 + i ) = *( old + i )  ;
            case 320 :
               for ( i = 0 ; i < 320 ; i ++ )
                  *( old + 320 + i ) = *( old + i )  ;
               line = 12  ;
               ns.Width = 640  ;
               ns.Height = 400  ;
               ns.ViewModes = HIRES | LACE  ;
               nw.Width = 640  ;
               nw.Height = 400  ;
               ClearMenuStrip( window )  ;
               CloseWindow( window )  ;
               CloseScreen( screen )  ;
               window = NULL  ;
               screen = OpenScreen( &ns )  ;
               if ( screen )
                  window = OpenWindow( &nw )  ;
               if ( window )
               {
                  for ( i = 0 ; i < CMAX ; i ++ )
                     SetRGB4( &screen->ViewPort , i , ctab[ i ][ 0 ] ,
                                                      ctab[ i ][ 1 ] ,
                                                      ctab[ i ][ 2 ] )  ;
                  SetMenuStrip( window , menulist ) ;
                  precision = 640  ;
                  precisionsub[ 0 ].item.Flags =
                           precisionsub[ 0 ].item.Flags & ( ~ CHECKED )  ;
                  precisionsub[ 1 ].item.Flags =
                           precisionsub[ 1 ].item.Flags & ( ~ CHECKED )  ;
               }
               else
               {
                  ns.Width = 320  ;
                  ns.Height = 200  ;
                  ns.ViewModes = 0  ;
                  nw.Width = 320  ;
                  nw.Height = 200  ;
                  if ( screen )
                     CloseScreen( screen )  ;
                  screen = OpenScreen( &ns )  ;
                  if ( screen )
                     window = OpenWindow( &nw )  ;
                  if ( window )
                     SetMenuStrip( window , menulist ) ;
                  else
                     cleanup()  ;
                  for ( i = 0 ; i < CMAX ; i ++ )
                     SetRGB4( &screen->ViewPort , i , ctab[ i ][ 0 ] ,
                                                      ctab[ i ][ 1 ] ,
                                                      ctab[ i ][ 2 ] )  ;
                  SetAPen( window->RPort , 7 )  ;
                  Move( window->RPort , 10 , 140 )  ;
                  Text(    window->RPort ,
                           "Unable to open high res screen" , 30 )  ;
                  precision = 320  ;
                  precisionsub[ 0 ].item.Flags =
                           precisionsub[ 0 ].item.Flags & ( ~ CHECKED )  ;
                  precisionsub[ 1 ].item.Flags =
                           precisionsub[ 1 ].item.Flags | CHECKED  ;
                  precisionsub[ 2 ].item.Flags =
                           precisionsub[ 2 ].item.Flags & ( ~ CHECKED )  ;
               }
               break  ;
            case 640 :
               break  ;
            }
            break  ;
         }
         break  ;
      case 3 : /* neighborhood */
         neighborhood = 2 + SUBNUM( code )  ;
         break  ;
      case 4 : /* modes */
         switch ( SUBNUM( code ) )
         {
         case 0 :
            modes = modes ^ STOPMODE  ;
            break  ;
         case 1 :
            modes = modes ^ SEEDMODE  ;
            break  ;
         case 2 :
            modes = modes ^ RULEMODE  ;
            break  ;
         }
         break  ;
      case 5 : /* quit */
         cleanup()  ;
         break  ;
      }
      break  ;

   case 1 : /* rule */
      switch ( ITEMNUM( code ) )
      {
      case 0 :
         randrule()  ;
         break  ;
      case 1 :
         for ( i = RULEMAX - 1 ; ( i >= 0 ) && ( ! rule[ i ] ) ; i -- )
            ; 
         for ( j = 0 ; i >= 0 ; i -- , j ++ )
            buff[ j ] = rule[ i ] + 48  ;
         buff[ j ] = 0  ;
         if ( stringreq( "  Enter New Rule  " , RULEMAX + 1 ) )
         {
            for ( i = 0 ; ( i < RULEMAX + 1 ) && ( buff[ i ] ) ; i ++ )
               ;
            for ( j = 0 , i -- ; i >= 0 ; i -- , j ++ )
               rule[ j ] = ( buff[ i ] - 48 ) & 7  ;
            for ( ; j < RULEMAX ; j ++ )
               rule[ j ] = 0  ;
            for ( colors = 0 , i = 0 ; i < RULEMAX ; i ++ )
               if ( rule[ i ] >= colors )
                  colors = rule[ i ] + 1  ;
         }
         break  ;
      }
      break  ;

   case 2 : /* seed */
      switch ( ITEMNUM( code ) )
      {
      case 0 :
         randseed()  ;
         break  ;
      case 1 :
         for ( i = 0 ; ( i < SEEDMAX + 1 ) && ( oldseed[ i ] != 255 ) ; i ++ )
            buff[ i ] = oldseed[ i ] + 48  ;
         buff[ i ] = 0  ;
         if ( stringreq( "  Enter New Seed  " , SEEDMAX + 1 ) )
         {
            for ( i = 0 ; ( i < SEEDMAX + 1 ) && buff[ i ] ; i ++ )
               oldseed[ i ] = ( buff[ i ] - 48 ) & 7  ;
            oldseed[ i ] = 255  ;
         }
         else
            break  ;
      case 2 :
         line = 12  ;
         for ( i = 0 ; i < precision ; i ++ )
            *( new + i ) = 0  ;
         j = ( precision >> 1 ) - 10  ;
         for ( i = 0 ; ( i < SEEDMAX ) && ( oldseed[ i ] != 255 ) ; i ++ )
            *( new + i + j ) = oldseed[ i ]  ;
         display()  ;
         if ( old == cells2 )
         {
            old = cells1  ;
            new = cells2  ;
         }
         else
         {
            old = cells2  ;
            new = cells1  ;
         }
         break  ;
      }
      break  ;
   }
 }


 void stoploop()
 {
   if ( ! stopflag )
   {
      stopflag = 1  ;
      while ( stopflag )
      {
         Wait( 1 << window->UserPort->mp_SigBit )  ;
         handlemsg()  ;
      }
   }
 }


 short stringreq( t , l )
   UBYTE    *t  ;
   short    l  ;
 {
   struct Window  *rw  ;
   struct Gadget  *g  ;
   long           sig  ;

   rnw.Title = t  ;
   rnw.Screen = screen  ;
   reqinfo.BufferPos = 0  ;
   reqinfo.DispPos = 0  ;
   reqinfo.MaxChars = l  ;

   rw = OpenWindow( &rnw )  ; /* a window but it looks like a requester */
   if ( rw )
   {
      sig =       ( 1 << window->UserPort->mp_SigBit )
               |  ( 1 << rw->UserPort->mp_SigBit )   ;

      while ( class != GADGETUP )
      {
         Wait( sig )  ;
         while ( mes = GetMsg( window->UserPort ) )
         {
            if ( ( mes->Class == MENUVERIFY ) && ( mes->Code == MENUHOT ) )
               mes->Code = MENUCANCEL  ;
            ReplyMsg( mes )  ;
         }
         while ( mes = GetMsg( rw->UserPort ) )
         {
            class = mes->Class  ;
            g = ( struct Gadget * ) mes->IAddress  ;
            ReplyMsg( mes )  ;
         }
      }

      CloseWindow( rw )  ;

      if ( g->GadgetID == 3 )
         return 0  ;
      else
         return 1  ;
   }
   else
   {
      DisplayBeep( screen )  ;
      return 0  ;
   }
 }




