import java.applet.*;
import java.awt.*;

public class Life extends Applet implements Runnable {
	Canvas C;
	Dimension CD;
	Label L;
	Person P, Plast;
	Food F, Flast;
	Graphics GC;
	int Initial_population = 100;
	int Initial_food = 5;
	int Current_population;
	int Current_females;
	int Current_food;
	int Current_fighting;
	int Current_mating;
	int Current_moving;
	int Current_eating;
	int Current_static;
	int Epoch=0;
	float see_distance=0.5f;  // horizon distance for a person
	float eat_distance=0.03f;  // distance below which food is within reach
	float mate_distance=0.03f; // distance below which mate is accessible
	int age_mate=200;         // age above which mating is possible
	float prob_mate=0.8f;     // probability of conception
	float prob_food=0.01f;    // probability food appears at a given epoch
	Thread T;


   public void init() {
	   int Rows = 1;
	   int Columns = 1;
	   setLayout(new GridLayout(Rows,Columns));

	   C = new Canvas();
	   add(C);
  }

   public void start() {

	   // generate a population

	   Plast = null;
	   for(int i=0;i<Initial_population;i++) {
		   int A = (int) (500.*Math.random());
		   float X = (float) Math.random();
		   float Y = (float) Math.random();
		   int W = (int) (100.*Math.random());
		   int S = (int) (100.*Math.random());
		   int Sex = 1;
		   if (Math.random() > 0.5) Sex = 0;
		   P = new Person(Plast,A,W,S,Sex,X,Y);
		   if(Plast!=null) Plast.setnext(P);
		   Plast = P;
	   }
       Current_population = Initial_population;	  

	   // generate some food

	   Flast = null;
	   for(int i=0;i<Initial_food;i++) {
		   int S = (int) (100.*Math.random());
		   float X = (float) Math.random();
		   float Y = (float) Math.random();
		   F = new Food(Flast,S,X,Y);
		   if(Flast!=null) Flast.setnext(F);
		   Flast = F;
	   }
      this.show();

	  T = new Thread(this);
	  T.setPriority(Thread.MIN_PRIORITY);
	  T.start();

   }

