// C6Applet.java
//
// gg  gg    ggg  ggg   gggg   gg    66   g  g  ggg   gggg   ggg   gg   gg
// g   g    g     g  g  g     g  g  6     g  g  g  g  g     g       g    g
//  g   g   g     ggg   ggg   gggg  666   g  g  ggg   ggg    gg    g    g
//          g     g g   g     g  g  6  6  g  g  g g   g        g
//           ggg  g  g  gggg  g  g   66    gg   g  g  gggg  ggg
//
//
//  MR June 2000
//
//  Menno Rubingh (c) 2000
//  http://www.rubinghscience.org/
//

//  ----------------------------------------------------------------------
//  Copyright (C) 2000 Menno Rubingh
// 
//  This source code / program is free software; you are allowed to 
//  redistribute it and/or to modify it under the terms of the GNU Public 
//  Licence (GNU GPL) as published by the Free Software Foundation.  See the 
//  file 'COPYING' which should have accompanied this source file.  
//
//  This program comes with ABSOLUTELY NO WARRANTY.
//  ----------------------------------------------------------------------



import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;




// --------------------------------------------------------------------------

//
// Class C6Rand :  Random-number generator that returns integers 
//                 within a specified range [0,m) where m > 0.
//
// Menno Rubingh (c) 1999, 2000
//

//
// Note that ''scaling down'' the return values of Random.nextInt() with the
// ''%'' operator (i.e., 
//
//             return ( Math.abs(nextInt()) % m );
//
// ) does NOT work well at all, since the random generator in Java's Random 
// class, like 99.9% of all random generators, is made in such a way that the
// returned integer **VALUES** are distributed uniformly over the interval
// between the minimum and maximum return values.  The values obtained from
// the ''%'' application quoted above, for an arbitrary choice of 'm',  will 
// therefore very probably *NOT* be distibited uniformly over the interval
// [0,m).
//
// See William H. Press et al., _Numerical Recepes in Fortran_, 2nd Edition,
// Cambridge University Press, 1992 (ISBN 0-521-43064-X), Chapter 7 ''Random 
// Numbers'', section 7.1, p. 268.
// 

class C6Rand extends Random
	{
	//Max. positive value of Java 4-byte 'int'.
	static private final int RMAX = (256*256*256*(256/2)) - 1;

	private int uintRand() //Return random integer in [0,RMAX)
		{
		return ( Math.abs( nextInt() ) );
		}

	public int n_rand( int m ) //Return random integer in [0,m)
		{
		int K = ( RMAX / m ) + 1;
		return ( uintRand() / K );
		}

	}


// --------------------------------------------------------------------------

//
// Class C6Gene :  General-purpose class for n-bit gene. 
//
//  Implements general operations like mutation, crossover, etc.
//

//  NOTE:  For 'C6Gene(g2)', 'copy(g2)' and 'mingleMutual(g2,r), the two
//         C6Gene objects ''this'' and 'g2' must contain the same length
//         ''bits'' array, otherwise the results are ''strange''.

