/*
	Module: 	RouterAnimApplet4.java
	Author: 	F. Michael O'Brien (michael@obrienm.com)
	Version:	4.02
	Date:		02 Dec 2000
	Documents:	_projv[nnn].doc
	Notes:		
				written for the Sun JDK 1.2.2
	History:
	28 Sep 2000	- Project started
	01 Oct 2000 - subclass RouterAnimApplet	from AnimApplet
	05 Oct 2000 - fixed adjacency fitness calculation
	20 Oct 2000 - redesign for Ant object swarm
	26 Oct 2000 - begin coding for v1
				- Ant Class started, implemented by RouterAnimApplet
				to provide a Turing machine like independent entity that will
				route the circuit board in parallel with other Ant objects
	27 Oct 2000 - Ant.class using behavior genome model, instead of direct state mapping
				of 64bit genome into 18,446,744,073,709,551,616 states!
	30 Oct 2000 - V5: completed swarm enumeration and display, next ga calculation
	04 Nov 2000 - preparing for demo for 07.201 Presentation
	08 Nov 2000 - working on behavioral link to Ant class
				- adding second layer framework to ivBoard[][]
					added selection, crossover code
	09 Nov 2000	- schema model is too big for memory, I cannot create more than
				33*7 Ants using the current memory model
				- will use byte(8bits signed) for each allelle instead of
				a continuous stream of booleans, will reduce from 262144-65536
	10 Nov 2000	- added 2 layer handling code
	11 Nov 2000 - Modified Ant expression schema, a fixed length deterministic array of bytes
				each allele is read sequentially, the phenotype is expressed as follows
				bit0 = hold/move
				bit1-2 = direction [0:E, 1:N, 2:W, 3:S]
				bit3 = switch layer 0->1 or 1->0
				bit4 = move toward target node (override bits1-2)
				bit5-7 = reserved for future use
	12 Nov 2000 - 
	13 Nov 2000	- fixed memory leak; dont use init()
				adding crossover, recombination code
				- looping freq =13 for 20 pop size, ind 0 is not modified
	15 Nov 2000 - testing gene boundary crossover as a secondary choice
					; completetly infeasible
	16 Nov 2000 - regression fixed, forgot to copy new ant array back to old after crossover
				- do not repick a swarm for crossover
				- v7 fixed error: running load.. instead of reset, in effect running random search
	17 Nov 2000 - added file output of best solutions in grid tabbed text format
				- error fixed: cannot switch layers and move at the same time
					must recompute for switch layers as well
	18 Nov 2000 -
	19 Nov 2000	- Nets with more than 2 nodes are not being solved correctly,
					some nodes need more than one originating ant.
					The ant associated with a node can only stop when adjacent to another
					nodes trace - when there is a valid path between all nodes in a net
	30 Nov 2000 - next version Ant4 combined with RouterAnimApplet4


*/

