/*
	Second Generation Agent-and-Patch Simulator for the
	EVOLVING ARTIFICIAL MORAL ECOLOGIES project, 
	Centre for Applied Ethics, University of British Columbia.
	Programmer/Designer: William F. Harms
	Supervisor: Peter Danielson	
	
	This simulator is designed as a basic platform which can be easily modified. Agents 
	play prisoner's dilemma tournaments within their patches. They move between patches 
	with a certain fixed probability. They feed on a renewable resource and reproduce at a
	rate determined by their success in play, feeding. There is no cost for movement. 
	
	3/30/99 This version is set up to run either as an Applet "AgentPatchSim.class" or 
	as an Application "AgentPatchApp.class". When run as an Application it generates a 
	REC_______.txt file in the same directory that records the frequencies of each 
	response ("gene") at one hundred cycle intervals. 
	
	5-28-99 this version is for frontier game with graded extinction. Patches with high
	x values have full extinction rates. x=0 has no extinction. Topology is a tube. 
	Agents begin at the left end (low extinction) and migrate into an increasingly
	hostile environment. 
	
	It has also been changed so that all of the necessary source occurs in the one
	.java file. Extra classes are added at the end for convenience in downloading and
	compiling. 
 */

import java.awt.*;
import java.applet.*;
import java.util.Date;
import java.util.Random;
import java.io.*;
import java.awt.event.*;

public final class AgentPatchSim extends Applet implements Runnable
{
	PrintWriter out1 =null;
    boolean writeFile = false;
	
	Queue queue = new Queue();
	int HEIGHT = 25;
	int WIDTH = 25;
	//Payoffs:
	double benefit = 4;
	double cost =  -1;//- benefit/CBRatio;
	
	double pCC = benefit + cost; 
	double pCD = cost;
	double pDC = benefit;
	double pDD = 0;
    
    double[] params = {0.0005,0.001,0.002,0.004,0.008,0.016,
                        0.032,0.064,0.128,0.256,0.512,0};
	
	int cooperators = 10;
	int newResourceLevel = 25;
	int reproLevel = 150;
	Patch[][] patch;
	long worldClock = 0;
	int numAgents = 80;
	//int maxPopSize = 1000;
	int trial = 0;
	int numTrials = 25;
	int run = 1;
	int numRuns = 10;
	int cooperatorTrials = 0;	
	double coopExtinctionTime =0;
	double defExtinctionTime =0;
	boolean move = true;
	boolean feed = true;
	boolean play = true;
	boolean grow =true;
	boolean seed = false;
	boolean draw = true;
	boolean randomStart = true;
	int BLOCK_SIZE;
	double movementRate = 0.02;
	double extinctionRate = 0.03;
	double seedRate = .001;
	Thread simulation;
	String inputString;
	Random rand = new Random();
	long randSeed;
	double mutationRate = 0.00;