class C6Gene
	{
	protected boolean[] bits;

	public C6Gene( int n )
		// Initialize to length of n bits, and initialize with
		//  all bits to value zero.
		{
		bits = new boolean[n];
		for ( int i = 0; i < n; i++ ) 
			{ bits[i] = false; }
		}

	public C6Gene( C6Gene g2 )
		// Copy constructor : copy contents of g2 to ''this''.
		{
		bits = new boolean[ g2.bits.length ];
		copy( g2 );
		}


	// --------

	public void initRand( C6Rand r )
		// Re-init contents of gene with random values,
		//  keeping it at same # of bits.
		{
		for ( int i = 0; i < bits.length; i++ )
			{ bits[i] = ( r.n_rand( 2 ) == 1 ); }
		}


	// --------


	public void copy( C6Gene g2 )
		// Copy contents of g2 into ''this''.
		{
		for ( int i = 0; i < bits.length && i < g2.bits.length; i++ )
			{ bits[i] = g2.bits[i]; }
		}


	public void mutate( C6Rand r )
		// Randomly flip one of the n bits in the gene.
		{
		int whichone = r.n_rand( bits.length );

		bits[whichone] = ! bits[whichone];
		}

	

	public void mingleMutual( C6Gene g2, C6Rand r )
		{
		for ( int i = 0; 
		   i < this.bits.length && i < g2.bits.length; 
		   i++ )
			{
			if ( r.n_rand(2) == 1 )
				{
				boolean b1 = this.bits[i];
				boolean b2 = g2.bits[i];
				this.bits[i] = b2;
				g2.bits[i] = b1;
				}
			}
		}
	public void mingleIntoThis( C6Gene g2, C6Rand r )
		{
		for ( int i = 0; 
		   i < this.bits.length && i < g2.bits.length; 
		   i++ )
			{
			if ( r.n_rand(2) == 1 )
				{
				this.bits[i] = g2.bits[i];
				}
			}
		}




	// --------


	// ''Read-only'' access functions :

	public boolean getBooleanValue( int where )
		{
		return ( 
		   ( 0 <= where && where < bits.length ) ?
		   bits[where] : 
		   false );
		}


	public int getIntValue( int start, int len )
		// Get numerical value, as an 'int', of the gene bits
		//  beginning at bit nr. ''start'' (numbering from 0), 
		//  and continuing over ''len'' bits.
		{
		int val = 0;
		for ( int i = start; i < start+len && i < bits.length; i++ )
			{
			val *= 2; 
			if ( bits[i] ) { val += 1; }
			}
		return val;
		}

	}


// --------------------------------------------------------------------------

//
// Class C6Crea6ure :  Creature with 6-bit gene. 
//

//
//                +----+----+ ----+----+ ----+----+
// Gene layout :  | c1 | c2 |  d1 | d2 |  s1 | s2 |
//                +----+----+ ----+----+ ----+----+
//         index     0    1     2    3     4    5
// 
//  c1, c2 = 2-bit creature color.
//  d1, d2 = Behaviour when other creature is colored different.
//  s1, s2 = Behaviour when other creature is colored same.
// 
//  d1 and s1 = ''Attack behaviour'' :  0 = flee, 1 = attack.
//  d2 and s2 = ''Mating behaviour'' :  0 = crossover, 1 = transplant.
//

