/*
      Life.c        Gary Teachout     August  1989

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

 #include <exec/types.h>
 #include <exec/memory.h>
 #include <intuition/intuition.h>

 #define FPEN        1
 #define DPEN        1
 #define BPEN        0
 #define SIZEX       160
 #define SIZEY       94
 #define PLANES      1

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

 struct IntuitionBase      *IntuitionBase  ;
 struct GfxBase            *GfxBase  ;

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

 ULONG   class  ;
 USHORT  code  ;

 struct  NewScreen   ns =
 {
   0 , 0 , 320 , 200 , PLANES , DPEN , BPEN , 0 ,
   CUSTOMSCREEN , NULL , NULL , NULL , NULL
 }  ;

 UBYTE   *title[ 2 ] =
 {
   "  Life" ,
   "  Edit Seed"
 }  ;

 struct  NewWindow
   nw =
   {
      0 , 0 , 320 , 200 , DPEN , BPEN ,
      MENUPICK | MENUVERIFY | MOUSEBUTTONS ,
      SMART_REFRESH | ACTIVATE | BACKDROP | BORDERLESS ,
      NULL , NULL , NULL , 
      NULL , NULL , 0 , 0 , 0 , 0 , CUSTOMSCREEN
   }  ;

 USHORT chip   pointer[ 20 ] =
 {
   0x0000 , 0x0000 ,
   0x8000 , 0x0000 ,
   0xc000 , 0x0000 ,
   0xa000 , 0x0000 ,
   0x9000 , 0x0000 ,
   0x8800 , 0x0000 ,
   0x8400 , 0x0000 ,
   0x8000 , 0x0000 ,
   0x0000 , 0x0000 ,
   0x0000 , 0x0000
 }  ;

 struct Menu
   menulist[ 2 ] =
   {
      {  NULL , 1   , 0 , 90 , 8  , MENUENABLED , " Control" , NULL } ,
      {  NULL , 1   , 0 , 90 , 8  , MENUENABLED , " Edit" , NULL }
   }  ;

 struct menubox 
   controlmenu[ 5 ] =
   {
      {
         {  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 , 'G' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Go" , NULL } 
      } ,
      {
         {  NULL , 0 , 22 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'C' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Custom Seed" , NULL } 
      } ,
      {
         {  NULL , 0 , 33 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP ,
            0 , NULL , NULL , 0 , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Random Seed" , NULL } 
      } ,
      {
         {  NULL , 0 , 50 , 140 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'Q' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Quit" , NULL } 
      } 
   } ,
   seedsub[ 3 ] =
   {
      {
         {  NULL , 130 , 0  , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , '1' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Small" , NULL } 
      } ,
      {
         {  NULL , 130 , 11 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ,
            0 , NULL , NULL , '2' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Medium" , NULL } 
      } ,
      {
         {  NULL , 130 , 22 , 120 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , '3' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 10 , 2 , NULL , "Large" , NULL } 
      }
   } ,
   editmenu[ 4 ] =
   {
      {
         {  NULL , 0 , 0  , 170 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ | CHECKIT ,
            0x02 , NULL , NULL , 'A' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "Alive (White)" , NULL } 
      } ,
      {
         {  NULL , 0 , 11 , 170 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ | CHECKIT ,
            0x01 , NULL , NULL , 'D' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "Dead  (Black)" , NULL } 
      } ,
      {
         {  NULL , 0 , 22 , 170 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'B' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "Blank Field" , NULL } 
      } ,
      {
         {  NULL , 0 , 33 , 170 , 11 , 
            ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ ,
            0 , NULL , NULL , 'C' , NULL , NULL } ,
         {  FPEN , BPEN , JAM1 , 25 , 2 , NULL , "Continue" , NULL } 
      } 
   }  ;

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

 UBYTE   *cells1 , *cells2 , *old , *new  ;

 short   stopflag = 0 , reseed = 0  ;

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

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

 void main()
 {
   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()  ;

   SetRGB4( &screen->ViewPort , 0 , 0 , 0 , 0 )  ;
   SetRGB4( &screen->ViewPort , 1 , 15 , 15 , 15 )  ;

   nw.Screen = screen  ;
   window = OpenWindow( &nw )  ;
   if ( ! window )
      cleanup()  ;

   SetBPen( window->RPort , 0 )  ;
   SetAPen( window->RPort , 15 )  ;
   Move( window->RPort , 60 , 90 )  ;       
   Text( window->RPort , "The Game of Life" , 16 )  ;
   Move( window->RPort , 60 , 102 )  ;
   Text( window->RPort , "by  Gary Teachout" , 17 )  ;

   cells1 = AllocMem( 2 * SIZEX * SIZEY , MEMF_FAST | MEMF_CLEAR )  ;
   if ( ! cells1 )
      cleanup()  ;
   cells2 = cells1 + ( SIZEX * SIZEY )  ;

   menulist[ 0 ].FirstItem = &controlmenu[ 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[ 3 ].item.SubItem = &seedsub[ 0 ].item  ;
      seedsub[ 0 ].item.ItemFill = ( APTR ) &seedsub[ 0 ].text  ;
      seedsub[ 0 ].item.NextItem = &seedsub[ 1 ].item  ;
      seedsub[ 1 ].item.ItemFill = ( APTR ) &seedsub[ 1 ].text  ;
      seedsub[ 1 ].item.NextItem = &seedsub[ 2 ].item  ;
      seedsub[ 2 ].item.ItemFill = ( APTR ) &seedsub[ 2 ].text  ;

   menulist[ 1 ].FirstItem = &editmenu[ 0 ].item  ;

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

   SetMenuStrip( window , &menulist[ 0 ] ) ;
   SetWindowTitles( window , NULL , title[ 0 ] )  ;
   ShowTitle( screen , TRUE )  ;

   Delay( 100 )  ;

   old = cells1  ;
   new = cells2  ;

   scramble( 25 )  ;
   display()  ;

   old = cells2  ;
   new = cells1  ;

   for ( ; ; )
   {
      stepauto()  ;

      display()  ;

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

   }

   cleanup()  ;
 }


 void cleanup()  
 {
   if ( cells1 )
      FreeMem( cells1 , 2 * SIZEX * SIZEY )  ;

   if ( window )
      CloseWindow( window )  ;

   if ( screen )
      CloseScreen( screen )  ;

   if ( GfxBase )
      CloseLibrary( GfxBase )  ;

   if ( IntuitionBase )
      CloseLibrary( IntuitionBase )  ;

   exit()  ;
 }


 void stepauto()
 {
   short    x , y  ;
   UBYTE    *oc , *n , *yp , *ym , *xp , *xm , *yy ,
                       *mm , *pm , *mp , *pp , t  ;

   oc = old  ;
   n = new  ;
   yy = old  ;

   for ( y = 0 ; y < SIZEY ; y ++ )
   {
      yp = old + ( ( ( y + 1 ) % SIZEY ) * SIZEX )  ;
      ym = old + ( ( ( y + SIZEY - 1 ) % SIZEY ) * SIZEX )  ;
      xp = yy + 1  ;
      xm = yy + SIZEX - 1  ;

      mm = ym + SIZEX - 1  ;
      mp = yp + SIZEX - 1  ;
      pm = ym + 1  ;
      pp = yp + 1  ;

      t =      *yp + *ym + *( xp ++ ) + *xm
            +  *mm + *( pm ++ ) + *mp + *( pp ++ )  ;

      if ( *( oc ++ ) )
         *( n ++ ) = ( ( t == 2 ) || ( t == 3 ) ) ? 1 : 0  ;
      else
         *( n ++ ) = ( t == 3 ) ? 1 : 0  ;

      mm = ym  ;
      mp = yp  ;
      yp ++  ;
      ym ++  ;
      xm = yy  ;

      for ( x = 2 ; x < SIZEX ; x ++ )
      {
         t =      *( yp ++ ) + *( ym ++ ) + *( xp ++ ) + *( xm ++ )
               +  *( mm ++ ) + *( pm ++ ) + *( mp ++ ) + *( pp ++ )  ;

         if ( *( oc ++ ) )
            *( n ++ ) = ( ( t == 2 ) || ( t == 3 ) ) ? 1 : 0  ;
         else
            *( n ++ ) = ( t == 3 ) ? 1 : 0  ;
      }

      xp = yy  ;
      pm -= SIZEX  ;
      pp -= SIZEX  ;

      t = *yp + *ym + *xp + *xm + *mm + *pm + *mp + *pp  ;

      if ( *( oc ++ ) )
         *( n ++ ) = ( ( t == 2 ) || ( t == 3 ) ) ? 1 : 0  ;
      else
         *( n ++ ) = ( t == 3 ) ? 1 : 0  ;

      yy += SIZEX  ;
   }
 }


 void display()
 {
   USHORT   x , y , d , i , k , m , *wp , *wpc  ;
   UBYTE    *c ;

   do
   {
      reseed = 0  ;

      i = screen->BitMap.BytesPerRow >> 1  ;
      wp = ( USHORT * ) screen->BitMap.Planes[ 0 ] + ( 12 * i ) ;
      wpc = ( USHORT * ) screen->BitMap.Planes[ 0 ] + ( 13 * i ) ;
      c = new  ;

      for ( y = 0 ; y < SIZEY ; y ++ )
      {
         for ( x = 0 ; x < SIZEX ; x += 8 )
         {
            d = 0  ;
            for ( m = 0xc000 , k = 0 ; k < 8 ; m = m >> 2 , k ++ )
            {
               if ( *( c + k ) & 1 )
                  d |= m  ;
            }
            *wp = d  ;
            wp ++  ;
            *wpc = d  ;
            wpc ++  ;
            c += 8  ;
         }
         wp += i  ;
         wpc += i  ;

         handlemsg()  ;
         if ( reseed )
            break  ;
      }
   } while ( reseed )  ;
 }


 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 scramble( s )
   short    s  ;
 {
   short    x , y  ;

   for ( x = 0 ; x < ( SIZEX * SIZEY ) ; x ++ )
      *( new + x ) = 0  ;

   for ( y = ( SIZEY - s ) >> 1 ; y < ( ( SIZEY + s ) >> 1 ) ; y ++ )
   {
      for ( x = ( SIZEX - s ) >> 1 ; x < ( ( SIZEX + s ) >> 1 ) ; x ++ )
      {
         *( new + x + ( y * SIZEX ) ) = random( 2 )  ;
      }
   }
 }


 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()
 {
   if ( ! MENUNUM( code ) )
   {
      switch ( ITEMNUM( code ) )
      {
      case 0 :
         stoploop()  ;
         break  ;
      case 1 :
         stopflag = 0  ;
         break  ;
      case 2 :
         editmode()  ;
         break  ;
      case 3 :
         switch ( SUBNUM( code ) )
         {
         case 0 :
            scramble( 5 )  ;
            break  ;
         case 1 :
            scramble( 25 )  ;
            break  ;
         case 2 :
            scramble( 80 )  ;
            break  ;
         }
         reseed = 1  ;
         break  ;
      case 4 : /* quit */
         cleanup()  ;
         break  ;
      }
   }
 }


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


 void editmode()
 {
   UBYTE    live = 1  ;
   short    x , y , ox , oy , mx , my  ;

   SetWindowTitles( window , NULL , title[ 1 ] )  ;
   ClearMenuStrip( window )  ;
   display()  ;
   editmenu[ 0 ].item.Flags = editmenu[ 0 ].item.Flags | CHECKED  ;
   editmenu[ 1 ].item.Flags = editmenu[ 1 ].item.Flags & ( ~ CHECKED )  ;
   SetMenuStrip( window , &menulist[ 1 ] )  ;

   SetPointer( window , pointer , 7 , 7 , -1 , 0 )  ;

   stopflag = 0  ;
   reseed = 0  ;
   while( ! reseed )
   {
      Wait( 1 << window->UserPort->mp_SigBit )  ;
      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 :
            switch ( ITEMNUM( code ) )
            {
            case 0 :
               live = 1  ;
               break  ;
            case 1 :
               live = 0  ;
               break  ;
            case 2 :
               for ( y = 0 ; y < ( SIZEY * SIZEX ) ; y ++ )
                  *( new + y ) = 0  ;
               SetRast( window->RPort , 0 )  ;
               break  ;
            case 3 :
               reseed = 1  ;
               break  ;
            }
            break  ;
         case MOUSEBUTTONS :
            if ( code == SELECTDOWN )
            {
               ox = oy = -1  ;
               while ( code != SELECTUP )
               {
                  mx = window->MouseX & 0xfffe  ;
                  my = window->MouseY & 0xfffe  ;
                  x = mx >> 1  ;
                  y = ( my >> 1 ) - 6  ;
                  if ( ( ( x != ox ) || ( y != oy ) ) && ( y >= 0 ) )
                  {
                     *( new + x + ( y * SIZEX ) ) = live  ;
                     if ( live )
                        SetAPen( window->RPort , 1 )  ;
                     else
                        SetAPen( window->RPort , 0 )  ;
                     WritePixel( window->RPort , mx , my )  ;
                     WritePixel( window->RPort , mx + 1 , my )  ;
                     WritePixel( window->RPort , mx , my + 1 )  ;
                     WritePixel( window->RPort , mx + 1 , my + 1 )  ;
                     ox = x  ;
                     oy = y  ;
                  }
                  while ( mes = GetMsg( window->UserPort ) )
                  {
                     if ( mes->Class == MOUSEBUTTONS )
                        code = mes->Code  ;
                     ReplyMsg( mes )  ;
                  }
               }
            }
            break  ;
         }
      }
   }

   ClearPointer( window )  ;

   ClearMenuStrip( window )  ;
   SetMenuStrip( window , &menulist[ 0 ] )  ;
   SetWindowTitles( window , NULL , title[ 0 ] )  ;
 }