	public void init()
	{
		//{{INIT_CONTROLS
		setLayout(new BorderLayout(0,0));
		setSize(700,600);
		setBackground(new Color(0));
		textField1 = new java.awt.TextField();
		textField1.setText("Input Settings ->");
		textField1.setBounds(0,0,736,23);
		textField1.setFont(new Font("Dialog", Font.BOLD, 12));
		textField1.setForeground(new Color(16777215));
		textField1.setBackground(new Color(255));
		add("North", textField1);
		panel2 = new java.awt.Panel();
		panel2.setLayout(null);
		panel2.setVisible(false);
		panel2.setBounds(0,23,633,360);
		panel2.setForeground(new Color(16776960));
		panel2.setBackground(new Color(8421504));
		add("Center", panel2);
		textArea1 = new java.awt.TextArea("",0,0,TextArea.SCROLLBARS_VERTICAL_ONLY);
		textArea1.setBounds(0,383,736,170);
		textArea1.setFont(new Font("Dialog", Font.BOLD, 12));
		textArea1.setForeground(new Color(16777215));
		textArea1.setBackground(new Color(255));
		add("South", textArea1);
		panel1 = new java.awt.Panel();
		panel1.setLayout(new GridLayout(13,1,0,0));
		panel1.setBounds(633,23,103,360);
		panel1.setBackground(new Color(-16744256));
		add("East", panel1);
		button1 = new java.awt.Button();
		button1.setLabel("Reset Simulator");
		button1.setBounds(0,0,103,27);
		button1.setBackground(new Color(12632256));
		panel1.add(button1);
		button2 = new java.awt.Button();
		button2.setLabel("Stop");
		button2.setBounds(0,27,103,27);
		button2.setBackground(new Color(12632256));
		panel1.add(button2);
		button3 = new java.awt.Button();
		button3.setLabel("Run");
		button3.setBounds(0,54,103,27);
		button3.setBackground(new Color(12632256));
		panel1.add(button3);
		button4 = new java.awt.Button();
		button4.setLabel("Step");
		button4.setBounds(0,81,103,27);
		button4.setBackground(new Color(12632256));
		panel1.add(button4);
		label1 = new java.awt.Label("Generation",Label.CENTER);
		label1.setBounds(0,108,103,27);
		label1.setFont(new Font("Dialog", Font.BOLD, 12));
		label1.setForeground(new Color(16777215));
		label1.setBackground(new Color(-16738079));
		panel1.add(label1);
		label2 = new java.awt.Label("",Label.CENTER);
		label2.setBounds(0,135,103,27);
		label2.setForeground(new Color(16777215));
		label2.setBackground(new Color(-16738079));
		panel1.add(label2);
		label3 = new java.awt.Label("Agents",Label.CENTER);
		label3.setBounds(0,162,103,27);
		label3.setFont(new Font("Dialog", Font.BOLD|Font.ITALIC, 12));
		label3.setForeground(new Color(16776960));
		label3.setBackground(new Color(-16751211));
		panel1.add(label3);
		label4 = new java.awt.Label("4",Label.CENTER);
		label4.setBounds(0,189,103,27);
		label4.setForeground(new Color(16776960));
		label4.setBackground(new Color(-16751211));
		panel1.add(label4);
		label5 = new java.awt.Label("Cooperators",Label.CENTER);
		label5.setBounds(0,216,103,27);
		label5.setFont(new Font("Dialog", Font.BOLD, 12));
		label5.setForeground(new Color(16777215));
		label5.setBackground(new Color(-8881988));
		panel1.add(label5);
		label6 = new java.awt.Label("",Label.CENTER);
		label6.setBounds(0,243,103,27);
		label6.setFont(new Font("Dialog", Font.BOLD, 12));
		label6.setForeground(new Color(16777215));
		label6.setBackground(new Color(-8881988));
		panel1.add(label6);
		label7 = new java.awt.Label("Trial: 1",Label.CENTER);
		label7.setBounds(0,270,103,27);
		label7.setFont(new Font("Dialog", Font.BOLD, 12));
		label7.setForeground(new Color(65535));
		panel1.add(label7);
		label8 = new java.awt.Label("Run: 1",Label.CENTER);
		label8.setBounds(0,297,103,27);
		label8.setFont(new Font("Dialog", Font.BOLD, 12));
		label8.setForeground(new Color(16776960));
		panel1.add(label8);
		button5 = new java.awt.Button();
		button5.setLabel("Don't Draw");
		button5.setBounds(0,324,103,27);
		button5.setBackground(new Color(12632256));
		panel1.add(button5);
		//}}

		//{{REGISTER_LISTENERS
		SymAction lSymAction = new SymAction();
		button2.addActionListener(lSymAction);
		button3.addActionListener(lSymAction);
		button4.addActionListener(lSymAction);
		button1.addActionListener(lSymAction);
		SymMouse aSymMouse = new SymMouse();
		this.addMouseListener(aSymMouse);
		button5.addActionListener(lSymAction);
		//}}
		if (!writeFile){
		    String s = this.getParameter("settings");
		    if (s!= null)
		        textField1.setText("<From HTML> "+s);
		}
		outputHeader(); //only happens once
		//movementRate = params[run];
		reset();
		outputVals();
	}

	void reset(){
	    try{ simulation.sleep(100);}
	    catch(InterruptedException e){}
	    randSeed = rand.nextLong();
		inputString = textField1.getText();
		if (inputString != null)
		    readInputValues(inputString);
		rand = new Random(randSeed);
	    patch = new Patch[WIDTH][HEIGHT];
	    Graphics g = this.getGraphics();
		g.clearRect(0,0,600,800);//erase old patch grid if any
	    worldClock = 0;
	  //Build Patch Grid
        for(int x=0;x<WIDTH;x++)
            for(int y=0;y<HEIGHT;y++){
               patch[x][y] = new Patch(x,y);
               patch[x][y].resource = 0;//((int)(rand.nextDouble()*50));
            }
        setNeighborsBox();
        setBlockSize();
      //Build Agent Queue
        queue = new Queue();
	    Patch agentsPatch;
	    int tag, strat,xLoc,yLoc;
        Agent a;
	    for (int i=0;i<numAgents;i++){
	        if (randomStart)
	            agentsPatch = patch[(int)(rand.nextDouble()*4)][(int)(rand.nextDouble()*4)];
	        else
	            agentsPatch = patch[i %3][0];
	        tag = i;
	        if (mutationRate ==0)
	            strat = i%2;
	        else
	            strat= 1;//two strategies: "cooperate" and "defect"
	        a = new Agent(agentsPatch,newResourceLevel,tag,strat,BLOCK_SIZE);
            add(a);
            a.patch.add(a);
	    }
        repaint();
	}