class C6Crea6ure 
	{
	// class constants
	public static final int GENELEN = 6;

	// constants to help interpret ''dirn'' values
	public static final int DX[] = { 1, 0, -1, 0 };
	public static final int DY[] = { 0, 1, 0, -1 };



	// member variables

	protected C6Gene gene;
	protected int x, y;
	protected int dirn; //Direction, in {0,1,2,3}

	protected boolean pa; //Previous Action
	                      // (true==attack, false==other).


	// --------

	public C6Crea6ure()
		// Initialize with gene of length of 6 bits, as yet with 
		//   blank gene contents, position and direction.
		{
		gene = new C6Gene( GENELEN ); 
		dirn = 0;
		x = 0;
		y = 0;

		pa = false;
		}

	// --------


	public boolean getPA() { return pa; }
	public void setPA( boolean panew ) { pa = panew; }
	


	public void initGene( C6Rand r )
		// Re-initialize gene with random gene contents.
		{
		gene.initRand( r );
		}
	public void initPosAndDir( int xmax, int ymax, C6Rand r )
		// Re-initialize 'x' to random x-position in [0,xmax),
		//  re-init 'y' to random y-position in [0,ymax),
		//  and re-init 'dirn' to random direction in {0,1,2,3}.
		{
		x = r.n_rand( xmax );
		y = r.n_rand( ymax );
		dirn = r.n_rand( 4 );
		}


	public void mutate( C6Rand r )
		{ 
		gene.mutate( r ); 
		}



	// --------


	public int getX()
		{ return x; }
	public int getY()
		{ return y; }


	// --------


	public void randturn( C6Rand r )
		// Let creature turn 90 degrees left or right w.r.t. its
		//  current 'dirn', 50-50 chance.
		{
		if ( r.n_rand(2) == 0 )
			{ dirn = ( dirn + 1 ) % 4; } //turn right
		else
			{ dirn = ( dirn + 3 ) % 4; } //turn left
		}


	public int xAhead()
		{
		return ( x + DX[dirn] );
		} 
	public int yAhead()
		{
		return ( y + DY[dirn] );
		} 

	public void moveTo( int xdest, int ydest )
		{
		x = xdest;
		y = ydest;
		}


	// --------


	protected int getColorValue()
		// Return color value in {0,1,2,3}.
		{ return gene.getIntValue( 0, 2 ); }

	protected int getAttackBehaviourValue( boolean same )
		// Return value in {3,2,1,0}
		{ return gene.getIntValue( (same ? 4 : 2), 2 ); }



	public boolean attack( C6Crea6ure c2, 
		C6Rand r )
		//
		// Give the the ''this'' creature the ''opportunity'' to
		//  attack creature 'c2', and see what the ''this'' creature
		//  does.
		//
		// Return false if ''this'' doesn't ''want'' to attack.
		// Return true if ''this'' does attack 'c2' -- in this
		//  case, the genetic information in 'c2', and possibly
		//  also of ''this'' is changed.
		//
		{
		boolean same = ( this.getColorValue() == c2.getColorValue() );

		int abv = this.getAttackBehaviourValue( same );
		if ( abv == 3 /*11*/ )
			{
			c2.gene.copy( this.gene );
			return true;
			}
		else if ( abv == 2 /*10*/ )
			{
			c2.gene.mingleIntoThis( this.gene, r );
			return true;
			}
		else if ( abv == 1 /*01*/ )
			{
			c2.gene.mingleMutual( this.gene, r );
			return true;
			}
		else /* case 00 */
			{
			return false; //No attack executed (flee)
			}
		}


	}//<End-class-C6Crea6ure>



// --------------------------------------------------------------------------

//
// Class C6World :  Container for the crea6ures, and simultaneously 
//                a ''canvas'' to draw them (and the ''chessboard they
//                walk around on) on the screen.
//

// 
// All the public 'C6World' member functions (methods) that affect the array 
// of crea6ures maintained in the 'C6World' object and that affect the data 
// associated with any single crea6ure, plus also the public 'C6World' member 
// functions that do drawing, are all 'synchronized': i.e., only one thread 
// at a time can enter and run code in only one single one of these 
// 'synchronized' member functions of the same 'C6World' object.  This is 
// implemented in this way not only to keep the ''data integrity'' array of 
// crea6ures maintained by the 'C6World' object, but also to keep what is 
// shown (drawn) on the screen of the crea6ures always completely updated 
// and correct. 
//