import java.applet.Applet;
import java.applet.*;
import java.awt.*;
import java.util.StringTokenizer;
import java.lang.Exception;
import java.awt.event.*;
//import java.util.BitSet;
import java.io.*;
//----------------------------------------------------------------
// Implements:		Ant4, Circuit, Board
// Inherits:		AnimApplet
// Implementors:	-
//----------------------------------------------------------------
public class RouterAnimApplet4 extends AnimApplet2 { //implements MouseListener, MouseMotionListener, ActionListener {
private int	ivGridCounter=0;

// for sound
private AudioClip			sound;
//private AudioClip			soundDigit[];

private static int 			izGridHeight;
private static int			izGridWidth;
//				ivFirstTime=0,
//				ivGridGreen2=0;
// for fitness calculation
// turn off display for subsequent init() calls
private boolean				bQuietInit=false;
// keep track of moves for fitness calc, could be part of Ant object
private int					totalMoves[][];

// fitness variables
//private int izMaxAdjacency;	// max fitness attainable, for statistics
// the integer fitness for each swarm
private int 				ivAbsoluteFitness[];
//,ivAbsoluteFitnessPrev[];	// cSwarm
// the floating-point [0-1] probability of selection for each swarm
// as a percentage of dvTotalFitness
private double				dvProbSelection[];
// for recombination
private double				dvProbRecombination[];
// the cumulative probabilities of selection for each swarm
private double				dvCumulativeProbSelection[];
///private double			dvNewPopulationProbSelection[];
// the sum of all the ivAbsoluteFitness[]
private double				dvTotalFitness=0;
private double				dvPrevTotalFitness=0;
private double				dvBestTotalFitnessEver=0;

// the new population indexes
private int					ivNewPopulationIndexes[];
// the new population indexed selected for crossover
private int					ivNewPopulationIndexedForCrossover[];
private int					ivCrossoverPopSize;

// keep the best swarm of all time
private static final int	izMaximumSwarmFitnessPossible=10000000;
private int					ivBestSwarmFitnessEver=0;
private int					ivBestSwarmFitness=0;
private int					ivWorstSwarmFitness=izMaximumSwarmFitnessPossible;
private int					ivWorstSwarmFitnessEver=izMaximumSwarmFitnessPossible;

private Ant4 				bestAntSwarm[];
// start at a level that would be infeasible
private int					ivBestTime=32767;
private int					ivBestTimePrev=32767;
private int					iaSwarmTimeToSolution[];

// to keep track of the fitness number for each swarm
//private int	ivTopIndividualsFitness[];
// the number of swarms that can mate
private static double 		dzProbCrossover;
private static double 		dzProbMutation;
// wether we crossover all genes at the same point or all randomly
private static boolean 		bvCrossoverPointEqual;
// set flag for mutation of all bits=1, mutation of crossover only=0
private static boolean 		bvMutateAllBits;

private int 				ivBestGeneration=0;
//private int 				izDeviceNodes=0;

protected boolean			singleStepSimulation=false;
//protected boolean			singleStepSimulation=true;
protected boolean			advanceSingleStep=false;

// vars used to blink objects
protected boolean 			bvOddFrame=true;
protected int 				ivFrameCount=0;
protected static final int	ivFrameFlipCount=10;
// counter for frames within current gen
protected int 				ivFrames=0;
// counter for current cSwarm simulation frame count
protected int 				ivFrames2=0;
// show statistics
protected static boolean	bvShowStatistics=true;

//protected int 		counter=0;

// constants
// fitness criteria
/*	private int		izFitnessCriteriaLowerUnconnected=1;
	private int		izFitnessCriteriaLowerConnected=4;
	private int		izFitnessCriteriaUpperUnconnected=1;
	private int		izFitnessCriteriaUpperConnected=4;
	private int		izFitnessCriteriaBothUnconnected=4;
	private int		izFitnessCriteriaBothConnected=16;
*/
private long 				lvGeneration=0;
private int 				ivIndividual=0;

// constants needed for colors
private int 				izSpacing,izSpacing2,izSpacing3
							,izSpacing4,izSpacing6;
private Color 				color_top, color_bottom, 
							color_both, color_none;
private Color 				color_connect1, color_connect2, color_connect3;
private Color 				color_connect3_lower, color_connect3_upper, 
							color_connect3_both, color_device,color_board_edge;
private Color 				color_found_source,color_found_target,
							color_seek_source,color_seek_target;
private Color 				color_trace_both[];
private Color 				color_trace_upper[];
private Color 				color_trace_lower[];
//private static final int 		ndir[]={9,8,11,10,5,4,7,6};
private static final int	xdir[]={1,0,-1,0};
private static final int	ydir[]={0,-1,0,1};

// keep track of vars for circ (target found) anim
private int					ivBullsEye[][];
private boolean 			bvDestFound[][];
private static final int	izMaxBullsEye=60;
private static final int	izBullsEyeDecrement=8;

// bit value used to represent an edge of board cell
private static final int	izBoardEdgeValue=16384;
// bit value used to represent a device occupied cell
private static final int	izDeviceValue=izBoardEdgeValue >> 1;

// the main array containing devices, edges, ants, traces[netId's]
// [swarm][layer][x][y]
private int 				ivBoard[][][][];
// a secondary array containing traces[nodeId's]
// [swarm][layer][x][y]
private byte 				ivBoardTraceId[][][][];
//	private int 			ivBoardGenome[][][];
//	private int 			ivBoardGenomeParents[][][];
private static int 			population;
private static int 			izPopulationPerNode;
private static int 			izNumberNets;
private static int 			izNumberNodes;		// total nodes - duplicates
private static int 			izTotalNumberNodes;	// total nodes with duplicates
private static int 			izMaxSimulationSteps;

//private int 				ivAdjacencyCount[];
//private Ant4 				topAntSwarms[][];
private Ant4 				selectedAnt;

// trace data structures
private int 				izNetNodeCount[];
// individual nodes, as part of a net, and separate
private int 				izNetNodeX[][],izNetNodeY[][];			 

// Ant GA variables
// x,y,layer board positioning data
private Ant4 				antSwarms[][];
private Ant4 				antSwarmsNext[][];
private int 				cSwarm;

/*
// adjacency fitness
private int 				deviceNodeX[],deviceNodeY[];
private int 				adjacencyMatrix[][];
private int 				deviceNodeCount;
*/
// discrete time step animation or GA computation?
private boolean 			bvPerformCrossover=false;
private boolean 			bvInitializeIndividuals=true;
private boolean 			bvSwitchOverNow=false;

// for statistics
private	long				lvFitnessTotal=0;
private int					ivFitnessAverage=0;

// for Statistics file output
//private byte 				fileBuffer[] = new byte[80];
private DataOutputStream 	outFile;
// for solution file output
private DataOutputStream 	phenoFile;

// target nodes
// trace path vectors
// connection adjacency matrix
//private int connectedAbove, connectedBelow;
// private custom objects

// eigentest
/*
	0	1	2	3	4	5	6	7	8	9
0	1									1
1										
2	2									4
3										
4	3									3
5										
6	4									2
7										
8	1									1
9										
*/
// height and width of the circuit being solved
private static final int 	izCircuitExtentWidth=10;
private static final int 	izCircuitExtentHeight=10;
private	static final int	izDeviceNodes[]={
		3,0,6,9,
		0,6,9,3
		};

/*
//	3,0,6,9,
//	0,6,9,3
	0,0,0,8,9,8,9,0,
	0,2,9,6,
//	0,4,9,4,
	0,6,9,2
	};
*/
/*
// height and width of the circuit being solved
private static final int 	izCircuitExtentWidth=;
private static final int 	izCircuitExtentHeight=18;
private	static final int 	izDeviceNodes[]={
//	4,4,4,5,4,6,4,8,4,10,	// 311
//	7,4,7,5,7,6,7,8,7,10,
	8,4,8,5,8,6,8,8,8,10,
	11,4,11,5,11,6,11,8,11,10,
	4,15,4,16,4,17,4,18,4,19,4,20,4,21,4,22,	// 541
	7,15,7,16,7,17,7,18,7,19,7,20,7,21,7,22		// 541
	};
*/
	// eigentest
/*
	0,0,0,8,9,8,9,0,
	0,2,9,6,
	0,4,9,4,
	0,6,9,2
*/
private	static final int 	izNetNodes[][]={
		{3,0,6,9},
		{0,6,9,3}
		}; 
/*
	{0,0,0,8,9,8,9,0},
	{0,2,9,6},
//	{0,4,9,4},
	{0,6,9,2}
	}; 
*/
/*
private	static final int 	izNetNodes[][]={
//	{4,22,7,17,7,19,4,10,7,10,8,10,11,10},	// 541,311 g
	{4,22,7,17,7,19,8,10,11,10},	// 541,311 g
//	{7,15,7,21,4,4,7,4,8,4,11,4},	// 541 p
	{7,15,7,21,8,4,11,4},	// 541 p
	{7,16,8,6},	// 541 d0
	{4,15,8,5},	// 541 d1
	{4,16,11,5},	// 541 d2
	{4,17,11,6},	// 541 d3										
//	{4,18,4,6},	// 541 d4										
//	{4,19,4,5},	// 541 d5										
//	{4,20,7,5},	// 541 d6										
//	{4,21,7,6},	// 541 d7										
//	{7,22,4,8,8,8},	// 541-311 con
	{7,22,8,8},	// 541-311 con
 //{7,18,7,20}		// 541 clk										
	}; 
*/			

// popup menu
private static final String svPopupNames[]=
	{"Single Step Mode On","Single Step Mode Off","Reset","Load Best Swarm"};
private MenuItem thePopupMenuItems[];
private	PopupMenu aPopupMenu;
		
// load the images when the applet begins executing
public void init()
{
	int ivParameterCount;	// used to parse multi-parameter strings
	int ivTemp; 			// used in parsing while loop
	int ivNodeCount;		// current node

	if(bvApplet) {
/*		soundDigit=new AudioClip[10];
		soundDigit[0]=getAudioClip(getCodeBase(),"0.au");
		soundDigit[1]=getAudioClip(getCodeBase(),"1.au");
		soundDigit[2]=getAudioClip(getCodeBase(),"2.au");
		soundDigit[3]=getAudioClip(getCodeBase(),"3.au");
		soundDigit[4]=getAudioClip(getCodeBase(),"4.au");
		soundDigit[5]=getAudioClip(getCodeBase(),"5.au");
		soundDigit[6]=getAudioClip(getCodeBase(),"6.au");
		soundDigit[7]=getAudioClip(getCodeBase(),"7.au");
		soundDigit[8]=getAudioClip(getCodeBase(),"8.au");
		soundDigit[9]=getAudioClip(getCodeBase(),"9.au");
*/		sound = getAudioClip(getCodeBase(),"blip.wav"); //vesna_belly.wav");
	}

	izSleepTime				=0;    // milliseconds to sleep
	// set colors
	color_top 				= new Color(0,255,64);				
	color_bottom 			= new Color(0,64,255);
	color_both 				= new Color(255,0,0);
	color_none 				= Color.darkGray;
	color_device 			= new Color(128,255,255);
	color_connect1 			= new Color(128,128,255);
	color_connect2 			= new Color(128,255,128);
	//color_connect3 		= new Color(255,128,128);
	color_connect3_lower 	= new Color(255,0,255);
	color_connect3_upper 	= new Color(255,255,0);
	color_connect3_both 	= new Color(255,255,255);
	color_board_edge 		= new Color(255,128,128);
 	color_found_source 		= new Color(255,192,0);
 	color_found_target 		= new Color(255,255,255);
 	color_seek_source 		= new Color(160,0,0);
 	color_seek_target 		= new Color(192,0,192);

	//super.init();
	if(bvApplet) {
		izMaxHeight=Integer.parseInt(getParameter("pixelHeight"));
		izMaxWidth=Integer.parseInt(getParameter("pixelWidth"));
	}
	// create graphics subsystem
	buffer = createImage(izMaxWidth,izMaxHeight); // create image buffer
	gContext = buffer.getGraphics(); // get graphics context

	// set background of buffer to black
	gContext.setColor(Color.black);	  
	gContext.fillRect(0,0,izMaxWidth,izMaxHeight+30);

	// applet listen for mouse events, ok
	addMouseListener(this);
	addMouseMotionListener(this);

	// get parameters when in applet mode		
	if(bvApplet) {
		izMaxHeight=Integer.parseInt(getParameter("pixelHeight"));
		izMaxWidth=Integer.parseInt(getParameter("pixelWidth"));
		izGridHeight=Integer.parseInt(getParameter("gridHeight"));
		izGridWidth=Integer.parseInt(getParameter("gridWidth"));
		population=Integer.parseInt(getParameter("population"));	
		dzProbMutation=0.001;//Double.parseDouble(getParameter("probMutation"));
		dzProbCrossover=0.2;//Double.parseDouble(getParameter("probCrossover"));
		izNumberNets=Integer.parseInt(getParameter("numberNets"));
		izNumberNodes=Integer.parseInt(getParameter("numberNodes"));
		izPopulationPerNode=Integer.parseInt(getParameter("populationPerNode"));
		izMaxSimulationSteps=Integer.parseInt(getParameter("maxSimulationSteps"));
		if(Integer.parseInt(getParameter("showStatistics"))>0)
			bvShowStatistics=true;
		else
			bvShowStatistics=false;
		// wether we crossover all genes at the same point or all randomly
		if(Integer.parseInt(getParameter("crossoverPointEqualForAllGenes"))>0)
			bvCrossoverPointEqual=true;
		else
			bvCrossoverPointEqual=false;
		// set flag for mutation of all bits=1, mutation of crossover only=0
		if(Integer.parseInt(getParameter("mutateAllBits"))>0) {
			bvMutateAllBits=true;
		} else {
			bvMutateAllBits=false;
		}


/*
		// initialize node data structures
		izNetNodeCount = new int[izNumberNets];
		izNetNodeX = new int[izNumberNets][izNumberNodes];
		izNetNodeY = new int[izNumberNets][izNumberNodes];
	
		// initialize Ant objects
		antSwarms = new Ant4[population][izNumberNodes];
		//topAntSwarms = new Ant4[population][izNumberNodes];

		ivNodeCount=0;	// start with 0 nodes
		for(short net=0;net<izNumberNets;net++) {
		    try {				   
				//String aParameter = "net" + (new Integer(net+1)).toString() + "Nodes";
				izNetNodeCount[net]=Integer.parseInt(getParameter("net" + (new Integer(net+1)).toString() + "Nodes"));
				// extract out the x coordinates of the net of nodes
				if (getParameter("net" + (new Integer(net+1)).toString() + "x") != null) {
				   	StringTokenizer Points = new StringTokenizer(getParameter("net" + (new Integer(net+1)).toString() + "x"),"\t\n\r ,;.");
					ivParameterCount=0;
		    		while (Points.hasMoreTokens()) {
						izNetNodeX[net][ivParameterCount++] = new Integer(Points.nextToken()).intValue();
						//System.out.println("x" + (new Integer(izNetNodeX[net][ivParameterCount-1])).toString());
					}
				}
				// extract out the y coordinates of the net of nodes
				if (getParameter("net" + (new Integer(net+1)).toString() + "y") != null) {
				   	StringTokenizer Points = new StringTokenizer(getParameter("net" + (new Integer(net+1)).toString() + "y"),"\t\n\r ,;.");
					ivParameterCount=0;
		    		while (Points.hasMoreTokens()) {
						izNetNodeY[net][ivParameterCount++] = new Integer(Points.nextToken()).intValue();
						//System.out.println("y" + (new Integer(izNetNodeY[net][ivParameterCount-1])).toString());
					}
				} 
		    } catch (Exception e) {}
		}
*/
		if(!bQuietInit) {
			System.out.println("_izGridHeight: " + (new Integer(izGridHeight)).toString());
			System.out.println("_izGridWidth: " + (new Integer(izGridWidth)).toString());
			System.out.println("_population: " + (new Integer(population)).toString());	
		}
	} else { // taken care of in main()
		// preset some values
		//loadBoardExample1(0, 0);
	}

	// initialize board
	ivBoard = new int[population][2][izGridWidth][izGridHeight];
	ivBoardTraceId = new byte[population][2][izGridWidth][izGridHeight];

	// initialize fitness arrays
	// the integer fitness for each swarm
	ivAbsoluteFitness = new int[population];
//	ivAbsoluteFitnessPrev = new int[population];
	// the floating-point [0-1] probability of selection for each swarm
	// as a percentage of dvTotalFitness
	dvProbSelection = new double[population];
	dvProbRecombination = new double[population];
//	dvNewPopulationProbSelection = new double[population];
	// the sum of all the ivAbsoluteFitness[]
	//dvTotalFitness;
	// the cumulative probabilities of selection for each swarm
	dvCumulativeProbSelection = new double[population];
	// the new population indexes
	ivNewPopulationIndexes = new int[population];
	ivNewPopulationIndexedForCrossover = new int[population];
	iaSwarmTimeToSolution=new int[population];

// 	loadBoardExample1(0, 0);
	loadBoardParadc((int)((izGridWidth-izCircuitExtentWidth)/2)
		,(int)((izGridHeight-izCircuitExtentHeight)/2));
	loadSwarmParadc((int)((izGridWidth-izCircuitExtentWidth)/2)
		,(int)((izGridHeight-izCircuitExtentHeight)/2));
	//izMaxAdjacency=(izGridHeight*izGridWidth-izDeviceNodes-(izGridHeight*2)-((izGridWidth-2)*2))*4*16;
	//System.out.println("i" + (new Integer(izMaxAdjacency)).toString());

	izSpacing=(int)(izMaxHeight-60)/izGridHeight;
	izSpacing2=(int)(izMaxHeight-60)/izGridHeight/2;
	izSpacing3=(int)(izMaxHeight-60)/izGridHeight/3;
	izSpacing4=(int)(izMaxHeight-60)/izGridHeight/6;
	izSpacing6=(int)(izMaxHeight-60)/izGridHeight/12;
	if(!bQuietInit) {
		System.out.println("izSpacing " + (new Integer(izSpacing)).toString());
		System.out.println("izSpacing2 " + (new Integer(izSpacing2)).toString());
		System.out.println("izSpacing3 " + (new Integer(izSpacing3)).toString());
		System.out.println("izSpacing4 " + (new Integer(izSpacing4)).toString());
		System.out.println("izSpacing6 " + (new Integer(izSpacing6)).toString());
	}

	///////////////////////////////////
	// create initial random population
	///////////////////////////////////
	loadRandomPopulation();
	// current swarm being simulated
	cSwarm=0;

	// initialize board grid
	// calculate the number of swarms that can mate
	//izCrossoverCount=(int)(population*izReplacementRatio/100);
	//ivBoard = new BitSet(izGridHeight*izGridWidth*2);
	//ivBoard = new int[izGridWidth][izGridHeight];
	//ivBoardGenome = new int[izGridWidth][izGridHeight][population];
	//ivBoardGenomeParents = new int[izGridWidth][izGridHeight][izCrossoverCount];
	//ivAdjacencyCount = new int[population];
	// to keep track of the fitness number for each swarm
	//ivTopIndividualsFitness = new int[(int)(population*izReplacementRatio/100)];

	// keep a separate copy of the top swarms
	//ivTopIndividuals = new Ant4[(int)(population*izReplacementRatio/100)];

	// create popup menu
	aPopupMenu = new PopupMenu("Popup");
	thePopupMenuItems = new MenuItem[svPopupNames.length];

	for(int i=0;i<svPopupNames.length;i++)	{
		thePopupMenuItems[i] = new MenuItem(svPopupNames[i]);
		aPopupMenu.add(thePopupMenuItems[i]);
		thePopupMenuItems[i].addActionListener(this);
	}

  	// add popup menu to this canvas
	add(aPopupMenu);
	enableEvents(AWTEvent.MOUSE_EVENT_MASK);
  
  	// write output header to disk for non-applets
  	if(!bvApplet) {
  		try {
  			// create output stream
  			outFile = new DataOutputStream(new FileOutputStream("out.txt",true));
			if(!bQuietInit) {
  			outFile.writeBytes("RouterAnimApplet:\r\n");
  			outFile.writeBytes("Population: \t" + (new Integer(population)).toString() + "\r\n");
  			outFile.writeBytes("izGridHeight: \t" + (new Integer(izGridHeight)).toString() + "\r\n");
  			outFile.writeBytes("izGridWidth: \t" + (new Integer(izGridWidth)).toString() + "\r\n");
  			outFile.writeBytes("dzProbCrossover: \t" + (new Double(dzProbCrossover)).toString() + "\r\n");
  			outFile.writeBytes("dzProbMutation: \t" + (new Double(dzProbMutation)).toString() + "\r\n");
  			outFile.writeBytes("izMaxSimulationSteps: \t" + (new Integer(izMaxSimulationSteps)).toString() + "\r\n");
			if(bvCrossoverPointEqual)
	  			outFile.writeBytes("bvCrossoverPointEqual: \ttrue\r\n");
			else
	  			outFile.writeBytes("bvCrossoverPointEqual: \tfalse\r\n");
			if(bvMutateAllBits)
	  			outFile.writeBytes("bvMutateAllBits: \ttrue\r\n");
			else
	  			outFile.writeBytes("bvMutateAllBits: \tfalse\r\n");
				
  			//outFile.writeBytes("izGridWidth: \t" + (new Integer(izGridWidth)).toString() + "\r\n");
  			outFile.writeBytes("gen\tfit\tafit\r\n");
			}
  			outFile.close();
  		} catch (Exception e) {
  			String err = e.toString();
  			System.out.println(err);
  		}
		if(!bQuietInit) {
		  	System.out.println("\nF.Michael O'Brien: RouterAnimApplet.java");
  			System.out.println("must alt-tab out and back in to step past");
		  	System.out.println("sun.awt.windows.WGraphics.drawImage(WGraphics.java:360) bug");
		  	System.out.println("when using double-buffering");
		}

//	for(int i=0;i<4;i++)
//		System.out.println("xdir["+ (new Integer(xdir[i])).toString() + "]:ydir["+ (new Integer(ydir[i])).toString() + "]");
  	}

	// create colors for the trace nets
	color_trace_both = new Color[izNumberNets];
	color_trace_upper = new Color[izNumberNets];
	color_trace_lower = new Color[izNumberNets];
	for(int i=0;i<izNumberNets;i++) {
		//color_trace_both[i]=new Color((izNumberNets-1-i)*((int)(256/izNumberNets)+1),192,i*izNumberNets);
		//color_trace_upper[i]=new Color(0,64,i*izNumberNets);
		//color_trace_lower[i]=new Color((izNumberNets-1-i)*((int)(256/izNumberNets)+1),64,0);
		color_trace_both[i]=new Color((izNumberNets-1-i)*((int)(256/izNumberNets)+1),64,i*izNumberNets);
		color_trace_upper[i]=new Color((izNumberNets-1-i)*((int)(256/izNumberNets)+1),64,i*izNumberNets);
		color_trace_lower[i]=new Color((izNumberNets-1-i)*((int)(256/izNumberNets)+1),64,i*izNumberNets);
	}

	// turn off printing for subsequent init() calls
	bQuietInit=true;
}


// display the image in the Applet's Graphics context
public void paint(Graphics g) {
	int xd,yd;
	int tempx, tempy;
	int ivDirx, ivDiry;
	String stringRep;	// string binary rep of grid cell 0-izBoardEdgeValue	
	boolean connected;
	int cellColor;
	int ivMaxConnections=0;
	int lastIndex;
	int p1,p2;
	int tempBitPos;
//	BitSet aBitSet;

	//super.paint(g);
	g.drawImage(buffer,0,0,this);
	// clear previous image from buffer
	gContext.setColor(Color.black);
	gContext.fillRect(0,0,izMaxWidth,izMaxHeight);
	
	// initialize arrays?
	//initializeSimulation();
 
	// display current solution
	displayCurrentSolution();

	// display genome statistics
	if(bvShowStatistics) {
		displayStatistics();
	}

	// increment timestep
	simulationTimeStep(g);

	// write out numerical parameters
	gContext.setColor(Color.cyan);
	gContext.drawString("G:" + (new Long(lvGeneration)).toString() + " S:" + 
		(new Integer(cSwarm)).toString(),10,izMaxHeight-40);
	gContext.drawString("SFRM: " + (new Integer(ivFrames2)).toString() + " GFRM: " + (new Integer(ivFrames)).toString() ,(int)(izMaxWidth/2),izMaxHeight-40);

	//gContext.drawString("F: " + (new Integer(ivFrames)).toString(),10,izMaxHeight-16);
	//gContext.drawString("I: " + (new Integer(ivIndividual)).toString(),80,izMaxHeight-16);
	//gContext.drawString("G: " + (new Integer((int)lvGeneration)).toString(),120,izMaxHeight-16);
	//gContext.drawString("A: " + (new Integer(ivAdjacencyCount[ivIndividual])).toString(),180,izMaxHeight-16);
	//gContext.drawString("P: " + (new Integer((int)Math.floor(100*ivAdjacencyCount[ivIndividual]/izMaxAdjacency+.5))).toString(),240,izMaxHeight-16);
	if(cSwarm>0)
		gContext.drawString("PF: " + (new Integer(ivAbsoluteFitness[cSwarm-1])).toString(),10,izMaxHeight-30);

	gContext.drawString("BF: " + (new Integer(ivBestSwarmFitnessEver)).toString(),10,izMaxHeight-20);
	//gContext.drawString("Age: " + (new Integer(antSwarms[cSwarm][0].getGeneration())).toString(),10,izMaxHeight-10);
	gContext.drawString("PTF: " + (new Double(dvPrevTotalFitness)).toString(),80,izMaxHeight-30);
	gContext.drawString("BTF: " + (new Double(dvBestTotalFitnessEver)).toString(),80,izMaxHeight-20);

	// compute fitness
	//computeFitness();
	
 	
  	//try {Thread.sleep( izSleepTime );}
	//catch ( InterruptedException e ){showStatus(e.toString());}
	repaint();  // display buffered image
} // paint()

// display current solution
private void displayCurrentSolution() {
	int px, py;
	Ant4 anAnt;
	int nId;
	int cLayer=0;	 

	// draw board
	for(int i=0;i<izGridWidth;i++) {
		//System.out.println(i);
		for(int j=0;j<izGridHeight;j++) {
			// check for edge of board
			if(ivBoard[cSwarm][cLayer][i][j]==izBoardEdgeValue) {
				gContext.setColor(color_board_edge);
				gContext.fillRect(10+i*izSpacing+izSpacing2-izSpacing4,
					10+j*izSpacing+izSpacing2-izSpacing4,
					izSpacing3+izSpacing3,izSpacing3+izSpacing3);
			} else
			// check for devices
			if(ivBoard[cSwarm][cLayer][i][j]==izDeviceValue) {
				gContext.setColor(color_device);
				gContext.fillRect(10+i*izSpacing+izSpacing2-izSpacing4,
					10+j*izSpacing+izSpacing2-izSpacing4,
					izSpacing3+izSpacing3,izSpacing3+izSpacing3);
			} else {
				//System.out.println((new Integer(ivBoard[cSwarm][0][i][j])).toString());
//				if(ivBoard[cSwarm][0][i][j]>0 && ivBoard[cSwarm][1][i][j]>0) {
//				// user upper layer for both
//					gContext.setColor(color_trace_both[ivBoard[cSwarm][1][i][j]-1]);
//					gContext.drawRect(10+i*izSpacing+izSpacing2,
//						10+j*izSpacing+izSpacing2,
//						izSpacing3,izSpacing3);
//				} else
				if(ivBoard[cSwarm][0][i][j]>0) {
					gContext.setColor(color_trace_lower[ivBoard[cSwarm][0][i][j]-1]);
					gContext.drawRect(10+i*izSpacing+izSpacing2,
						10+j*izSpacing+izSpacing2,
						izSpacing3,izSpacing3);
				} 
				if(ivBoard[cSwarm][1][i][j]>0) {
					gContext.setColor(color_trace_upper[ivBoard[cSwarm][1][i][j]-1]);
					gContext.drawRect(10+i*izSpacing+izSpacing2+izSpacing4,
						10+j*izSpacing+izSpacing2+izSpacing4,
						izSpacing3-izSpacing4,izSpacing3-izSpacing4);
				}							   
			}
/*			if(ivBoard[cSwarm][][i][j]==1) {
				gContext.setColor(Color.yellow);
				gContext.fillRect(10+i*izSpacing+izSpacing2,
						10+j*izSpacing+izSpacing2,
						izSpacing3,izSpacing3);
			}				
*/
		}
	}		 

	// draw Ants
	for(int n=0;n<izTotalNumberNodes;n++) {
		anAnt=antSwarms[cSwarm][n];
//		System.out.println((new Integer(n)).toString());
		nId=anAnt.getNodeId();
		//px=anAnt.getPosy();	// traced bug for last 24 hours here 1419 2000oct30
		//py=anAnt.getPosx();
		py=anAnt.getPosy();	// traced bug for last 24 hours here 1419 2000oct30
		px=anAnt.getPosx();

		// draw line to target
		if(anAnt.getcEnd())
			gContext.setColor(color_found_target);
		else   
			gContext.setColor(color_seek_target);
		gContext.drawLine(10+px*izSpacing+izSpacing2+izSpacing4,
			10+py*izSpacing+izSpacing2+izSpacing4,
			10+anAnt.getDestx()*izSpacing+izSpacing2+izSpacing4,
			10+anAnt.getDesty()*izSpacing+izSpacing2+izSpacing4);

		// draw line to src if finished
		if(anAnt.getcEnd()) {
			if((anAnt.getpEnd()||ivBullsEye[cSwarm][nId]<izMaxBullsEye)&&!bvDestFound[cSwarm][nId]) {
				//gContext.setColor(Color.magenta);
				gContext.setColor(Color.green);
				gContext.drawOval(10+px*izSpacing+izSpacing2+izSpacing4-ivBullsEye[cSwarm][nId],
					10+py*izSpacing+izSpacing2+izSpacing4-ivBullsEye[cSwarm][nId],
					ivBullsEye[cSwarm][nId]*2,ivBullsEye[cSwarm][nId]*2);
					ivBullsEye[cSwarm][nId]-=izBullsEyeDecrement;
					if(ivBullsEye[cSwarm][nId]<izBullsEyeDecrement) {
						bvDestFound[cSwarm][nId]=true;
						ivBullsEye[cSwarm][nId]=izMaxBullsEye+izBullsEyeDecrement;
					}
			}
//			if(bvApplet) {	 
			if(!anAnt.getpEnd()) {	// dest recently found
				//iaTimeToSolution[cSwarm][nId]=ivFrames2;
//				soundDigit[nId].play();
			}
//			}
			
			gContext.setColor(color_found_source);
			gContext.drawLine(10+px*izSpacing+izSpacing2+izSpacing4,
				10+py*izSpacing+izSpacing2+izSpacing4,
				10+anAnt.getSrcx()*izSpacing+izSpacing2+izSpacing4,
				10+anAnt.getSrcy()*izSpacing+izSpacing2+izSpacing4);
		} else {
			gContext.setColor(color_seek_source);		
			gContext.drawLine(10+px*izSpacing+izSpacing2+izSpacing4,
				10+py*izSpacing+izSpacing2+izSpacing4,
				10+anAnt.getSrcx()*izSpacing+izSpacing2+izSpacing4,
				10+anAnt.getSrcy()*izSpacing+izSpacing2+izSpacing4);
		}
		//ivBoard[][cLayer][px][py]=1;
//		if(selectedAnt.getNodeId()==anAnt.getNodeId())
//			gContext.setColor(Color.green);
//		else
			if(anAnt.getLayer()==0)
				gContext.setColor(Color.white);
			else
				gContext.setColor(Color.yellow);

		// draw body of insect
		gContext.fillRect(10+px*izSpacing+izSpacing2,
					10+py*izSpacing+izSpacing2,
					izSpacing3,izSpacing3);
		gContext.setColor(Color.cyan);
		gContext.drawString((new Integer(anAnt.getNodeId())).toString(),
			10+px*izSpacing+izSpacing2+izSpacing3,
			9+py*izSpacing+izSpacing2);
//		gContext.drawString((new Integer(anAnt.getPosx())).toString(),
//			26+px*izSpacing+izSpacing2,
//			9+py*izSpacing+izSpacing2);
//		gContext.setColor(Color.blue);
//		gContext.drawString((new Integer(anAnt.getPosy())).toString(),
//			42+px*izSpacing+izSpacing2,
//			9+py*izSpacing+izSpacing2);

		// draw current directional point of insect
		gContext.drawLine(10+px*izSpacing+izSpacing2+izSpacing4+((izSpacing2+izSpacing4)*xdir[anAnt.getcDirection()]),
					10+py*izSpacing+izSpacing2+izSpacing4+((izSpacing2+izSpacing4)*ydir[anAnt.getcDirection()]),
					10+px*izSpacing+izSpacing2+izSpacing4,
					10+py*izSpacing+izSpacing2+izSpacing4);

		// draw previous directional point of insect
		gContext.setColor(Color.red);
		gContext.drawLine(10+px*izSpacing+izSpacing2+izSpacing4+((izSpacing2+izSpacing4)*xdir[anAnt.getpDirection()]),
					10+py*izSpacing+izSpacing2+izSpacing4+((izSpacing2+izSpacing4)*ydir[anAnt.getpDirection()]),
					10+px*izSpacing+izSpacing2+izSpacing4,
					10+py*izSpacing+izSpacing2+izSpacing4);
	}								 
}

private int enumerateBoardCell(int cLayer, Ant4 anAnt, int dir, int x,int y,int aNetId) {
	Ant4 otherAnt;
	//int cLayer=0;
	// direction:				values:
	// 0-11 10du swne swne		0 = empty			4 = device
	//      0123 4567 8901		1 = trace other		5 = Ant other
	//    p 1098 7654 3210		2 = trace twin		6 = Ant twin
	//                6745		3 = trace self		7 = target in sight

	//System.out.println("_x,y: " + (new Integer(x)).toString() + "," + (new Integer(y)).toString());
	//System.out.println("_cSwarm: " + (new Integer(cSwarm)).toString());

	// board edge
	if(ivBoard[cSwarm][cLayer][x][y]==izBoardEdgeValue)
		return 4;
	// devices
	// check for self trace // this line will disallow valid cycling
//	if(ivBoardTraceId[cSwarm][cLayer][x][y]==anAnt.getNodeId()+1)
//		return 3;
	if(ivBoard[cSwarm][cLayer][x][y]==izDeviceValue)
		return 4;
	// twin trace
	if(ivBoard[cSwarm][cLayer][x][y]==aNetId+1)
		return 2;
	// other trace
	if(ivBoard[cSwarm][cLayer][x][y]>0)	//alien traces fixed 2000nov09
 		return 1;
	// check all other ants for position info
	for(int n=0;n<izTotalNumberNodes;n++) {
		otherAnt=antSwarms[cSwarm][n];
		// we wont have to check that ant will enumerate itself since we dont check 0,0
		if((otherAnt.getPosx()==x)&&(otherAnt.getPosy()==y))
			if(otherAnt.getNetId()==aNetId) {
				return 6;
			} else {
				return 5;
			}
	}

	// check line of sight to target
	int tx=anAnt.getDestx();
	int ty=anAnt.getDesty();

	// with or without obstructions
	switch (dir) {
		case 0:
			if(ty==y && tx>=x)
				return 7;
		break;
		case 1:
			if(tx==x && ty<=y)
				return 7;
		break;
		case 2:
			if(ty==y && tx<=x)
				return 7;
		break;
		case 3:
			if(tx==x && ty>=y)
				return 7;
		break;
		case 4: // directly above or below
			// check for twin trace, but don't stop on self trace
			if(ivBoardTraceId[cSwarm][1-cLayer][x][y]==anAnt.getNodeId()+1) {
				if(antSwarms[cSwarm][ivBoardTraceId[cSwarm][1-cLayer][x][y]-1].getNetId()==aNetId+1) {
					System.out.println("traceAboveBelow=" + (new Integer(1-cLayer)).toString());
					return 2;
				}
			}
	}

	// nothing is on the proximity
	return 0;
}

// write all the state variables to the Ant swarm
private void updateProximity() {
	// holder for current ant within the loop
	Ant4 anAnt;
	int x,y,l,net;
	//for(int cLayer=0;cLayer<2;cLayer++) {
		for(int n=0;n<izTotalNumberNodes;n++) {
			anAnt=antSwarms[cSwarm][n];
			//System.out.println((new Integer(n)).toString());
			// update proximity for ants
			// should need no torodial checking since devices will always be along edges

			x=anAnt.getPosx();
			y=anAnt.getPosy();
			l=anAnt.getLayer();
			net=anAnt.getNetId();
			//System.out.println("x:"+(new Integer(x)).toString()+ "y:"+(new Integer(x)).toString());
			// leave a path, net specific only
//			ivBoardTraceId[cSwarm][l][x][y]=(byte)(anAnt.getNodeId()+1);
//			if(ivBoard[cSwarm][l][x][y]<izDeviceValue)
//				ivBoard[cSwarm][l][x][y]=net+1;
			anAnt.setProxBelow(
				enumerateBoardCell(0,anAnt,0,x+xdir[0],y+ydir[0],net),
				enumerateBoardCell(0,anAnt,1,x+xdir[1],y+ydir[1],net),
				enumerateBoardCell(0,anAnt,2,x+xdir[2],y+ydir[2],net),
				enumerateBoardCell(0,anAnt,3,x+xdir[3],y+ydir[3],net));
			anAnt.setProxAbove(
				enumerateBoardCell(1,anAnt,0,x+xdir[0],y+ydir[0],net),
				enumerateBoardCell(1,anAnt,1,x+xdir[1],y+ydir[1],net),
				enumerateBoardCell(1,anAnt,2,x+xdir[2],y+ydir[2],net),
				enumerateBoardCell(1,anAnt,3,x+xdir[3],y+ydir[3],net));	

			// set the NodeId specific trace proximities
			anAnt.setProxTraceBelow(
				ivBoardTraceId[cSwarm][0][x+xdir[0]][y+ydir[0]],
				ivBoardTraceId[cSwarm][0][x+xdir[1]][y+ydir[1]],
				ivBoardTraceId[cSwarm][0][x+xdir[2]][y+ydir[2]],
				ivBoardTraceId[cSwarm][0][x+xdir[3]][y+ydir[3]]);

			anAnt.setProxTraceAbove(
				ivBoardTraceId[cSwarm][1][x+xdir[0]][y+ydir[0]],
				ivBoardTraceId[cSwarm][1][x+xdir[1]][y+ydir[1]],
				ivBoardTraceId[cSwarm][1][x+xdir[2]][y+ydir[2]],
				ivBoardTraceId[cSwarm][1][x+xdir[3]][y+ydir[3]]);

			// set the prox directly above or below the Ant
			anAnt.setProxLayer(enumerateBoardCell(
				1-l,anAnt,4,x+xdir[0],y+ydir[0],net));

			// set the NodeId specific trace proximity, directly above or below the Ant
			anAnt.setProxTraceLayer(ivBoardTraceId[cSwarm][1-l][x][y]);

 /*
			gContext.setColor(Color.cyan);
				gContext.drawString((new Integer(enumerateBoardCell(x+xdir[0],y+ydir[0],anAnt.getNetId()))).toString(),
					140+izMaxHeight+12*3,
					20+14*n);
				gContext.drawString((new Integer(enumerateBoardCell(x+xdir[1],y+ydir[1],anAnt.getNetId()))).toString(),
					140+izMaxHeight+12*2,
					20+14*n);
				gContext.drawString((new Integer(enumerateBoardCell(x+xdir[2],y+ydir[2],anAnt.getNetId()))).toString(),
					140+izMaxHeight+12*1,
					20+14*n);
				gContext.drawString((new Integer(enumerateBoardCell(x+xdir[3],y+ydir[3],anAnt.getNetId()))).toString(),
					140+izMaxHeight+12*0,
					20+14*n);
*/
/*			for(int i=0;i<4;i++)		
				gContext.drawString((new Integer(enumerateBoardCell(x+xdir[i],y+ydir[i],anAnt.getNetId()))).toString(),
					140+izMaxHeight+12*i,
					20+14*n);
*/	
		}		  
	//}		  
}

private void computeFitness(Graphics g, boolean solved) {
	//Ant4	anAnt;
	int		area;
	// for now get # of cEnd Ants
	//for(int s=0;s<population;s++) {

	// increase fitness for completely solved swarms
	if(solved) {
		ivAbsoluteFitness[cSwarm]+=izMaxSimulationSteps*6000*izTotalNumberNodes;
		// add the path time for each node
//	 	for(int n=0;n<izTotalNumberNodes;n++) {
//			ivAbsoluteFitness[cSwarm]+=
//				10*izMaxSimulationSteps*(izMaxSimulationSteps-antSwarms[cSwarm][n].getTimeToSolution());
//		}
		// minimize area used
		area=0;
		for(int u=0;u<izGridWidth;u++)
			for(int v=0;v<izGridHeight;v++)
			 	for(int n=0;n<izTotalNumberNodes;n++)
					for(int l=0;l<2;l++)
						if(ivBoardTraceId[cSwarm][l][u][v]==n+1)
							area+=1;

		// subtract min path
		area-=izTotalNumberNodes*3;

		ivAbsoluteFitness[cSwarm]-=(int)(area*izMaxSimulationSteps*25*izTotalNumberNodes);
		System.out.println("S_area: " +	(new Integer(area+(izTotalNumberNodes*3))).toString() + "\teq:" +
			(new Integer((int)(area*izMaxSimulationSteps*25*izTotalNumberNodes))).toString());

/*		// minimize attempted turns,switches
		int asum=0;
		int aswitch=0;
	 	for(int n=0;n<izTotalNumberNodes;n++) {
			ivAbsoluteFitness[cSwarm]-=(int)(antSwarms[cSwarm][n].getCompletedTurns()*izMaxSimulationSteps/50);
			asum+=(int)(antSwarms[cSwarm][n].getCompletedTurns()*izMaxSimulationSteps/50);
			ivAbsoluteFitness[cSwarm]-=(int)(antSwarms[cSwarm][n].getCompletedSwitches()*izMaxSimulationSteps/50);
			aswitch+=(int)(antSwarms[cSwarm][n].getCompletedSwitches()*izMaxSimulationSteps/50);
		}

//		System.out.println("_turns-: " +	(new Integer(asum)).toString() + "\tSwitches-" +
//			(new Integer(aswitch)).toString());
*/
	} else {
 		for(int n=0;n<izTotalNumberNodes;n++) {
			// ants have stopped at their destination
			//anAnt=antSwarms[cSwarm][n];
			if(antSwarms[cSwarm][n].getcEnd()) {
			 	//ivAbsoluteFitness[cSwarm]+=izMaxSimulationSteps*izTotalNumberNodes;
			 	ivAbsoluteFitness[cSwarm]+=
		 			(izMaxSimulationSteps-antSwarms[cSwarm][n].getTimeToSolution())*izMaxSimulationSteps;
			} else {
				//ivAbsoluteFitness[cSwarm]+=totalMoves[cSwarm][n];
				//System.out.println("_totalMoves[][]: " +	(new Integer(totalMoves[cSwarm][n])).toString());
				// get distance to dest
				ivAbsoluteFitness[cSwarm]+=
//					10;
					izGridWidth-Math.abs(antSwarms[cSwarm][n].getPosx()-antSwarms[cSwarm][n].getDestx());
				ivAbsoluteFitness[cSwarm]+=
					izGridHeight-Math.abs(antSwarms[cSwarm][n].getPosy()-antSwarms[cSwarm][n].getDesty());
			}
		}

		// minimize area used
		area=0;
		for(int u=0;u<izGridWidth;u++)
			for(int v=0;v<izGridHeight;v++)
			 	for(int n=0;n<izTotalNumberNodes;n++)
					for(int l=0;l<2;l++)
						if(ivBoardTraceId[cSwarm][l][u][v]==n+1)
							area+=1;

		ivAbsoluteFitness[cSwarm]-=(int)((area-izTotalNumberNodes*5)*izMaxSimulationSteps/100);
		System.out.println("_area: " +	(new Integer(area)).toString() + "\teq:" +
			(new Integer((int)((area-izTotalNumberNodes*5)*izMaxSimulationSteps/100))).toString());

/*		// minimize attempted turns
		int asum=0;
		int aswitch=0;
 		for(int n=0;n<izTotalNumberNodes;n++) {
			ivAbsoluteFitness[cSwarm]-=antSwarms[cSwarm][n].getCompletedTurns()*1;
			asum+=antSwarms[cSwarm][n].getCompletedTurns()*1;

			ivAbsoluteFitness[cSwarm]-=antSwarms[cSwarm][n].getCompletedSwitches()*1;
			aswitch+=antSwarms[cSwarm][n].getCompletedSwitches()*1;
		}
//		System.out.println("_turns-: " +	(new Integer(asum)).toString() + "\tSwitches-" +
//			(new Integer(aswitch)).toString());
*/
	}
	//ivAbsoluteFitness[cSwarm]=ivAbsoluteFitness[cSwarm]/100;
	ivAbsoluteFitness[cSwarm]=ivAbsoluteFitness[cSwarm]/izMaxSimulationSteps;
	if(ivAbsoluteFitness[cSwarm]<1)	{
//		System.out.println("_ivAbsoluteFitness: " +	(new Integer(ivAbsoluteFitness[cSwarm])).toString());
		ivAbsoluteFitness[cSwarm]=1;
	}
	System.out.println("_swarm: " + (new Integer(cSwarm)).toString()+
		"\tFitness: "+(new Integer(ivAbsoluteFitness[cSwarm])).toString());
 	for(int n=0;n<izTotalNumberNodes;n++) {
		System.out.print(" N:"+(new Integer(n)).toString()+" P:"+(new Integer(antSwarms[cSwarm][n].getTimeToSolution())).toString()+" ");
	}
	System.out.println();	
	// add fitness to total for swarm
//	dvTotalFitness+=ivAbsoluteFitness[cSwarm];

	// trap potential high fitness
	boolean newPeak=false;
	if(ivBestSwarmFitnessEver<ivAbsoluteFitness[cSwarm]) {
		ivBestSwarmFitnessEver=ivAbsoluteFitness[cSwarm];
		System.out.println("Max Fitness\t" + (new Integer(ivBestSwarmFitnessEver)).toString() + 
			"\tG: "+(new Long(lvGeneration)).toString() +
			"\tS: "+(new Integer(cSwarm)).toString());
		newPeak=true;
	}
	if(ivBestSwarmFitness<ivAbsoluteFitness[cSwarm]) {
		ivBestSwarmFitness=ivAbsoluteFitness[cSwarm];
	}

	// get worst fitness
	if(ivWorstSwarmFitnessEver>ivAbsoluteFitness[cSwarm]) {
		ivWorstSwarmFitnessEver=ivAbsoluteFitness[cSwarm];
	}	
	if(ivWorstSwarmFitness>ivAbsoluteFitness[cSwarm]) {
		ivWorstSwarmFitness=ivAbsoluteFitness[cSwarm];
	}	

	// best fitness ever
	if(newPeak) {
		for(int node=0;node<izTotalNumberNodes;node++) {
			// write out swarm to file
			if(!bvApplet) {
	 			antSwarms[cSwarm][node].writeGenomeToFile("log\\bf_g"+(new Long(lvGeneration)).toString()+"s"+(new Integer(cSwarm)).toString()+"n"+(new Integer(node)).toString()+"_f"+(new Integer(ivBestSwarmFitnessEver)).toString()+".txt");
			}
	   		bestAntSwarm[node]=antSwarms[cSwarm][node];
   			//System.out.print(" " + (new Integer(bestAntSwarm[node].getNodeId())).toString());
		}
//		System.out.println("Best Fitness ; Gen:" + (new Long(lvGeneration)).toString() + " F:" +
//  			(new Integer(ivBestSwarmFitnessEver)).toString());
		// output phenotype (board layout) to disk
		outputPhenotypeToDisk();
	}

//	System.out.println("Fitness of : " + (new Integer(cSwarm)).toString() + " " +
//  		(new Integer(ivAbsoluteFitness[cSwarm])).toString() + " " +
//		(new Integer(ivAbsoluteFitnessPrev[cSwarm])).toString());
	//}
}

private boolean determineIfCurrentSwarmIsSolved(Graphics g) {
	boolean solved=true;
	for(int j=0;j<izTotalNumberNodes;j++) {
		if(!bvDestFound[cSwarm][j]) {
			solved=false;
		} else {

		}
	}
							   
	if(solved) {
//		if(ivBestTime>ivFrames2) {
//			ivBestTime=ivFrames2;

			// add to fitness

		//resetSwarms();
		//if(((int)(dvTotalFitnessPrev))==((int)(dvTotalFitness))) { // false positives

		//ivFrames=0;
		// perform crossover
		//performSelectionAndCrossoverWithMutation();
	 	g.setColor(Color.cyan);
		g.drawString("Solved..." + (new Long(cSwarm)).toString(),4*izSpacing,4*izSpacing);

		//init();
		ivFrames2=0;
	}
	return solved;
}


private void performSelectionAndCrossoverWithMutation() {
	double	dPos;
	Ant4	holdingAnt;
	int		crossoverPoint;

	// add fitness to total for swarm
	for(int s=0;s<population;s++) {
		dvTotalFitness+=ivAbsoluteFitness[s];
//		System.out.println("dvTotalFitness: "+(new Double(dvTotalFitness)).toString());
	}
	//	save old fitness
	dvPrevTotalFitness=dvTotalFitness;

	// update dvBestTotalFitnessEver
	if(dvBestTotalFitnessEver<dvTotalFitness)
		dvBestTotalFitnessEver=dvTotalFitness;

//	System.out.println("dvTotalFitness:t "+(new Double(dvTotalFitness)).toString());
	writeStatistics();

	// GET FITNESS
	// get total fitness for all population swarms
	for(int s=0;s<population;s++) {
		//for(int node=0;node<izTotalNumberNodes;node++) {	// error 2000nov15
			dvProbSelection[s]=ivAbsoluteFitness[s]/dvTotalFitness;
//		System.out.println((new Integer(s)).toString()+":"+(new Double(ivAbsoluteFitness[s])).toString()+" ");			
	   	//}
//		System.out.print((new Integer(s)).toString()+":"+(new Double(dvProbSelection[s])).toString()+" ");
	}

	// SELECTION
	// calc cumulative prob from relative probSelection
	double total=0;
	for(int s=0;s<population;s++) {
		dvCumulativeProbSelection[s]=total+dvProbSelection[s];
		total+=dvProbSelection[s];
//		System.out.println((new Integer(s)).toString()+" c:"+(new Double(dvCumulativeProbSelection[s])).toString()+" ");
	}

	// select population# individuals from population
	for(int p=0;p<population;p++) {
		dPos=Math.random();
//		System.out.println((new Double(dPos)).toString());
		for(int s=0;s<population;s++) {
			if(dvCumulativeProbSelection[s]>=dPos) {
				// select at index according to coverage of total
				ivNewPopulationIndexes[p]=s;
				// truncate loop
				s=population;
			}
		}
//		System.out.print((new Integer(p)).toString()+" i:"+(new Integer(ivNewPopulationIndexes[p])).toString()+" ");
	}

	// CROSSOVER
	// get a rand 0-1 # for each individual
	boolean indexUsed;
	ivCrossoverPopSize=0;
	for(int p=0;p<population;p++) {
		dvProbRecombination[p]=Math.random();
		//System.out.println((new Double(dzProbCrossover)).toString()+"," +(new Double(dvProbRecombination[p])).toString());
		if(dzProbCrossover>dvProbRecombination[p]) {
//			indexUsed=false;
//			while(!indexUsed)	{
//				// get a random index
//				// check if this swarm has already been selected
//				for(int i=0;i<ivCrossoverPopSize;i++) {
//					if(ivNewPopulationIndexedForCrossover[i]==p)
//						indexUsed=true;
//				}
//			}
			ivNewPopulationIndexedForCrossover[ivCrossoverPopSize]=p;
			//System.out.println((new Integer(ivCrossoverPopSize)).toString());
			ivCrossoverPopSize++;
		}
	}
	// apply recombination % on these individuals, do not use 100%
	//ivCrossoverPopSize=(int)(Math.floor(Math.random()*population));
//	System.out.println((new Integer(ivCrossoverPopSize)).toString());

	// make sure that we have an even number for pairing
	if(ivCrossoverPopSize-((int)(Math.floor(ivCrossoverPopSize/2)*2))==1)
		// either add one or subract one from an odd number
		if((Math.random()*256)>128) {
			//ivCrossoverPopSize++;
		} else {
			ivCrossoverPopSize--;
		}
//	System.out.println("ivCrossoverPopSize: "+(new Integer(ivCrossoverPopSize)).toString());
	// fix any over/underflow potentials
	if(ivCrossoverPopSize<0)
		ivCrossoverPopSize=0;
	if(ivCrossoverPopSize>population)
		ivCrossoverPopSize=population;

//	// get the indexed selected for crossover
//	for(int p=0;p<ivCrossoverPopSize) {
//		ivNewPopulationIndexedForCrossover[p]=dvProbRecombination[p]
//	}
	
	// copy over all the selected individuals to new array
	System.out.print("selected: ");
	for(int p=0;p<population;p++) {
		for(int node=0;node<izTotalNumberNodes;node++) {
			// move old to new, invoke copy contructor
			// perform mutation only on the back end
			antSwarmsNext[p][node]=new Ant4(antSwarms[ivNewPopulationIndexes[p]][node],(double)(0.0),bvMutateAllBits);
		}
		System.out.print((new Integer(ivNewPopulationIndexes[p])).toString()+" ");
	}
	System.out.println();

	// perform crossover
	System.out.print("crossover: ");
	for(int p=0;p<ivCrossoverPopSize;p+=2) {
		// crossover at the same point on the genotype for all genes
		crossoverPoint=(int)(Math.floor(Math.random()*izMaxSimulationSteps));
		for(int node=0;node<izTotalNumberNodes;node++) {
			// crossover at random points on the genotype for all genes
			if(!bvCrossoverPointEqual)
				crossoverPoint=(int)(Math.floor(Math.random()*izMaxSimulationSteps));

// 			antSwarmsNext[ivNewPopulationIndexedForCrossover[p]][node].performCrossover(
//				antSwarmsNext[ivNewPopulationIndexedForCrossover[p+1]][node],
//				crossoverPoint,
//				dzProbMutation);

 			antSwarmsNext[ivNewPopulationIndexedForCrossover[p]][node].performCrossoverWithMutation(
				antSwarmsNext[ivNewPopulationIndexedForCrossover[p+1]][node],
				crossoverPoint,
				dzProbMutation,
				bvMutateAllBits);
			// crossover genes only
 /*			if((Math.random()*256)>128) {
				// switch genes
				holdingAnt=antSwarmsNext[ivNewPopulationIndexedForCrossover[p]][node];
				antSwarmsNext[ivNewPopulationIndexedForCrossover[p]][node]=
					antSwarmsNext[ivNewPopulationIndexedForCrossover[p+1]][node];
				antSwarmsNext[ivNewPopulationIndexedForCrossover[p+1]][node]=holdingAnt;
			} else {
				// keep the same gene
			}
*/
		}
		System.out.print((new Integer(ivNewPopulationIndexedForCrossover[p])).toString()+"-"+
			(new Integer(ivNewPopulationIndexedForCrossover[p+1])).toString()+" ");
	}
	System.out.println();
/*
	// verify that the genotype is equal
	for(int p=0;p<population;p++) {
		for(int node=0;node<izTotalNumberNodes;node++) {
			for(int i=0;i<antSwarms[0][0].getGenotypeLength();i++) {
				if(!(antSwarms[ivNewPopulationIndexes[p]][node].getGenotypeAllele(i)==antSwarmsNext[p][node].getGenotypeAllele(i)))
					System.out.println((new Integer(p)).toString()+":"+
						(new Integer(node)).toString()+":"+
						(new Integer(i)).toString());
			}
		}
	}
*/

	// bug:2000nov16 forgot to move new ants back to old array
	// copy over all the selected individuals to new array
	for(int p=0;p<population;p++) {
		for(int node=0;node<izTotalNumberNodes;node++) {
			// move new back to old
			antSwarms[p][node]=new Ant4(antSwarmsNext[p][node],dzProbMutation,bvMutateAllBits);
		}
	}
/*	for(int p=0;p<population;p++) {
		System.out.print("\nS " +(new Integer(p)).toString()+" ");
		for(int node=0;node<izTotalNumberNodes;node++) {
			// move new back to old
			System.out.print((new Integer(node)).toString()+":"+
				(new Integer(antSwarms[p][node].getTimeToSolution())).toString()+" ");

		}
	}
	System.out.println();
*/	
	// play a sound
//   	if(bvApplet)
//   		sound.play();
}
private void performSelectionAndCrossoverWithOutMutation() {
}

private void initializeSimulation() {}

// clear all arrays and objects for the next generation
private void resetSwarms() {
	//
	dvTotalFitness=0;
	ivWorstSwarmFitness=izMaximumSwarmFitnessPossible;
	ivBestSwarmFitness=0;
	for(int s=0;s<population;s++) {
		//System.out.print((new Integer(s)).toString()+":"+(new Integer(iaSwarmTimeToSolution[s])).toString()+" ");
		// initialize fitness to 1, needed to combat division by zero
		//ivAbsoluteFitnessPrev[i]=ivAbsoluteFitness[i];
		ivAbsoluteFitness[s]=1;
//		iaSwarmTimeToSolution[s]=izMaxSimulationSteps;
		for(int n=0;n<izTotalNumberNodes;n++) {
			totalMoves[s][n]=0;
		}
		//dvProbSelection = new double[population];
		//dvCumulativeProbSelection = new double[population];
		//ivNewPopulationIndexes = new int[population];
	}

	// initialize board
	loadBoardParadc((int)((izGridWidth-izCircuitExtentWidth)/2)
		,(int)((izGridHeight-izCircuitExtentHeight)/2));
	resetSwarmParadc((int)((izGridWidth-izCircuitExtentWidth)/2)
		,(int)((izGridHeight-izCircuitExtentHeight)/2));
	//loadRandomPopulation(); // comment after crossover complete
	System.out.println();
}

// executed on each anim frame
private void simulationTimeStep(Graphics g) {
	int x,y,l;
	boolean solved=false;
	//Ant4 anAnt;
	//System.out.println("_simulationTimeStep()");

	// first place all the ants in the current swarm
/*	for(int n=0;n<izTotalNumberNodes;n++) {
		ivBoard[][0][antSwarms[cSwarm][n].getPosx()][antSwarms[cSwarm][n].getPosy()]=antSwarms[
	}
*/
	if((!singleStepSimulation)||(advanceSingleStep)) {
		advanceSingleStep=false;

//	if(ivFrames>2000) {
//		ivFrames=0;
//		init();
//	} 

		// update frame and frameflip count
		ivFrames++;
		if(ivFrameCount<ivFrameFlipCount) {
			bvOddFrame=false;
			ivFrameCount++;
		} else {
			if(ivFrameCount<ivFrameFlipCount*2) {
				bvOddFrame=true;
				ivFrameCount++;
			} else {
				bvOddFrame=false;
				ivFrameCount=0;
			}				
		}

		// advance swarm count
//		if(solved)
//			ivFrames2=0;

		if(ivFrames2<izMaxSimulationSteps) {
			ivFrames2++;
			solved=determineIfCurrentSwarmIsSolved(g);
		} else {
			solved=determineIfCurrentSwarmIsSolved(g);
			ivFrames2=0;
			//ivFrames=0;
		}
	
		// advance swarm
		if(ivFrames2==0) {
			if(cSwarm<population-1) {
//				System.out.println("0_swarm: " +	(new Integer(cSwarm)).toString());
				computeFitness(g,solved);
				cSwarm++;
				//bComputeFitness
			} else {
//				System.out.println("1_swarm: " +	(new Integer(cSwarm)).toString());
				computeFitness(g,solved);
				cSwarm=0;
				
			 	g.setColor(Color.cyan);
				g.drawString("Initializing Gen: " + (new Long(lvGeneration)).toString(),4*izSpacing,4*izSpacing+10);
				
				ivFrames=0;
				performSelectionAndCrossoverWithMutation();
				resetSwarms();
				//determineIfAllSwarmsAreCyclicStable(g);
				lvGeneration++;
			}
			//loadBoardParadc(7,1);
			//init();
		}

		for(int n=0;n<izTotalNumberNodes;n++) {
			// don't compute for finished ants
			//if(!antSwarms[cSwarm][n].getcEnd()) {
			if(!bvDestFound[cSwarm][n]) {
				//anAnt=antSwarms[cSwarm][n];
				//if(anAnt.getNodeId()==0)
				//	System.out.println("before ivBoard:" + 
				//	(new Integer(ivBoard[][anAnt.getLayer()][anAnt.getPosx()][anAnt.getPosy()])).toString());

				// bug must recompute for switch layers as well
				if(antSwarms[cSwarm][n].computeNextState()>0) {
					// recompute positions
					// to deal withe the case that 2 ants are 3 cells apart, aproaching each other
					// they will both have clearance to occupy the center cell unless we update the
						// proximity after every move
					//if(anAnt.getNodeId()==0)
					//	System.out.println("after ivBoard:" + 
					//	(new Integer(ivBoard[][anAnt.getLayer()][anAnt.getPosx()][anAnt.getPosy()])).toString());

					// leave a path, net specific only
					x=antSwarms[cSwarm][n].getPosx();
					y=antSwarms[cSwarm][n].getPosy();
					l=antSwarms[cSwarm][n].getLayer();
					ivBoardTraceId[cSwarm][l][x][y]=(byte)(antSwarms[cSwarm][n].getNodeId()+1);
					if(ivBoard[cSwarm][l][x][y]<izDeviceValue)
						ivBoard[cSwarm][l][x][y]=antSwarms[cSwarm][n].getNetId()+1;

					updateProximity(); //2000oct30
					totalMoves[cSwarm][n]+=1;
				}
				//anAnt.getPosx();
			} else { // 2000nov12
				updateProximity();
			}
		}
	}
}

private void writeStatistics() {
	int max;
	int swarmBestTime=izMaxSimulationSteps;
	System.out.println("Gen:"+(new Long(lvGeneration)).toString()+"\tFitness: " +
		(new Double(dvTotalFitness)).toString());
	for(int s=0;s<population;s++) {
		max=0;		
		for(int n=0;n<izTotalNumberNodes;n++) {
			if(max<antSwarms[s][n].getTimeToSolution())
				max=antSwarms[s][n].getTimeToSolution();
		}			  
		iaSwarmTimeToSolution[s]=max;
		// get current swarm best time
		if(swarmBestTime>iaSwarmTimeToSolution[s])
			swarmBestTime=iaSwarmTimeToSolution[s];

		// look for best time, min of the max
		if(ivBestTime>iaSwarmTimeToSolution[s]) {
			ivBestTime=iaSwarmTimeToSolution[s];
			System.out.println("Min Time\t" + (new Integer(ivBestTime)).toString() +
				"\tG: "+(new Long(lvGeneration)).toString() +
				"\tS: "+(new Integer(s)).toString());

			for(int node=0;node<izTotalNumberNodes;node++) {
				// write out swarm to file
				if(!bvApplet) {
	 				antSwarms[s][node].writeGenomeToFile("log\\bt_g"+(new Long(lvGeneration)).toString()+"s"+(new Integer(s)).toString()+"n"+(new Integer(node)).toString()+"_t"+(new Integer(ivBestTime)).toString()+".txt");
				}
		   		bestAntSwarm[node]=antSwarms[s][node];
   				//System.out.print(" " + (new Integer(bestAntSwarm[node].getNodeId())).toString());
			}
		}
	}

  	// write output header to disk for non-applets
  	if(!bvApplet) {
  		try {
  			// create output stream
  			outFile = new DataOutputStream(new FileOutputStream("out.txt",true));
//  			outFile = new DataOutputStream(new FileOutputStream(
//  				"log\\pheno_bf_g"+(new Long(lvGeneration)).toString()+
//  				"s"+(new Integer(s)).toString()+
//  				"n"+(new Integer(node)).toString()+
//  				"_t"+(new Integer(ivBestTime)).toString()+
//  				".txt",true));
  			outFile.writeBytes(
  				"G:\t"+ (new Long(lvGeneration)).toString() +
				"\tBT:\t"+ (new Integer(swarmBestTime)).toString() + 
				"\tTF:\t"+ (new Double(dvTotalFitness)).toString() + 
				"\tBF:\t"+ (new Integer(ivBestSwarmFitness)).toString() + 
				"\tWF:\t"+ (new Integer(ivWorstSwarmFitness)).toString() + 
//				"\tBFE:\t"+ (new Integer(ivBestSwarmFitnessEver)).toString() + 
//				"\tWFE:\t"+ (new Integer(ivWorstSwarmFitnessEver)).toString() + 
				"\r\n");
   			outFile.close();
  		} catch (Exception e) {
  			String err = e.toString();
  			System.out.println(err);
  		}
	}
}

private void outputPhenotypeToDisk() {
	int 	cell=0;
	int		total=0;

  	if(bvApplet) return;

	// open file
	try {
		// create output stream
		phenoFile = new DataOutputStream(new FileOutputStream(
			"log\\pheno_bf_g"+(new Long(lvGeneration)).toString()+
			"s"+(new Integer(cSwarm)).toString()+
			"_f"+(new Integer(ivBestSwarmFitnessEver)).toString()+
			".txt",true));
  	} catch (Exception e) {
  		String err = e.toString();
  		System.out.println(err);
  	}

	for(int i=0;i<izGridHeight;i++) {
		for(int j=0;j<izGridWidth;j++) {
			cell=0;
			if(ivBoard[cSwarm][0][j][i]>=izDeviceValue)
				cell=1024;
			else
				if(ivBoard[cSwarm][0][j][i]>0)
				cell+=ivBoard[cSwarm][0][j][i]*izNumberNets*2;

			if(ivBoard[cSwarm][1][j][i]>=izDeviceValue)
				cell=1024;
			else
				if(ivBoard[cSwarm][1][j][i]>0)
				cell+=ivBoard[cSwarm][1][j][i]*(izNumberNets*2)*(izNumberNets*2);

			// write cell to disk
	  		try {
				if(cell>0)
					phenoFile.writeBytes(
						"\t"+ (new Integer(cell)).toString());
				else
					phenoFile.writeBytes("\t");
	  		} catch (Exception e) {
				String err = e.toString();
				System.out.println(err);
  			}
		}
		// write cr/lf
  		try {
			phenoFile.writeBytes("\r\n");
	  	} catch (Exception e) {
  			String err = e.toString();
  			System.out.println(err);
	  	}

		total+=cell;
	}

	// write area
	try {
		phenoFile.writeBytes("\t"+ (new Integer(total)).toString() + "\r\n");
	} catch (Exception e) {
  		String err = e.toString();
  		System.out.println(err);
  	}

	// close file
	try {
		phenoFile.close();
	} catch (Exception e) {
		String err = e.toString();
		System.out.println(err);
	}	
}


// display genome statistics
private void displayStatistics() {
	// draw Ants
	int px,py,prox,l;
	Ant4 anAnt;
	int proxAnt[];
	int sx=izMaxHeight-36;
	int sy=50;
	int hy=13;

	// global stats
 	gContext.setColor(Color.white);
	gContext.drawString("POP: " + (new Long(lvGeneration)).toString(),sx,sy-36);
	gContext.drawString("SF: " + (new Integer(ivFrames2)).toString() + " GF: " + (new Integer(ivFrames)).toString() ,sx+80,sy-36);
 	gContext.setColor(Color.blue);
	gContext.drawString("Nd",sx,sy-16);
	gContext.drawString("Nt",sx+16,sy-16);
	gContext.drawString("X",sx+32,sy-16);
	gContext.drawString("Y",sx+48,sy-16);
	gContext.drawString("L",sx+68,sy-16);
	gContext.drawString("Px",sx+80,sy-16);
	gContext.drawString("M",sx+96,sy-16);
	gContext.drawString("T",sx+112,sy-16);
	gContext.drawString("S",sx+120,sy-16);
	gContext.drawString("D",sx+128,sy-16);
	gContext.drawString("D",sx+136,sy-16);
	gContext.drawString("H",sx+144,sy-16);
	

	// draw box around selected node
	gContext.setColor(Color.green);
	gContext.drawRect(sx-20,sy-12+hy*selectedAnt.getNodeId(),200,hy);

	for(int n=0;n<izTotalNumberNodes;n++) {
		anAnt=antSwarms[cSwarm][n];
		//px=anAnt.getPosy();	// traced bug for last 24 hours here 1419 2000oct30
		//py=anAnt.getPosx();
		py=anAnt.getPosy();
		px=anAnt.getPosx();
		l=anAnt.getLayer();
		//System.out.println("_px,py: " +	(new Integer(px)).toString()+","+(new Integer(px)).toString());
		gContext.setColor(Color.cyan);
		gContext.drawString((new Integer(n)).toString(),sx,sy+hy*n);
		gContext.drawString((new Integer(anAnt.getNetId())).toString(),16+sx,sy+hy*n);
		if((px==1)||(px==izGridWidth-2))
			gContext.setColor(Color.red);
		else
			gContext.setColor(Color.cyan);

		gContext.drawString((new Integer(px)).toString(),32+sx,sy+hy*n);
		if((py==1)||(py==izGridHeight-2))
			gContext.setColor(Color.red);
		else
			gContext.setColor(Color.cyan);

		gContext.drawString((new Integer(py)).toString(),48+sx,sy+hy*n);

		// draw proximity grid
		gContext.setColor(color_none);
		gContext.fillRect(80+sx-4,sy-6+hy*n-4,12,12);

	// direction:				values:
	// 0-11 10du swne swne		0 = empty			4 = device
	//      0123 4567 8901		1 = trace other		5 = Ant other
	//    p 1098 7654 3210		2 = trace twin		6 = Ant twin
	//                6745		3 = trace self		7 = target in sight
		// draw 4 prox cells		
		for(int d=0;d<4;d++) {
			prox=enumerateBoardCell(anAnt.getLayer(), anAnt, d, px+xdir[d],py+ydir[d],anAnt.getNetId());
			if(prox>0) {
				switch (prox) {
					case 1:
						gContext.setColor(Color.red);
						gContext.drawRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
					case 2:
						gContext.setColor(Color.green);
						gContext.drawRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
					case 3:
						gContext.setColor(Color.white);
						gContext.drawRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
					case 4:
						gContext.setColor(color_device);
						gContext.fillRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
					case 5:
						gContext.setColor(Color.red);
						gContext.fillRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
					case 6:
						gContext.setColor(Color.green);
						gContext.fillRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
					case 7:
						gContext.setColor(Color.blue);
						gContext.fillRect(80+sx+xdir[d]*4,	sy-6+hy*n+ydir[d]*4, 4,4);
						break;
				}
			}
		}
		
		if(anAnt.getcEnd())
			gContext.setColor(Color.white);
		else
			gContext.setColor(Color.pink);
//		gContext.setColor(Color.cyan);
		//gContext.drawString((new Integer(totalMoves[cSwarm][anAnt.getNodeId()])).toString(),
		//		95+izMaxHeight+8*3,
		//		40+hy*n);
		//gContext.drawString((new Integer(anAnt.getcMoveSuccessfull())).toString(),
		gContext.drawString((new Integer(l)).toString(),68+sx,sy+hy*n);

		gContext.drawString((new Integer(anAnt.getcMoveSuccessfull())).toString(),96+sx+8*0,sy+hy*n);
		if((anAnt.getcAlleleValue() & 16)> 0)
			gContext.drawString((new Integer("1")).toString(),112+sx,sy+hy*n);
		else
			gContext.drawString((new Integer("0")).toString(),112+sx,sy+hy*n);
		if((anAnt.getcAlleleValue() & 8)> 0)
			gContext.drawString((new Integer("1")).toString(),120+sx,sy+hy*n);
		else
			gContext.drawString((new Integer("0")).toString(),120+sx,sy+hy*n);
		if((anAnt.getcAlleleValue() & 4)> 0)
			gContext.drawString((new Integer("1")).toString(),128+sx,sy+hy*n);
		else
			gContext.drawString((new Integer("0")).toString(),128+sx,sy+hy*n);
		if((anAnt.getcAlleleValue() & 2)> 0)
			gContext.drawString((new Integer("1")).toString(),136+sx,sy+hy*n);
		else
			gContext.drawString((new Integer("0")).toString(),136+sx,sy+hy*n);
		if((anAnt.getcAlleleValue() & 1)> 0)
			gContext.drawString((new Integer("0")).toString(),144+sx,sy+hy*n);
		else
			gContext.drawString((new Integer("1")).toString(),144+sx,sy+hy*n);

/*		gContext.drawString((new Integer(anAnt.getcAllelePosition())).toString(),
				95+izMaxHeight+8*5,
				40+hy*n);
*/
	}
}

// override update to eliminate flicker
public void update(Graphics g){paint(g);}


private void loadRandomPopulation() {
	
	// create a swarm of Ant objects with random genomes
	// 
	//for(int i=0;i<

	/*
		//if((int)(Math.floor(Math.random()*512))>500) {
		//	for(int i=0;i<izGridWidth;i++)
		//		for(int j=0;j<izGridHeight;j++)
		//			if(ivBoard[cSwarm][][i][j]<izDeviceValue)
		//				//ivBoard[cSwarm][][i][j]=(int)(Math.floor(Math.random()*512));
		//				ivBoard[cSwarm][][i][j]=511;//i*izGridHeight+j;
		//} else {
			for(int i=0;i<izGridWidth;i++)
				for(int j=0;j<izGridHeight;j++)
					if(ivBoard[cSwarm][][i][j]<izDeviceValue)
						ivBoard[cSwarm][][i][j]=(int)(Math.floor(Math.random()*izDeviceValue));
		//}
	*/
}

private void copyGenerationTo(int gen) {
// 	for(int i=0;i<izGridWidth;i++)
// 		for(int j=0;j<izGridHeight;j++)
// 			ivBoard[cSwarm][]Genome[i][j][gen]=ivBoard[cSwarm][][i][j];
}


private void loadBoardParadc(int x, int y) {
	for(int p=0;p<population;p++) {
		// clear board
		for(int l=0;l<2;l++) {
			for(int i=0;i<izGridHeight;i++) {
				for(int j=0;j<izGridWidth;j++) {
					// clear board and trace net id's
					ivBoard[p][0][j][i]=0;
					ivBoard[p][1][j][i]=0;
					// clear Ant trace id's
					ivBoardTraceId[p][0][j][i]=0;
					ivBoardTraceId[p][1][j][i]=0;
				}
			}
		}
		// initialize edges of board
		for(int i=0;i<izGridHeight;i++) {
			ivBoard[p][0][0][i]=izBoardEdgeValue;
			ivBoard[p][1][0][i]=izBoardEdgeValue;
			ivBoard[p][0][izGridWidth-1][i]=izBoardEdgeValue;
			ivBoard[p][1][izGridWidth-1][i]=izBoardEdgeValue;
		}
		for(int i=0;i<izGridWidth;i++) {
			ivBoard[p][0][i][0]=izBoardEdgeValue;
			ivBoard[p][1][i][0]=izBoardEdgeValue;
			ivBoard[p][0][i][izGridHeight-1]=izBoardEdgeValue;
			ivBoard[p][1][i][izGridHeight-1]=izBoardEdgeValue;
		}

/*		// and some extra walls
		for(int i=0;i<izGridHeight;i++) {
			ivBoard[p][0][6][i]=izBoardEdgeValue;
			ivBoard[p][1][6][i]=izBoardEdgeValue;
			ivBoard[p][0][izGridWidth-7][i]=izBoardEdgeValue;
			ivBoard[p][1][izGridWidth-7][i]=izBoardEdgeValue;
		}	
*/
		// initialize devices
		for(int i=0;i<izDeviceNodes.length;i+=2) {	
			ivBoard[p][0][izDeviceNodes[i]+x][izDeviceNodes[i+1]+y]=izDeviceValue;
			ivBoard[p][1][izDeviceNodes[i]+x][izDeviceNodes[i+1]+y]=izDeviceValue;
		}			
	}
}

private void loadSwarmParadc(int x, int y) {
	int lNodeCount=0;
	Ant4 anAnt;

 	// initialize node data structures
	izPopulationPerNode=1;
	izNumberNets=izNetNodes.length;
	izNumberNodes=0;
	for(int i=0;i<izNumberNets;i++)
		izNumberNodes+=(int)(izNetNodes[i].length/2);
	izTotalNumberNodes=izNumberNodes*izPopulationPerNode;

	// initialize fitness stats
	totalMoves = new int[population][izTotalNumberNodes];

  	izNetNodeCount = new int[izNumberNets];
	izNetNodeX = new int[izNumberNets][izTotalNumberNodes];
	izNetNodeY = new int[izNumberNets][izTotalNumberNodes];
	if(!bQuietInit) {
		System.out.println("_izNumberNodes: " + (new Integer(izNumberNodes)).toString());
		System.out.println("_izNumberNets: " + (new Integer(izNumberNets)).toString());
	}
	// initialize Ant objects, must use object pool, move all [] below too
	if(!bQuietInit) {
		antSwarmsNext= new Ant4[population][izTotalNumberNodes];
		antSwarms = new Ant4[population][izTotalNumberNodes];
		bestAntSwarm = new Ant4[izTotalNumberNodes];
		ivBullsEye = new int[population][izTotalNumberNodes];
		bvDestFound = new boolean[population][izTotalNumberNodes];
	}
	for(int i=0;i<population;i++) {
		if(!bQuietInit) {
		System.out.println("Population: " + (new Integer(i)).toString());
		}
		// initialize fitness to 1, needed to combat division by zero
		ivAbsoluteFitness[i]=1;
		// set solved time to an infeasible number, to come down later during proc
//		iaSwarmTimeToSolution[i]=izTotalNumberNodes*izMaxSimulationSteps+1;
		iaSwarmTimeToSolution[i]=izMaxSimulationSteps;
		//ivAbsoluteFitnessPrev[i]=1;
		for(int j=0;j<izNumberNodes;j++)
			for(int k=0;k<izPopulationPerNode;k++) {
				antSwarms[i][j*izPopulationPerNode+k]=new Ant4(j*izPopulationPerNode+k,izMaxSimulationSteps+1);
//				if(!bQuietInit) {
//				System.out.println("Genotype completed for " + (new Integer(j)).toString());
//				}
				//System.out.println("_loadBoardExample: " + (new Integer(x)).toString() + "," + (new Integer(y)).toString());
			}
	}

	for(int k=0;k<izPopulationPerNode;k++) {
		lNodeCount=0;
		for(int j=0;j<izNumberNets;j++) {
			izNetNodeCount[j]=(int)(izNetNodes[j].length/2);
			for(int i=0;i<((int)(izNetNodes[j].length/2));i++) {
				izNetNodeX[j][k+i]=izNetNodes[j][i*2]+x; izNetNodeY[j][k+i]=izNetNodes[j][i*2+1]+y;
				for(int p=0;p<population;p++) {
					antSwarms[p][lNodeCount].setPosx(izNetNodeX[j][k+i]);
					antSwarms[p][lNodeCount].setPosy(izNetNodeY[j][k+i]);
					ivBullsEye[p][lNodeCount]=izMaxBullsEye+izBullsEyeDecrement;
					bvDestFound[p][lNodeCount]=false;

				}
				lNodeCount++;
			}
		}
	}

	for(int p=0;p<population;p++) {	
		lNodeCount=0;
		for(int n=0;n<izNumberNets;n++) {
			for(int i=0;i<izNetNodeCount[n];i++) {
				for(int k=0;k<izPopulationPerNode;k++) {
					anAnt=antSwarms[p][lNodeCount];
					// set src, dest nodes
					//ivBoard[][][anAnt.getPosx()][anAnt.getPosy()]=256;
					if(i==izNetNodeCount[n]-1) {
						anAnt.setDestx(izNetNodeX[n][0]);
						anAnt.setDesty(izNetNodeY[n][0]);
					} else {
						anAnt.setDestx(izNetNodeX[n][i+1]);
						anAnt.setDesty(izNetNodeY[n][i+1]);
					}
					//System.out.println("_n,i: "+(new Integer(n)).toString()+","+(new Integer(i)).toString());
					totalMoves[p][lNodeCount]=0;

					// set sequential properties
					anAnt.setNetId(n);
					anAnt.setLayer(0);
					anAnt.setNodeId(lNodeCount);
					anAnt.setSrcx(anAnt.getPosx());
					anAnt.setSrcy(anAnt.getPosy());
					//System.out.println((new Integer(lNodeCount)).toString());
					lNodeCount++;
				}
			}
		}
	}
	// default selectedAnt
	selectedAnt=antSwarms[0][0];
	if(!bQuietInit) {
	System.out.println("Initialization Complete....");
	}
}

private void resetSwarmParadc(int x, int y) {
	int lNodeCount=0;
	Ant4 anAnt;
						
 	// initialize node data structures
	izPopulationPerNode=1;
	izNumberNets=izNetNodes.length;
	izNumberNodes=0;
	for(int i=0;i<izNumberNets;i++)
		izNumberNodes+=(int)(izNetNodes[i].length/2);
	izTotalNumberNodes=izNumberNodes*izPopulationPerNode;

	// initialize fitness stats
//	totalMoves = new int[population][izTotalNumberNodes];

  	izNetNodeCount = new int[izNumberNets];
	izNetNodeX = new int[izNumberNets][izTotalNumberNodes];
	izNetNodeY = new int[izNumberNets][izTotalNumberNodes];
	if(!bQuietInit) {
		System.out.println("_izNumberNodes: " + (new Integer(izNumberNodes)).toString());
		System.out.println("_izNumberNets: " + (new Integer(izNumberNets)).toString());
	}
	// initialize Ant objects, must use object pool, move all [] below too
//	if(!bQuietInit) {
//		antSwarmsNext= new Ant4[population][izTotalNumberNodes];
//		antSwarms = new Ant4[population][izTotalNumberNodes];
//		bestAntSwarm = new Ant4[izTotalNumberNodes];
//		ivBullsEye = new int[population][izTotalNumberNodes];
//		bvDestFound = new boolean[population][izTotalNumberNodes];
//	}
	for(int i=0;i<population;i++) {
		if(!bQuietInit) {
		System.out.println("Population: " + (new Integer(i)).toString());
		}
		// initialize fitness to 1, needed to combat division by zero
		ivAbsoluteFitness[i]=1;
		// set solved time to an infeasible number, to come down later during proc
//		iaSwarmTimeToSolution[i]=izTotalNumberNodes*izMaxSimulationSteps+1;
		iaSwarmTimeToSolution[i]=izMaxSimulationSteps;
		//ivAbsoluteFitnessPrev[i]=1;
		for(int j=0;j<izNumberNodes;j++)
			for(int k=0;k<izPopulationPerNode;k++) {
				antSwarms[i][j*izPopulationPerNode+k].reset();
//				antSwarms[i][j*izPopulationPerNode+k]=new Ant4(j*izPopulationPerNode+k,izMaxSimulationSteps+1);
//				if(!bQuietInit) {
//				System.out.println("Genotype completed for " + (new Integer(j)).toString());
//				}
				//System.out.println("_loadBoardExample: " + (new Integer(x)).toString() + "," + (new Integer(y)).toString());
			}
	}

	for(int k=0;k<izPopulationPerNode;k++) {
		lNodeCount=0;
		for(int j=0;j<izNumberNets;j++) {
			izNetNodeCount[j]=(int)(izNetNodes[j].length/2);
			for(int i=0;i<((int)(izNetNodes[j].length/2));i++) {
				izNetNodeX[j][k+i]=izNetNodes[j][i*2]+x; izNetNodeY[j][k+i]=izNetNodes[j][i*2+1]+y;
				for(int p=0;p<population;p++) {
//					antSwarms[p][lNodeCount].setPosx(izNetNodeX[j][k+i]);
//					antSwarms[p][lNodeCount].setPosy(izNetNodeY[j][k+i]);
					ivBullsEye[p][lNodeCount]=izMaxBullsEye+izBullsEyeDecrement;
					bvDestFound[p][lNodeCount]=false;

				}
				lNodeCount++;
			}
		}
	}

	for(int p=0;p<population;p++) {	
		lNodeCount=0;
		for(int n=0;n<izNumberNets;n++) {
			for(int i=0;i<izNetNodeCount[n];i++) {
				for(int k=0;k<izPopulationPerNode;k++) {
					anAnt=antSwarms[p][lNodeCount];
					// set src, dest nodes
					//ivBoard[][][anAnt.getPosx()][anAnt.getPosy()]=256;
					if(i==izNetNodeCount[n]-1) {
						anAnt.setDestx(izNetNodeX[n][0]);
						anAnt.setDesty(izNetNodeY[n][0]);
					} else {
						anAnt.setDestx(izNetNodeX[n][i+1]);
						anAnt.setDesty(izNetNodeY[n][i+1]);
					}
					//System.out.println("_n,i: "+(new Integer(n)).toString()+","+(new Integer(i)).toString());
					totalMoves[p][lNodeCount]=0;

					// set sequential properties
//					anAnt.setNetId(n);
					anAnt.setLayer(0);
					anAnt.setNodeId(lNodeCount);
					anAnt.setSrcx(anAnt.getPosx());
					anAnt.setSrcy(anAnt.getPosy());
					//System.out.println((new Integer(lNodeCount)).toString());
					lNodeCount++;
				}
			}
		}
	}
	// default selectedAnt
	selectedAnt=antSwarms[0][0];
	if(!bQuietInit) {
	System.out.println("Initialization Complete....");
	}
}