	void startNewTrial(){
	    if(cooperators == 0)coopExtinctionTime += worldClock;
	    if(cooperators == queue.size()) {
	        cooperatorTrials++;
	        defExtinctionTime += worldClock;
	    }
	    //outToFile();
	    reset();
	    trial++;
	    //textArea1.appendText("Run:" + run + " Trial:" + trial + "\n");
	    label7.setText("Trial: " + trial);
	}

	void startNewRun(){
        if(!(cooperatorTrials == numTrials))
            coopExtinctionTime/=(numTrials-cooperatorTrials);
        if(!(cooperatorTrials == 0))
            defExtinctionTime/=(cooperatorTrials);
        String st = (run + "\t" + HEIGHT*WIDTH + "\t" + movementRate + "\t" +
	                 cooperatorTrials + "\t" + coopExtinctionTime + "\t" +
	                 defExtinctionTime );
	    out1.println(st);
	    textArea1.appendText(st + "\n");
	    trial = 0;
	    defExtinctionTime = 0;
	    coopExtinctionTime = 0;
	    cooperatorTrials = 0;
	    run++;
	    movementRate = params[run];
	    label8.setText("Run: " + run);
	}

	void setBlockSize(){
        BLOCK_SIZE = (int)Math.min( (this.size().height*0.60)/HEIGHT
            ,(this.size().width)/WIDTH *0.8);
        //BLOCK_SIZE = (int)Math.min( (panel2.size().height -40 )/HEIGHT
        //    ,(panel2.size().width - 40 )/WIDTH);
    }


    void setNeighborsTorus(){ //creates implicit grid for agent navigation
       for(int x=0;x<WIDTH;x++)
        for(int y=0;y<HEIGHT;y++){
            patch[x][y].neighbor[0] =
                patch[x]                [(y-1+HEIGHT)%HEIGHT]; //above
            patch[x][y].neighbor[1] =
                patch[(x+1+WIDTH)%WIDTH][(y-1+HEIGHT)%HEIGHT];
            patch[x][y].neighbor[2] =
                patch[(x+1+WIDTH)%WIDTH][y];                   //right
            patch[x][y].neighbor[3] =
                patch[(x+1+WIDTH)%WIDTH][(y+1+HEIGHT)%HEIGHT];
            patch[x][y].neighbor[4] =
                patch[x]                [(y+1+HEIGHT)%HEIGHT]; //below
            patch[x][y].neighbor[5] =
                patch[(x-1+WIDTH)%WIDTH][(y+1+HEIGHT)%HEIGHT];
            patch[x][y].neighbor[6] =
                patch[(x-1+WIDTH)%WIDTH][y];                   //left
            patch[x][y].neighbor[7] =
                patch[(x-1+WIDTH)%WIDTH][(y-1+HEIGHT)%HEIGHT];
        }
    }

    void setNeighborsTube(){
        for(int x=0;x<WIDTH;x++)
        for(int y=0;y<HEIGHT;y++){
            patch[x][y].neighbor[0] =
                patch[x]                [(y-1+HEIGHT)%HEIGHT]; //above
            patch[x][y].neighbor[1] =
                patch[(x+1+WIDTH)%WIDTH][(y-1+HEIGHT)%HEIGHT];
            patch[x][y].neighbor[2] =
                patch[(x+1+WIDTH)%WIDTH][y];                   //right
            patch[x][y].neighbor[3] =
                patch[(x+1+WIDTH)%WIDTH][(y+1+HEIGHT)%HEIGHT];
            patch[x][y].neighbor[4] =
                patch[x]                [(y+1+HEIGHT)%HEIGHT]; //below
            patch[x][y].neighbor[5] =
                patch[(x-1+WIDTH)%WIDTH][(y+1+HEIGHT)%HEIGHT];
            patch[x][y].neighbor[6] =
                patch[(x-1+WIDTH)%WIDTH][y];                   //left
            patch[x][y].neighbor[7] =
                patch[(x-1+WIDTH)%WIDTH][(y-1+HEIGHT)%HEIGHT];
        }
        for(int y=0;y<HEIGHT;y++){  //seal of left of torus
            patch[0][y].neighbor[5] = null;
            patch[0][y].neighbor[6] = null;
            patch[0][y].neighbor[7] = null;
        }
        for(int y=0;y<HEIGHT;y++){  //seal of right of torus
            patch[WIDTH-1][y].neighbor[1] = null;
            patch[WIDTH-1][y].neighbor[2] = null;
            patch[WIDTH-1][y].neighbor[3] = null;
        }
    }