class C6World extends Canvas
	{
	// Constant maximum size (width and height) of chessboard.  The 
	//  chessboard is ALWAYS square.
	public static final int MAXWSIZE = 15;


	// Member variables

	protected C6Rand r;    // Reference to the central random generator 
	                       //  used by everything in the ''C6World''.

	protected int ncr;     // Current # of creatures in C6World.
	protected int ixcurcr; // Index in crarr of next creature to be moved.
	protected C6Crea6ure[] crarr;

	protected int wsize;  // Current size of the ''chessboard''.


	// --------


	public C6World( C6Rand r0, int sz0 )
		// Initialize to chessboard size 'sz0', and with 'r0'
		//  as the random generator used.
		// But as yet with NO creatures in the C6World (= empty
		//  array 'crarr[]').
		{
		r = r0;
		wsize = sz0;

		// Initialize the crarr[] array object to suffice for the
		//  max number of crea6ures in the max-size chessboard,
		//  i.e. half the squares on that max-size chessboard
		//  occupied. 
		crarr = new C6Crea6ure[ (MAXWSIZE * MAXWSIZE)/2 ];

		ncr = 0;
		ixcurcr = 0;

		//setBackground( Color. .... C6Crea6ure.BCKCOLOR );
		}



	// --------


	public synchronized void reinitAllGenes()
		{
		for ( int i = 0; i < ncr; i++ )
			{ crarr[i].initGene( r ); }
		}

	public synchronized void mutateOne()
		{
		if ( ncr <= 0 ) { return; }
		int whichone = r.n_rand( ncr );
		crarr[whichone].mutate( r );
		}


	// --------


	public synchronized int getNCr() 
		{ return ncr; }    // Return current # of crea6ures

	public synchronized int getWSize()
		{ return wsize; }  // Return current chessboard size


	// --------


	protected C6Crea6ure squareContents( int x, int y )
		// Return null if square (x,y) in C6World is empty or
		//  if (x,y) is outside of the ''chessboard''; 
		//  otherwise return the creature on that square.
		{
		if ( ! ( 0 <= x && x < wsize && 0 <= y && y < wsize ) ) 
			{ return null; }

		C6Crea6ure rv = null;
		for ( int i = 0; i < ncr; i++ )
			{
			if ( crarr[i].getX() == x && crarr[i].getY() == y )
				{ rv = crarr[i]; }
			}
		return rv;
		}


	// --------

	public synchronized boolean insertNewCrea6ure()
		// Insert new extra crea6ure at random position that
		//  is still empty, and with random gene and direction.
		// Refuse to add crea6ure if C6World already quite ''full''.
		{
		if ( ncr >= crarr.length ) { return false; } /*array full*/
		if ( ncr >= (wsize*wsize)/2 ) { return false; }

		C6Crea6ure cnew = new C6Crea6ure();
		cnew.initGene( r ); 
		do //find a still-empty square
			{ 
			cnew.initPosAndDir( wsize, wsize, r ); 
			}
		while( squareContents( cnew.getX(), cnew.getY() ) != null );

		crarr[ncr] = cnew;
		ncr++;

		return true; /*success*/
		}

	public synchronized boolean deleteLastCrea6ure()
		{
		if ( ncr <= 0 ) { return false; }

		crarr[ncr-1] = null;
		ncr--;
		if ( ixcurcr > ncr-1 ) { ixcurcr = 0; }

		return true; /*success*/
		}


	
	// --------


	public synchronized void reinitAllPositions()
		{
		// (Note that the coding of this function is an ugly, dirty, 
		//   implementation-specific trick.)

		// Step 1: Move everything ''outside of the board'' :-):-)
		for ( int i = 0; i < ncr; i++ )
			{ 
			crarr[i].moveTo( -1, -1 );
			}

		// Step 2: Put everything back in
		int ncrOrig = ncr;
		ncr = 0; //(to fool our func squareContents())
		for ( int i = 0; i < ncrOrig; i++ )
			{
			C6Crea6ure c = crarr[i];
			do //find a still-empty square
				{ 
				c.initPosAndDir( wsize, wsize, r ); 
				}
			while( squareContents( c.getX(), c.getY() ) != null );
			
			ncr++;
			}
		}

	public synchronized void resizeWorld( int newsize )
		{
		if ( ! ( 5 <= newsize && newsize < MAXWSIZE ) ) 
			{ return; }

		wsize = newsize;

		while ( ncr > (wsize*wsize)/2 )
			{ deleteLastCrea6ure(); }

		reinitAllPositions();
		}


	// --------



	public synchronized boolean doOneMove( boolean doPaint )
		// Move one (the ''current'') creature, 
		//  perform the necessary ''incremental'' repainting on 
		//  the screen, and update ixcurcr.
		// If 'doPaint' == null, then don't do any drawing.
		// Return true if the move executed is the
		//  last move in a ''cycle'' through all the
		//  creatures on the chessboard.
		{
		if ( ncr <= 0 ) { return true; /*nothing to do*/ }

		Graphics g = getGraphics();
		C6Crea6ure curcr = crarr[ixcurcr];
		boolean ppa = curcr.getPA();

		curcr.setPA( false );

		// Erase old picture of curcr on screen
		if ( doPaint ) { drawCrea6ure( g, curcr, false ); }

		// See what's ahead
		int xdest = curcr.xAhead();
		int ydest = curcr.yAhead();

		// Take appropriate action

		if ( ppa ||
		   ! ( 0 <= xdest && xdest < wsize &&
		   0 <= ydest && ydest < wsize ) )
			{
			//
			// PREVOUS STATE IS 'PA'==TRUE, or
			// WALL AHEAD ==> turn.
			//
			curcr.randturn( r );
			}
		else 
			{
			C6Crea6ure othercr = squareContents( xdest, ydest );

			if ( othercr == null )
				{
				//
				// EMPTY SQUARE AHEAD ==> advance, or
				//   with probability 1/5 turn.
				//
				// (The small turn probability serves to 
				//   increase the ''randomness'' with which 
				//   creatures run into each other and 
				//   exchange information.)
				//
				if ( r.n_rand(5) == 0 )
					{ curcr.randturn( r ); }
				else
					{ curcr.moveTo( xdest, ydest ); }
				}
			else
				{
				//
				// OTHER CREATURE AHEAD ==> perform gene-coded
				//        action.
				//
				if ( curcr.attack( othercr, r ) )
					{
					// Attack executed ?  ==>	
					//  set 'PA', and
					//  redraw othercr, too.

					curcr.setPA( true );

					if ( doPaint )
					   { drawCrea6ure( g, othercr, true ); }
					}
				else
					{
					//no attack ==> curcr turns instead.
					curcr.randturn( r );
					}
				}
			}

		// Redraw curcr in its new state/position
		if ( doPaint ) { drawCrea6ure( g, curcr, true ); }

		// Update ixcurcr
		ixcurcr = ( ixcurcr + 1 ) % ncr;

		return ( ixcurcr == 0 );
		}



	// --------

	//
	// Graphics stuff
	//

	private static final Color BCKCOLOR = Color.gray;

	private static final Color colorarr[] = 
		{ Color.red, Color.yellow, Color.green, Color.blue };


	private static final int BITPERIOD = 3;
	private static final int BITWIDTH = 2;

	private static final int GHALFWIDE = BITPERIOD * (C6Crea6ure.GENELEN/2);
	private static final int GHALFHIGH = 4; 


	private static final int BODYRADIUS = GHALFWIDE + 2;
	private static final int NOSERADIUS = BODYRADIUS / 3;


	public static final int SQSZ = 2*BODYRADIUS + 4;
	        //Diameter of the square in the ''C6World'' chessboard 
	        // which a creature occupies.

	public static final int WMARGIN = 3; 

	public static final int CHBMAXSIZE = SQSZ*MAXWSIZE + 2*WMARGIN; 
	        // Chessboard total max size in pixels



	public Dimension preferredSize()
		// This function is necessary to get the FlowLayout in the 
		//  constructor of our C6Applet work correctly with
		//  our ''C6World'' Canvas.
		{
		return ( new Dimension( CHBMAXSIZE, CHBMAXSIZE ) );
		}
	public Dimension minimumSize()
		{
		return ( new Dimension( CHBMAXSIZE, CHBMAXSIZE ) );
		}
	

	protected void drawCrea6ure( Graphics g, 
		C6Crea6ure c,
		boolean drawNotErase )
		// Draw crea6ure in its correct (x,y) position on the
		//  chessboard.
		// If 'drawNotErase' == false, then erase the crea6ure (i.e., 
		//  overpaint with background color) instead of drawing it.
		{
		int or = ( CHBMAXSIZE - (SQSZ-1)*wsize ) / 2;
		   // 'or' = graphics pixel coordinate within Canvas of the
		   // CENTER of the top-left square of the chessboard
		   // in its current size of 'wsize' squares.  The 
		   // chessboard is drawn centered within the Canvas.
		int x0 = or + ( SQSZ * c.x );
		int y0 = or + ( SQSZ * c.y );
		   // (x0,y0) = graphics pixel coordinate within Canvas
		   // of the center of the chessboard square occupied by
		   // the crea6ure 'c'.


		g.setColor( BCKCOLOR ); //initialize for erase


		// Draw colored body
		if ( drawNotErase )
			{ g.setColor( colorarr[ c.getColorValue() ] ); }
		g.fillOval( 
		   x0-BODYRADIUS-1, 
		   y0-BODYRADIUS-1, 
		   2*BODYRADIUS,
		   2*BODYRADIUS );

		// Draw direction marker (the ''nose'' of the creature)
		if ( drawNotErase )
			{ g.setColor( Color.black ); }
		g.fillOval( 
		   x0 + BODYRADIUS*C6Crea6ure.DX[c.dirn] - NOSERADIUS, 
		   y0 + BODYRADIUS*C6Crea6ure.DY[c.dirn] - NOSERADIUS,
		   2*NOSERADIUS, 
		   2*NOSERADIUS );


		if ( ! drawNotErase )
			{ return; }


		// Draw grey background box for the gene
		g.setColor( Color.gray );
		g.fillRect( 
		   x0 - GHALFWIDE - 1, 
		   y0 - GHALFHIGH - 1, 
		   2*GHALFWIDE+ 1, 
		   2*GHALFHIGH + 2 );

		// Draw the gene
		for ( int i = 0; i < C6Crea6ure.GENELEN; i++ )
			{
			g.setColor( c.gene.getBooleanValue( i ) ? 
			   Color.white : Color.black );

			g.fillRect( 
			   x0 + (i - C6Crea6ure.GENELEN/2)*BITPERIOD, 
			   y0 - GHALFHIGH, 
			   BITWIDTH,
			   2*GHALFHIGH );
			}
		}




	public synchronized void paint( Graphics g )
		{
		int or = ( CHBMAXSIZE - (SQSZ-1)*wsize ) / 2;

		// Draw background for ''world''
		g.setColor( BCKCOLOR );
		g.fillRect( 
		   or - (SQSZ/2) - WMARGIN,
		   or - (SQSZ/2) - WMARGIN, 
		   wsize*SQSZ + 2*WMARGIN, 
		   wsize*SQSZ + 2*WMARGIN );

		// Draw the creatures
		for( int i = 0; i < ncr; i++ )
			{
			drawCrea6ure( g, crarr[i], true );
			}
		}

	}