	private void setValues(String event, int x, int y) {
		s=event; xPos=x; yPos=y; //repaint();
	}

	public void mouseClicked(MouseEvent e) { 
		setValues("Clicked",e.getX(),e.getY());
		//advanceSingleStep=true;
 	}

	public void mousePressed(MouseEvent e) { 
		setValues("Pressed",e.getX(),e.getY());
	}

	public void mouseReleased(MouseEvent e)	{ 
		setValues("Released",e.getX(),e.getY());
	}

	public void mouseEntered(MouseEvent e) { 
		setValues("Entered",e.getX(),e.getY());
	}

	public void mouseExited(MouseEvent e) { 
		setValues("Exited",e.getX(),e.getY());
	}

	public void mouseDragged(MouseEvent e) {
		setValues("Dragging",e.getX(),e.getY());
		Ant4 anAnt;
		//gContext.setColor(Color.cyan);
		//gContext.drawString((new Integer(e.getX())).toString(),e.getX(),e.getY());
		//gContext.drawString((new Integer(e.getY())).toString(),e.getX()+30,e.getY());
		for(int n=0;n<izTotalNumberNodes;n++) {
			anAnt=antSwarms[cSwarm][n];
			if((Math.abs(10+anAnt.getPosx()*izSpacing+izSpacing4+izSpacing2-e.getX())<=izSpacing3)&&(Math.abs(10+anAnt.getPosy()*izSpacing+izSpacing2+izSpacing4-e.getY())<=izSpacing3))
				selectedAnt=anAnt;				
		}							   
	}