    void setNeighborsBox(){
        for(int x=0;x<WIDTH;x++)
        for(int y=0;y<HEIGHT;y++){
          if(y>0)
            patch[x][y].neighbor[0] = patch[x]  [y-1]; //above
          if(y>0 && x<WIDTH-1)
            patch[x][y].neighbor[1] = patch[x+1][y-1];
          if(x<WIDTH-1)
            patch[x][y].neighbor[2] = patch[x+1][y];   //right
          if(y<HEIGHT-1 && x<WIDTH-1)
            patch[x][y].neighbor[3] = patch[x+1][y+1];
          if(y<HEIGHT-1)
            patch[x][y].neighbor[4] = patch[x]  [y+1]; //below
          if(y<HEIGHT-1 && x>0)
            patch[x][y].neighbor[5] = patch[x-1][y+1];
          if(x>0)
            patch[x][y].neighbor[6] = patch[x-1][y];  //left
          if(y>0 && x>0)
            patch[x][y].neighbor[7] = patch[x-1][y-1];
        }
    }

 //*************************Process Management**********************************
	public synchronized void stop(){
	    if((simulation != null)){
	        simulation.stop();
	        simulation = null;
	    }		    
	}
	public void start(){
	    if (simulation == null){
	        simulation = new Thread(this);
	        simulation.start();
	    }
	}
//*************************Main Process*****************************************
	public void run(){
	    while(run < numRuns +1){
	        if(draw){
	            try{ simulation.sleep(20);}
	            catch(InterruptedException e){}
	        }
	        synchronized(this){
	            cycle();
	        }

	    }
        //outFileMeans();
        label7.setText("Done");
        repaint();
        stop();
     }

	void cycle(){
	    if (worldClock == 0) repaint();
	    worldClock++;
	    tickPatches();
	    tickAgents();
	    if(play) runTournaments();

	    if (draw){
            repaint();
            runningValues();
        }
        else
            if(worldClock%20 ==0)
                runningValues();
        if(cooperators == 0 || cooperators == queue.size()){
            //if(trial == numTrials)
            //    startNewRun();
            //startNewTrial();

        }
	}
//***********************Process Details**********************************
	void tickPatches(){
	    for(int x=0;x<WIDTH;x++)
	      for(int y=0;y<HEIGHT;y++){
	        if(grow)
    	        patch[x][y].grow(.2);
    	    if(queue.size() > 0)
	            if(rand.nextDouble() < extinctionRate* Math.max(x,y)/WIDTH)
                    decimate(patch[x][y]);
            //if(rand.nextDouble() < seedRate && grow)
            //     patch[x][y].resource+=5;
          }
	}
	
	void decimate(Patch p){
	    Agent a;
	    Object[] list = p.queue.getObjectList();
	    for (int i=0;i<list.length;i++)
	      kill((Agent)list[i]);	    
	    if(draw){
	        Graphics g = this.getGraphics();
	        g.translate(30,30);
	        g.setColor(Color.red);
	        g.fillRect(p.X*BLOCK_SIZE+1,p.Y*BLOCK_SIZE+1,BLOCK_SIZE-4,BLOCK_SIZE-4);
	        try{ //just to let the red flash linger...
	            simulation.sleep(50);
	        }
	        catch(InterruptedException e){}
	    }
	}

	void runTournaments(){
	    for (int x=0;x<WIDTH;x++)
	        for(int y=0;y<HEIGHT;y++){
	            if(patch[x][y].queue.size() >0)
                    pdTournament(patch[x][y]);
            }
	}