   public void run() {
	   Food Fclosest,FPrevious;
	   Person Pclosest,PPrevious;
       while(Current_population>0) {
		   Epoch++;
		   F = Flast;
		   Current_food = 0;
		   while(F!=null) {
			   if(F.size<=1) {
				   if(F.previous!=null) F.previous.setnext(F.next);
				   if(F.next!=null) F.next.setprevious(F.previous);
				   if(F==Flast) Flast = F.previous;
				   FPrevious = F.previous;
				   F = null;
			   } else {
				   F.grows();
				   Current_food++;
				   FPrevious = F.previous;
			   }
			   F = FPrevious;
		   }

		   // Create new food from time to time

		   if(Math.random()<prob_food) {
				int S = (int) (100.*Math.random());
				float X = (float) Math.random();
				float Y = (float) Math.random();
				F = new Food(Flast,S,X,Y);
				if(Flast!=null) Flast.setnext(F);
				Flast = F;
				Current_food++;
		   }


		   Current_population = 0;
		   Current_females = 0;
		   Current_eating = 0;
		   Current_mating = 0;
		   Current_moving = 0;
		   Current_fighting = 0;
		   Current_static = 0;

		   P = Plast;
		   while(P!=null) {
			   if(P.dies()) {
				   if(P.previous!=null) P.previous.setnext(P.next);
				   if(P.next!=null) P.next.setprevious(P.previous);
				   if(P==Plast) Plast = P.previous;
				   PPrevious = P.previous;
				   P = null;
			   } else {
				   boolean activity = false;
				   boolean anger = false;
				   Current_population++;
				   if(P.sex>0) Current_females++;
				   // This person ages by one epoch
				   P.ages(1);
				   // where is the closest food ?
				   Fclosest = Food_near(P.Xpos,P.Ypos);
				   if(Fclosest!=null) {
					   float d = distance(Fclosest.Xpos,Fclosest.Ypos,P.Xpos,P.Ypos);
					   if(d < eat_distance) {
						  // within eating distance: person eats a portion
						 int portion = 1 + Fclosest.size/5;
					     P.eats(portion);
						 Fclosest.consumed(portion);
						 Current_eating++;
						 if(portion<=1) {
							anger = true;
						 }
						 activity = true;
					  } else {
						   if(d < see_distance) {
						       // person moves towards nearest food, and sleeps
					           P.moves(Fclosest.Xpos,Fclosest.Ypos);
							   Current_moving++;
						       activity = true;
						   } 
					  }
				   }
				   // where is the nearest person ?
				   Pclosest = Person_near(P,P.Xpos,P.Ypos);
				   if(Pclosest!=null) {
					   float d = distance(Pclosest.Xpos,Pclosest.Ypos,P.Xpos,P.Ypos);
					   if(d < mate_distance) {
						   // if there was insufficient food, fight closest person
						   if(anger && P.age>age_mate) {
							   int S=P.strength;
							   P.fights(Pclosest.strength);
							   Pclosest.fights(S);
							   Current_fighting++;
							   activity = true;
						   } 
						   // mating produces offspring, with probability prob_mate
						   if(!anger 
							   && Pclosest.sex != P.sex 
							   && Pclosest.age>age_mate 
							   && P.age>age_mate
							   && Math.random() < prob_mate) {
							   // sex of child is random
							   int Sex = 1;
							   if (Math.random() > 0.5) Sex = 0;
							   // age is zero, weight is 1, strength is random
							   // child is at same location as parent
							   int Strength = (int) (10.*Math.random());
							   Person Pnew = new Person(Plast,0,1,Strength,Sex,P.Xpos,P.Ypos);
							   Plast.setnext(Pnew);
							   Plast = Pnew;
							   Current_population++;
							   Current_mating++;
							   activity = true;
						   }
					   } else {
						   // if other person closer than horizon, move closer, unless person
						   // has already been active this epoch
						   if(!activity && d < see_distance) {
							   P.moves(Pclosest.Xpos,Pclosest.Ypos);
							   Current_moving++;
							   activity = true;
						   }
					   }
				   }
				   // if there has been activity, sleep and gain strength
				   if(activity) {
					   P.sleeps();
					   P.strengthens();
				   } else {
					   Current_static++;
				   }

	               PPrevious = P.previous;
			   }
			   P = PPrevious;
		   }
//		   System.out.print("Food="+Current_food+" Pop="+Current_population+" Fem="+Current_females);
//		   System.out.print(" Mate="+Current_mating+" Fight="+Current_fighting+" Eat="+Current_eating);
//		   System.out.println(" Move="+Current_moving+" Static="+Current_static);
		   update(getGraphics());
	   }
   }

   float distance(float X, float Y, float X1, float Y1) {
	   return (float) Math.sqrt( (X-X1)*(X-X1) + (Y-Y1)*(Y-Y1) );
   }

   Person Person_near(Person Pthis, float X, float Y) {
	   float d,dmin=9999.f;
	   Person Ptest,Pnear=null;
	   Ptest = Plast;
	   while(Ptest!=null) {
		   if(Ptest!=Pthis) {
		      d = distance(Ptest.Xpos,Ptest.Ypos,X,Y);
		      if(d<dmin) {
			      Pnear = Ptest;
			      dmin = d;
			  }
		   }
		   Ptest = Ptest.previous;
	   }
	   return Pnear;
   }

   Food Food_near(float X, float Y) {
	   float d,dmin=9999.f;
	   Food Ftest,Fnear=null;
	   Ftest = Flast;
	   while(Ftest!=null) {
		   d = distance(Ftest.Xpos,Ftest.Ypos,X,Y);
		   if(d<dmin) {
			   Fnear = Ftest;
			   dmin = d;
		   }
		   Ftest = Ftest.previous;
	   }
	   return Fnear;
   }