	public void mouseMoved(MouseEvent e) {
		setValues("Moving",e.getX(),e.getY());
		advanceSingleStep=true;

	}

	// capture popup menu event
	public void processMouseEvent(MouseEvent e) {
		if(e.isPopupTrigger())
			aPopupMenu.show(this,e.getX(),e.getY());
		//super.processMouseEvent(e); // only when not sublassed twice
	}
	
	// process popup menu event
	public void actionPerformed(ActionEvent e) {
//	private String svPopupNames[] = {"Single Step Mode On","Single Step Mode Off","Reset"};
		//"Single Step Mode On"
		if(e.getSource()==thePopupMenuItems[0]) {
			singleStepSimulation=true;
			advanceSingleStep=true;
		}
		
		//"Single Step Mode Off"
		if(e.getSource()==thePopupMenuItems[1]) {
			singleStepSimulation=false;
		}
		
		//"Reset"
		if(e.getSource()==thePopupMenuItems[2]) {
			init();
			loadBoardParadc(7,1);
		}

		// Load Best Swarm
		if(e.getSource()==thePopupMenuItems[3]) {
			init();
			//loadBoardParadc(7,1);
			for(int n=0;n<izTotalNumberNodes;n++) {
				antSwarms[0][n]=bestAntSwarm[n];
			}
		}
	}	

// start the applet
public void start() {
	super.start();
}

public void stop() {
	super.stop();
}

//static boolean bvApplet=true;
// allow application use outside of browser, appletviewer
public static void main(String args[]) {
	bvApplet=false;
	// cl-parameters = Population MaxHeight MaxWidth GridHeight MutationMultiplier dzReplacementRatio
	// enumerate arguments passed from the console
	if(args.length > 10) {
		//for(int i=0;i<args.length;i++)
		//	if(args[i].equals("-debug")) 
				//bzDebug = true; // set global debug flag
		//	else { usage(); System.exit(0); }
		// get arguments
		population=Integer.parseInt(args[0]);	
		izMaxHeight=Integer.parseInt(args[2]);
		izMaxWidth=Integer.parseInt(args[1]);
		izGridHeight=Integer.parseInt(args[4]);
		izGridWidth=Integer.parseInt(args[3]);
		dzProbMutation=Double.parseDouble(args[5]);	// out of 10000
		dzProbCrossover=Double.parseDouble(args[6]);
		izMaxSimulationSteps=Integer.parseInt(args[7]);
		if(Integer.parseInt(args[8])>0) {
			// when running as an app, adjust screen size depending on stats
			izMaxWidth=izMaxHeight+160;
			bvShowStatistics=true;
		} else {
			izMaxWidth=izMaxHeight;
			bvShowStatistics=false;
		}
		// set flag for crossover point equality
		if(Integer.parseInt(args[9])>0) {
			bvCrossoverPointEqual=true;
		} else {
			bvCrossoverPointEqual=false;
		}
		// set flag for mutation of all bits=1, mutation of crossover only=0
		if(Integer.parseInt(args[10])>0) {
			bvMutateAllBits=true;
		} else {
			bvMutateAllBits=false;
		}

		//izNumberNets=Integer.parseInt(args[6]);
		//izPopulationPerNode=Integer.parseInt(args[7]);
	} else {
		// set defaults
		//bzDebug = false;
		usage(); System.exit(0);
	}	

	Frame aFrame = new Frame("Circuit Router:Genetic Algorithm via State-Machines Swarm-9 (c) F.Michael O'Brien V1.11.20");
	RouterAnimApplet4 anApplet=new RouterAnimApplet4();
	aFrame.add("Center",anApplet);
	aFrame.setSize(izMaxWidth+10,izMaxHeight+30);
	aFrame.show();	
	// must alt-tab out and back in to step past 
	// sun.awt.windows.WGraphics.drawImage(WGraphics.java:360) bug
	// when using double-buffering
	anApplet.init();
	anApplet.start();
}


/** print the expected parameters to the console */
public static void usage() {
	/**
	-debug : turn console debugging output on, default=off
	 : select grid size
	*/
	System.out.println("\nCommand-Line Options are...\njava RouterAnimApplet " +
		"Population pixelWidth pixelHeight GridWidth GridHeight probMutation probCrossover MaxSimulationSteps" + 
		" [0/1 showStatistics] [0/1 crossover point equal for genes] [0/1 mutateAllBits?][-debug]\n");}
 }