	void pdTournament(Patch p){
	    double payoffs1,payoffs2;
        double[][] pMx = {{pCC,pCD},{pDC,pDD}}; //payoffs
        Agent player1,player2; //places to hold the handles of current players
        p.queue = scrambleQueue(p.queue);
        Object[] residentList = p.queue.getObjectList();
        int numPairs = (int) residentList.length/2; //how many rounds?
        for (int i=0;i<numPairs;i++){
            player1 = (Agent)residentList[i*2];//get players from shuffled list
            player2 = (Agent)residentList[i*2+1];
            payoffs1 = pMx[player1.strat %2][player2.strat%2];
            player1.resource += payoffs1;
            payoffs2 =  pMx[player2.strat %2][player1.strat%2];
            player2.resource += payoffs2;
        }
    }

	void tickAgents(){  //tick, reproduce, and kill
	    cooperators =0;
	    int strat=0;
        queue = scrambleQueue(queue);
        Agent a;
        try {
          synchronized (queue) {
            for (int i = queue.size(); i > 0; i--){
                a = (Agent)queue.requeue();
                if(move)
                    if(rand.nextDouble() < movementRate)
                      moveRandom(a);
                if(feed){
                   a.resource += a.patch.feed();
                   a.resource --;
                }
                if(a.resource > reproLevel){//reproduce
                   strat = a.strat;
                   if(rand.nextDouble() < mutationRate)
                        strat = 1- strat;
                   Agent aa = new Agent(a.patch,(int)(a.resource/3),a.tag, strat,BLOCK_SIZE);
                   add(aa);
                   aa.patch.add(aa);
                   a.resource = (int)(a.resource/3);
                }
                if (a.strat == 0)
                    cooperators++;
                if (a.resource < 0) //death
                    kill(a);
            }
          }
        }
        catch (QueueEmpty e) { }
    }

    void moveRandom(Agent a){
        int random7 = (int)(rand.nextDouble()*8);
        if(a.patch.neighbor[random7] != null)
            a.moveTo(a.patch.neighbor[random7]);
    }
    
	void kill(Agent a){
	    sub(a);
	    a.patch.sub(a);
	    a = null;
	}
	
    void add(Agent a){
        queue.enqueue(a);
    }
    
    void sub(Agent a){
        try{
            queue.remove(a);
        }
        catch(QueueItemAbsent e){}
    }
    
//************************Display Routines***********************************
	public void paint(Graphics g){
	    repaint(); // repaint() calls update()
	}

	public void update(Graphics g){
        g.translate(30,30);
        int redLevel;
        Color[] extColor = {Color.white,
                            Color.lightGray,
                            Color.gray,
                            Color.orange,
                            Color.magenta,
                            Color.red,
                            Color.red};
       //draw Patches
        for (int x=0;x<WIDTH;x++)
         for (int y=0;y<HEIGHT;y++){
           g.setColor(new Color(0,
               Math.min(255,50+(int)patch[x][y].resource*2),0));
           g.fillRect(x*BLOCK_SIZE,y*BLOCK_SIZE, BLOCK_SIZE-2,BLOCK_SIZE-2);
           redLevel = 255-(int)(255*Math.max(x,y)/WIDTH);//Math.min(255,50+(int)patch[x][y].resource*4);
           g.setColor(new Color((int)(170+redLevel/3),redLevel,redLevel));
           //g.setColor(extColor[(int)(200*extinctionRate*Math.max(x,y)/WIDTH)]);
           g.drawRect(x*BLOCK_SIZE,y*BLOCK_SIZE, BLOCK_SIZE-2,BLOCK_SIZE-2);
         }
        //draw Agents
         Object [] agentList = queue.getObjectList();
         Agent a;
         int xLoc,yLoc;
         Color[] agentColor =
                {Color.white,Color.black};
         for(int i=0;i<agentList.length;i++){
            a = (Agent)agentList[i];
            redLevel = Math.min(255,(int)(a.resource)*2);
            if (redLevel<0) redLevel = 0;
            g.setColor(new Color(redLevel,0,255-redLevel));
            xLoc = a.patch.X*
                        BLOCK_SIZE + (int)(Math.random()*(BLOCK_SIZE-9));
            yLoc = a.patch.Y
                        *BLOCK_SIZE + (int)(Math.random()*(BLOCK_SIZE-9));
            /*if (a.strat %2 == 0)
                g.fillRect(xLoc,yLoc,7,7);
            else
                g.fillOval(xLoc,yLoc,5,5);*/
            g.fillRect(xLoc,yLoc,5,5);
            g.setColor(agentColor[a.strat]);
            g.fillRect(xLoc,yLoc+2,5,1);
         }
	}
	