// --------------------------------------------------------------------------


//
// Class C6AnimationManager :  Thread which autonomously runs the continuous
//                             automatic updating of the movement of the
//                             crea6ures.
//

class C6AnimationManager extends Thread
	{
	private C6Applet ourapp;
	private int cycleTime;
	private boolean repaintMode; //true ==> repaint each individual
	                             //         creature move.
	                             //false ==> repaint whole world after
	                             //         a complete cycle through
	                             //         all creatures.

	C6AnimationManager( C6Applet app_init ) //Constructor
		{
		ourapp = app_init;
		cycleTime = 600;
		repaintMode = false;
		}

	public void toggleRepaintMode()
		{
		repaintMode = ! repaintMode;
		}

	public void run()
		{
		while( true )
			{
			int nCr = ourapp.world.getNCr();
			int n = ( nCr == 0 ? 1 : nCr );

			try 
				{ 
				sleep( cycleTime / n ); 
				}
			catch ( InterruptedException e ) 
				{ 
				//empty
				}

			boolean last;
			last = ourapp.world.doOneMove( repaintMode );
			if ( last && ( repaintMode == false ) )
				{
				ourapp.world.repaint();
				}
			if ( last )
				{
				ourapp.iLabel ++ ;
				if ( ourapp.iLabel > 100 )
					{ ourapp.iLabel = 0; }
				ourapp.ourLabel.setText( "[" + 
				   ourapp.iLabel + "]");
				}
			}
		}

	}