   public void update(Graphics G) {
	   CD = C.size();
	   int width = CD.width;
	   int height = CD.height;
       GC = C.getGraphics();
	   GC.setColor(getBackground());
	   GC.fillRect(0,0,width,height);
	   P = Plast;
	   while(P!=null) {
		   int SC = P.strengthcolour();
		   GC.setColor(new Color(SC,SC,SC));
		   int xpos = (int) (width*P.Xpos);
		   int ypos = (int) (height*P.Ypos);
		   int wid = P.agewidth();
		   if(P.sex == 1) {
			   GC.fillOval(xpos,ypos,wid,wid);
		   } else {
			   GC.fillRect(xpos,ypos,wid,wid);
		   }
		   P = P.previous;
	   }
	   GC.setColor(new Color(0,255,0));
	   F = Flast;
	   while(F!=null) {
		   int xpos = (int) (width*F.Xpos);
		   int ypos = (int) (height*F.Ypos);
		   int wid = F.sizewidth();
		   GC.fillOval(xpos,ypos,wid,wid);
		   F = F.previous;
	   }

   }



   public String getAppletInfo () {
      return "Life by J.J.B.";
   }


}

class Food {
	int max_size=100;
	int size;
	float Xpos;
	float Ypos;
	Food next;
	Food previous;

	Food(Food last, int Size, float X, float Y) {
		Xpos = X;
		Ypos = Y;
		size = Size;
		next = null;
		previous = last;
	}

	void setnext(Food F) {
		next = F;
	}
	void setprevious(Food F) {
		previous = F;
	}

	int sizewidth() {
		if(size/5>1) {
			return 10;//size/5;
		} else {
		    return 10;
		}
	}

    void consumed(int amount) {
	    size -= amount;
		if(size<=0) size = 0;
    }

	void grows() {
		size++;
		if(size>max_size) size = max_size;
	}
}




class Person {

	int max_age=500;
	int max_weight=100;
	int max_strength=100;
	int age;
	int weight;
	int strength;
	int sex;
	float Xpos;
	float Ypos;
	Person next;
	Person previous;

	Person (Person last, int Age, int Weight, int Strength, int Sex, float X, float Y) {
		age = Age;
		weight = Weight;
		strength = Strength;
		sex = Sex;
		Xpos = X;
		Ypos = Y;
		next = null;
		previous = last;
	}

	void setnext(Person P) {
		next = P;
	}
	void setprevious(Person P) {
		previous = P;
	}

	int agewidth() {
		return (10*age)/max_age;
	}
	int strengthcolour() {
		return (255*strength)/max_strength;
	}
	int weightwidth() {
		if(weight/10>1) {
		   return 10; // weight/10;
		} else {
			return 10; //1;
		}
	}

	void eats (int amount) {
		weight += amount;
		if(weight>max_weight) weight = max_weight;
		strength++;
		if(strength>=max_strength) strength = max_strength;
	}

	void sleeps () {
		weight--;
		if(weight<0) weight = 0;
	}

	void fights (int OtherStrength) {
		strength-=OtherStrength;
		if(strength<=0) strength = 0;
	}

	void strengthens () {
		strength++;
		if(strength>max_strength) strength = max_strength;
	}

	void moves (float X, float Y) {
		Xpos = Xpos + 0.05f*(X-Xpos)*(float)strength/(float)max_strength;
		Ypos = Ypos + 0.05f*(Y-Ypos)*(float)strength/(float)max_strength;
		weight--;
		if(weight<=0) weight = 0;
	}

	void ages (int days) {
		age += days;
		if(age>max_age) age = max_age;
	}

	boolean dies() {
		if(age>=max_age) {
			return true;
		} 
		if(weight<=0) {
			strength--;
		}
		if(strength<=0) {
			return true;
		}
		if(weight>=max_weight && strength < max_strength/2) {
			return true;
		}
		return false;
	}
		
}







           