	void outputHeader(){
	    Date date = new Date();
        textArea1.appendText("SIMULATION RECORD FOR "+
             "EAME AGENT BASED SIMULATOR"+"\n"+
             "       < "+date.toString() + " >\n"+
             "--------------------------------------------");
        textArea1.appendText("\n");
     }

    void outputVals(){ //outputs settings each time simulator is reset()
        Date ddate = new Date();
        textArea1.appendText("<"+ddate.getHours()+":"+
                ddate.getMinutes()+":"+
                ddate.getSeconds()+">  "+ "\n");
        textArea1.appendText("    Height:"+HEIGHT+", ");
        textArea1.appendText("Width:"+WIDTH+", ");
        textArea1.appendText("pCC:"+pCC+", ");
        textArea1.appendText("pCD:"+pCD+", ");
        textArea1.appendText("pDC:"+pDC+", ");
        textArea1.appendText("pDD:"+pDD+", ");
        textArea1.appendText("RandSeed:"+randSeed+", ");
        textArea1.appendText("\n" + "     ");
        textArea1.appendText("InitialAgents:"+numAgents+", ");
        textArea1.appendText("MovementRate:" + movementRate+", ");
        textArea1.appendText("ExtinctionRate:" + extinctionRate+", ");
        textArea1.appendText("MutationRate:" + mutationRate+", ");
        textArea1.appendText("SeedRate:"+ seedRate + ", ");
        textArea1.appendText("\n"+"     ");
        textArea1.appendText("feed:" + feed+", ");
        textArea1.appendText("move:" + move+", ");
        textArea1.appendText("grow:" + grow+", ");
        textArea1.appendText("play:" + play+", ");
        textArea1.appendText("\n");
     }
     void runningValues(){ //running values output at each cycle
        label2.setText(String.valueOf(worldClock));
	    label4.setText(String.valueOf(queue.size()));
	    label6.setText(String.valueOf(cooperators));
     }
//********************************OutputFile**********************************
void makeFile(){
    Date date = new Date();
    String s = ("REC" +(date.getMonth()+1)+//namethe output file
                date.getDate() +
                date.getHours()+
                date.getMinutes());
    try{
        out1 = new PrintWriter(
                new BufferedWriter(
                  new FileWriter(s+ ".txt")));
        out1.println("EAME Simulator Record for: " + date.toString());
        out1.println("   Second Generation Agent/Patch Prototype");
        out1.println("Parameters:");
        out1.println("   Height:"+ HEIGHT + " Width:" + WIDTH + " Agents:" + numAgents +
                     " Payoffs:{{" + pCC + "," + pCD + "},{" + pDC + "," + pDD +
                     "}} MoveRate:" + movementRate + " Trials:" + numTrials);
        out1.println("______________________________________________ ");
        out1.println();
        out1.println("Run" + "\t" + "Size" + "\t" + "MvRt" + "\t" + "CpTrls" + "\t" +
                     "CxT" + "\t" + "DxT");
        out1.println();
    }
    catch(IOException e){;}
}

void outToFile(){ //output to file
    String st = (trial +  "\t" +worldClock + "\t" +
                queue.size() + "\t" + cooperators );
    out1.println(st);
    textArea1.appendText(st + "\n");
}

void outFileMeans(){
    out1.println();
    out1.println("Cooperators to Fixation: " + cooperatorTrials +"/" + numTrials);
    if(!(cooperatorTrials == numTrials))
        out1.println("Mean Time to Coopeator Extinction: " + "\t" +
                    coopExtinctionTime/(numTrials-cooperatorTrials));
    if(!(cooperatorTrials == 0))
        out1.println("Mean Time to Defector Extinction: " + "\t" +
                    defExtinctionTime/(cooperatorTrials));
}
 
//********************************Queue Handling Routines********************
Queue scrambleQueue(Queue q){//randomizes queue order
        Object[] objectList = q.getObjectList();
        Object a;
        int i,j;
        int listLength = objectList.length;
        for(int x=0;x<listLength;x++){
            i = (int)(rand.nextDouble()*listLength);
            j = (int)(rand.nextDouble()*listLength);
            a = objectList[i];
            objectList[i] = objectList[j];
            objectList[j] = a;
        }
        Queue qq = new Queue();
        for (int x=0;x<q.size();x++)
            qq.enqueue(objectList[x]);
        return qq;
    }   


//********************************Input Codes*********************************
    //code parser
    void readInputValues(String s){
        if (s.indexOf("Width:") != -1){WIDTH = getIntValue(s,"Width:");}
        if (s.indexOf("Height:") != -1){HEIGHT = getIntValue(s,"Height:");}
        if (s.indexOf("pCC:") != -1){pCC = getIntValue(s,"pCC:");}
        if (s.indexOf("pCD:") != -1){pCD = getIntValue(s,"pCD:");}
        if (s.indexOf("pDC:") != -1){pDC = getIntValue(s,"pDC:");}
        if (s.indexOf("pDD:") != -1){pDD = getIntValue(s,"pDD:");}
        if(s.indexOf("RandSeed:") != -1){randSeed = getLongValue(s,"RandSeed:");};

        if (s.indexOf("InitialAgents:") != -1){numAgents = getIntValue(s,"InitialAgents:");}
        if (s.indexOf("feed:") != -1){feed = getBooleanValue(s,"feed:");}
        if (s.indexOf("seed:") != -1){seed = getBooleanValue(s,"seed:");}
        if (s.indexOf("grow:") != -1){grow = getBooleanValue(s,"grow:");}
        if (s.indexOf("play:") != -1){play = getBooleanValue(s,"play:");}
        if (s.indexOf("move:") != -1){move = getBooleanValue(s,"move:");}
        if (s.indexOf("MovementRate:") != -1){movementRate = getDoubleValue(s,"MovementRate:");}
        if (s.indexOf("SeedRate:") != -1){seedRate = getDoubleValue(s,"SeedRate:");}
        if (s.indexOf("ExtinctionRate:") != -1){extinctionRate = getDoubleValue(s,"ExtinctionRate:");}
        if (s.indexOf("MutationRate:") != -1){mutationRate = getDoubleValue(s,"MutationRate:");}

    }
    //the next routines extract values from strings
    int getIntValue(String s,String name){
        int place, nextSpace;
        place = s.indexOf(name);
        if (place != -1){ //if the keystring is found
            nextSpace = s.indexOf(",",place);
            s = s.substring(place+name.length(),nextSpace).trim();
            return Integer.valueOf(s).intValue();
        }
        else return 0;
    }
    
