/*
      Demon.c        Gary Teachout     August  1989

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

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

 #define DPEN        15
 #define BPEN        14
 #define SIZEX       160
 #define SIZEY       94
 #define CMAX        16
 #define PLANES      4
 #define COLORS      12

 struct IntuitionBase      *IntuitionBase  ;
 struct GfxBase            *GfxBase  ;

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

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

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

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

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

 UBYTE   ctab[ 16 ][ 3 ] =
 {
   { 9 , 1 , 0 } ,
   { 12 , 5 , 0 } ,
   { 15 , 0 , 0 } ,
   { 10 , 0 , 8 } ,

   { 8 , 0 , 15 } ,
   { 0 , 0 , 15 } ,
   { 0 , 8 , 5 } ,
   { 0 , 15 , 0 } ,

   { 8 , 8 , 8 } ,
   { 15 , 15 , 0 } ,
   { 15 , 5 , 0 } ,
   { 6 , 2 , 0 } ,

   { 0 , 0 , 0 } ,
   { 0 , 0 , 0 } ,
   { 0 , 0 , 0 } ,
   { 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( void )  ;
 void scramble( void )  ;


 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 < 16 ; i ++ )
      SetRGB4( &screen->ViewPort , i , ctab[ i ][ 0 ] ,
                                       ctab[ i ][ 1 ] ,
                                       ctab[ i ][ 2 ] )  ;

   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 , "Cyclic Space Automaton" , 22 )  ;
   Move( window->RPort , 60 , 102 )  ;
   Text( window->RPort , "by  Gary Teachout" , 17 )  ;
   Move( window->RPort , 60 , 130 )  ;
   Text( window->RPort , "Press any key to exit" , 21 )  ;

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

   ShowTitle( screen , TRUE )  ;

   old = cells1  ;
   new = cells2  ;

   scramble()  ;
   display()  ;

   old = cells2  ;
   new = cells1  ;

   for ( ; ; )
   {
      stepauto()  ;

      if ( mes = GetMsg( window->UserPort ) )
      {
         ReplyMsg( mes )  ;
         break  ;
      }

      display()  ;

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

      if ( mes = GetMsg( window->UserPort ) )
      {
         ReplyMsg( mes )  ;
         break  ;
      }
   }

   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    t , *oc , *n , *yp , *ym , *xp , *xm , *yy  ;

   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  ;

      t = ( *oc + 1 ) % COLORS  ;

      if (     ( *yp == t )
            || ( *ym == t )
            || ( *( yy + SIZEX - 1 ) == t )
            || ( *xp == t ) )
         *( n ++ ) = t  ;
      else
         *( n ++ ) = *oc  ;

      yp ++  ;
      ym ++  ;
      xp ++  ;
      xm = yy  ;
      oc ++  ;

      for ( x = 2 ; x < SIZEX ; x ++ )
      {
         t = ( *oc + 1 ) % COLORS  ;

         if (     ( *yp == t )
               || ( *ym == t )
               || ( *xm == t )
               || ( *xp == t ) )
            *( n ++ ) = t  ;
         else
            *( n ++ ) = *oc  ;

         yp ++  ;
         ym ++  ;
         xp ++  ;
         xm ++  ;
         oc ++  ;
      }

      xp = yy  ;

      t = ( *oc + 1 ) % COLORS  ;

      if (     ( *yp == t )
            || ( *ym == t )
            || ( *xm == t )
            || ( *xp == t ) )
         *( n ++ ) = t  ;
      else
         *( n ++ ) = *oc  ;

      yy += SIZEX  ;
      oc ++  ;
   }
 }


 void display()
 {
   USHORT   x , y , d , i , j , k , m , *wp[ PLANES ] , *wpc[ PLANES ]  ;
   UBYTE    cm , *c ;

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

   for ( y = 0 ; y < SIZEY ; y ++ )
   {
      for ( x = 0 ; x < SIZEX ; x += 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  ;
      }
      wp[ 0 ] += i  ;
      wp[ 1 ] += i  ;
      wp[ 2 ] += i  ;
      wp[ 3 ] += i  ;
      wpc[ 0 ] += i  ;
      wpc[ 1 ] += i  ;
      wpc[ 2 ] += i  ;
      wpc[ 3 ] += i  ;
   }
 }


 UBYTE random()
 {
   #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 ) ( ( COLORS * rr ) / RANDCOMP )  ;
 }


 void scramble()
 {
   short    x , y  ;

   for ( y = 0 ; y < SIZEY ; y ++ )
   {
      for ( x = 0 ; x < SIZEX ; x ++ )
      {
         *( new + x + ( y * SIZEX ) ) = random()  ;
      }
   }
 }