// --------------------------------------------------------------------------


public class C6Applet extends Applet
	{
	// Constants

	//Fixed size of total ''Frame'' of our Applet:
	public static final int APPXSIZE = C6World.CHBMAXSIZE + 10;
	public static final int APPYSIZE = C6World.CHBMAXSIZE + 75;

	//Initial chessboard size:
	private static final int WSZINI = 10;



	// Instance variables

	public C6Rand r; // Use *ONE* central random generator.

	private C6AnimationManager am;
	public C6World world;
	
	public int iLabel = 0;
	Label ourLabel;


	public C6Applet()  // Constructor
		{
		am = null;
		//am = new C6AnimationManager( this );

		r = new C6Rand(); // Initialize our central random generator.


		// Add buttons and add our ''C6World'' Canvas:

		setLayout( new FlowLayout() );

		add( new Button( "reinit" ) );
		add( new Button( "world++" ) );
		add( new Button( "world--" ) );
		add( new Button( "ncrea++" ) );
		add( new Button( "ncrea--" ) );
		add( new Button( "mutate1" ) );
		add( new Button( "ff" ) ); //''Fast Forward''
		add( new Button( "vmode" ) ); 
		//add( new Label( APPXSIZE + "x" + APPYSIZE ) );
		add( ourLabel = new Label( "[" + iLabel + "]" ) );

		world = new C6World( 
		   r,              //Random generator used by 'world'
		   WSZINI );       //initial chessboard size

		add( world );
		}


	// ------

	
	public void init() // Web browser tells applet to initialize
		{
		resize( APPXSIZE, APPYSIZE );

		// Put some Creat6ures into the C6World :
		for ( int i = 0; i < 10; i++ )
			{ world.insertNewCrea6ure(); }
		}

	public void start() // Web browser tells applet to start
		{
		// Spawn off 'C6AnimationManager' subprocess:
		am = new C6AnimationManager( this );
		am.start();
		}

	public void stop() // Web browser tells applet to stop
		{
		// Kill the 'C6AnimationManager' subprocess again:
		am.stop();
		am = null;
		}


	// ------


	//
	// Our ''Event Handler'' :
	// 

	public boolean action( Event e, Object arg )
		{
		if ( e.target instanceof Button ) 
			{
			String label = (String)arg;

			if ( label.equals( "reinit" ) ) 
				{
				world.reinitAllGenes();
				world.reinitAllPositions();
				}

			else if ( label.equals( "world++" ) )
				{
				world.resizeWorld( world.getWSize() + 1 );
				}
			else if ( label.equals( "world--" ) )
				{
				world.resizeWorld( world.getWSize() - 1 );
				}
	
			else if ( label.equals( "ncrea++" ) )
				{
				world.insertNewCrea6ure();
				}
			else if ( label.equals( "ncrea--" ) )
				{
				world.deleteLastCrea6ure();
				}

			else if ( label.equals( "mutate1" ) )
				{
				world.mutateOne();
				}

			else if ( label.equals( "ff" ) ) 
				{
				for ( int i = 0; 
				    i < ( 10 * world.getNCr() );
				    i++ )
					{
					world.doOneMove( false ); 
					}
				}

			else if ( label.equals( "vmode" ) )
				{
				am.toggleRepaintMode();
				}

			world.repaint();

			return true;
			}
		return false;
		}


	// ------
	
	static public void main( String args[] )
		{
		C6Applet ourapp = new C6Applet();
		ourapp.init();

		Frame f = new Frame( "-- C6Applet --" );
		f.resize( APPXSIZE, APPYSIZE );

		f.add( "Center", ourapp );
		f.show();

		ourapp.start();
		}

	}