    long getLongValue(String s,String name){
        int place, nextSpace;
        place = s.indexOf(name);
        if (place != -1){ //if the keystring is found
            nextSpace = s.indexOf(",",place);
            s = s.substring(place+name.length(),nextSpace).trim();
            return Long.valueOf(s).longValue();
        }
        else return 0;
    }
    boolean getBooleanValue(String s,String name){
        int place, nextSpace;
        place = s.indexOf(name);
        if (place != -1){ //if the keystring is found
            nextSpace = s.indexOf(",",place);
            s = s.substring(place+name.length(),nextSpace).trim();
            return Boolean.valueOf(s).booleanValue();
        }
        else return true;
    }
    double getDoubleValue(String s,String name){
        int place, nextSpace;
        place = s.indexOf(name);
        if (place != -1){ //if the keystring is found
            nextSpace = s.indexOf(",",place);
            s = s.substring(place+name.length(),nextSpace).trim();
            return Double.valueOf(s).doubleValue();
        }
        else return 0.01;
    }
    
//********************************Controls*************************************
	//{{DECLARE_CONTROLS
	java.awt.TextField textField1;
	java.awt.Panel panel2;
	java.awt.TextArea textArea1;
	java.awt.Panel panel1;
	java.awt.Button button1;
	java.awt.Button button2;
	java.awt.Button button3;
	java.awt.Button button4;
	java.awt.Label label1;
	java.awt.Label label2;
	java.awt.Label label3;
	java.awt.Label label4;
	java.awt.Label label5;
	java.awt.Label label6;
	java.awt.Label label7;
	java.awt.Label label8;
	java.awt.Button button5;
	//}}

	class SymAction implements java.awt.event.ActionListener
	{
		public void actionPerformed(java.awt.event.ActionEvent event)
		{
			Object object = event.getSource();
			if (object == button2)
				button2_ActionPerformed(event);
			else if (object == button3)
				button3_ActionPerformed(event);
			else if (object == button4)
				button4_ActionPerformed(event);
			else if (object == button1)
				button1_ActionPerformed(event);
			else if (object == button5)
				button5_ActionPerformed(event);
		}
	}

	void button2_ActionPerformed(java.awt.event.ActionEvent event)
	{
		stop();		
	}

	void button3_ActionPerformed(java.awt.event.ActionEvent event)
	{
		start();
	}

	void button4_ActionPerformed(java.awt.event.ActionEvent event)
	{
	    if(simulation == null)
    		cycle();
	}

	void button1_ActionPerformed(java.awt.event.ActionEvent event)
	{
	    reset();
	    outputVals();
		repaint();
	}

	class SymMouse extends java.awt.event.MouseAdapter
	{
		public void mousePressed(java.awt.event.MouseEvent event)
		{
			Object object = event.getSource();
			if (object == AgentPatchSim.this)
				AgentPatchSim_MousePressed(event);
		}
	}

	void AgentPatchSim_MousePressed(java.awt.event.MouseEvent event)
	{
        int mouseX = event.getX()-30;
	    int mouseY = event.getY()-30;
	    int patchX;
	    int patchY;
	    if(mouseY > 0 && mouseY<HEIGHT*BLOCK_SIZE)
	      if(mouseX > 0 && mouseX <WIDTH*BLOCK_SIZE){
	        patchX = (int)mouseX/BLOCK_SIZE;
	        patchY = (int)mouseY/BLOCK_SIZE;
	        outputPatchInfo(patchX,patchY);
	    }
	}
	void outputPatchInfo(int X,int Y){//outputs info when you click on a patch
	    String s;
	    int coop =0;
	    textArea1.appendText("Patch["+X+"]["+Y+"]");
	    textArea1.appendText("   Generation: " + worldClock);
	    textArea1.appendText("  Resource Level: "+patch[X][Y].resource);
	    textArea1.appendText("  Census: "+ patch[X][Y].queue.size());
	    Object[] list = patch[X][Y].queue.getObjectList();
	    for(int i=0;i<list.length;i++)
	      if( ((Agent)list[i]).strat == 0)
	        coop++;
	    textArea1.appendText("  Cooperators: "+coop);
	    textArea1.appendText("\n");
	}

	void button5_ActionPerformed(java.awt.event.ActionEvent event)
	{

		if (draw){
		    button5.setLabel("Draw");
		    draw = false;
		}
		else{
		    button5.setLabel("Don't Draw");
		    draw = true;
		}

	}
}


//************************AGENT CLASS************************************
final class Agent
{
    Patch patch; //location is a patch
    double resource = 100;
    final int tag;
    final int strat;
    Agent(Patch p,int r,int ident, int Strat,int BLOCK_SIZE){
        patch = p;
        resource = r;
        tag= ident;
        strat = Strat;           
    }

    void moveTo(Patch p){
        patch.sub(this);
        patch = p;
        patch.add(this);
    }  
}
//***********************PATCH CLASS*****************************************
final class Patch
{
    Queue queue = new Queue();
    static int MAX_RESOURCE = 50;
    final int X;
    final int Y;
    int resource = 0;
    int cooperators;
    Patch[] neighbor = new Patch[8];
    
    Patch(int xLoc, int yLoc){ //constructor
        X = xLoc;
        Y = yLoc;
    }
    
    void add(Agent a){
        queue.enqueue(a);
    }

    void sub(Agent a){
        try{
            queue.remove(a);
        }
        catch(QueueItemAbsent e){}
    }

    void grow(double r){        //grows resource at rate r
        //resource = (int)(resource + resource*r);
        //if (resource > MAX_RESOURCE)resource=MAX_RESOURCE;
        resource+=5;
        if (resource > 50) resource = 50;
    }

    int feed(){         //returns food value if any
        int food;
        if (resource > 5)
          food = 5;
        else food = resource;
        resource -= food;        
        return food;
    }     
}


//****************Main Frame "wrapper" Class for Application**************
        //If you borrow this wrapper class, be sure to disable parameter calls from the 
        //applet to the HTML page. 
class AgentPatchApp extends Frame
{
    static AgentPatchSim sim;
    public AgentPatchApp( String title )
    {
        super( title );
        enableEvents( AWTEvent.WINDOW_EVENT_MASK );
    }

    public void processWindowEvent( WindowEvent e )
    {
        if( e.getID() == WindowEvent.WINDOW_CLOSING )
        {
            setVisible( false );
            dispose();
            sim.out1.close();
            System.exit( 0 );
        }
        super.processWindowEvent( e );
    }

    public static void main( String args[] )
    {
        sim = new AgentPatchSim();
        AgentPatchApp window = new AgentPatchApp( "EAME Agent-Patch Simulator" );
        window.setSize( 600, 530 );
        window.setLocation( 100, 100 );
        window.add(sim);
        window.addNotify();
        sim.writeFile = true; //lets the applet know that it's run as application
        sim.init();
        window.show();
        sim.start();
        sim.makeFile(); //tells the applet to open and initialize a record file. 
    }
}

