
//  BugWorld.java          version  10:00pm Tuesday January 23, 2007
//
//  (with reproduction and with full reporting to bug (i.e., calls
//   to "eat_this" and "lose_this").
//
//  by J.Horn,   for CS 326 Winter 2007   for HOMEWORK 1


import java.awt.*;
import java.util.*;
import java.awt.event.*;

//  (1,8,15) for ABC below, gives NL (Nate Lampi) domination
//  (1,8,5)  for ABC below, gives Fluffination (Scott Raiford)
//


public class BugWorld extends Frame implements WindowListener, MouseListener
       {
         public static final int NUM_ROWS = 16;
         public static final int NUM_COLS = 24;
         public static final int CELL_WIDTH = 40;
         public static final int CELL_HEIGHT = 40;
         public static final int INIT_HEALTH = 4;
         public static final int CELL_FOOD_CAPACITY = 5;
         public static final int INITIAL_NUM_COPIES= 5;

         public static final int COST_OF_STAYING_STILL = 1;   // A
         public static final int COST_OF_MOVING = 2;          // B
         public static final int COST_OF_REPRODUCTION = 10;   // C
         private static int current_cost_of_reproduction = COST_OF_REPRODUCTION;
         public static final double PROBABILITY_OF_REPRODUCTION = 0.2;

         private static int num_bugs;
         private static int t=0;        // Counter for number of time steps.
         private static Graphics g;
         private static BugWorld this_world;

         private static LinkedList allBugs = new LinkedList();
         public static LinkedList cells[][] =
                        new LinkedList[NUM_ROWS][NUM_COLS];



         public static void main(String [] arguments) throws Exception
			  {

			    this_world = new BugWorld();
			    boolean end = false;
			    while(!end)
			       {
			         Thread.sleep(30);   //  Pause between screen updates
			         t++;                //  Time moves ahead
			         feed_Bugs();
			         reproduce_Bugs();
			         squash_Bugs();       //
			         remove_dead_bugs();
			         this_world.paint(g);    // Re-paint everything.
			         move_Bugs();
			   }

			  }


         public BugWorld()
             {
                super("  Splat! World  ");  //  Create and name window
                setSize(NUM_COLS*CELL_WIDTH+65,NUM_ROWS*CELL_HEIGHT+60);
                                         //  Set initial size
                setBackground(Color.yellow.brighter()); // Initial back. color
                setVisible(true);                //  Display it!

                addWindowListener(this); //  Tell Java runner that this frame
                addMouseListener(this);  //   obj will handle Window events
                                    //   and mouse events, so give them
                                    //   to us!

                for(int i = 0; i < INITIAL_NUM_COPIES; i++)
                   {
					 allBugs.add(new BugRecord(new JhornBug()));

				//	 allBugs.add(new BugRecord(new AanagnosBug()));
	/*				 allBugs.add(new BugRecord(new AgiovaniBug()));
                     allBugs.add(new BugRecord(new BdepewBug()));
                     allBugs.add(new BugRecord(new BkrentBug()));
                     allBugs.add(new BugRecord(new EspreheBug()));
                     allBugs.add(new BugRecord(new KwieringBug()));
                     allBugs.add(new BugRecord(new NlampiBug()));
//                     allBugs.add(new BugRecord(new RBeggsBug()));
                     allBugs.add(new BugRecord(new RobjohnsBug()));
//                    allBugs.add(new BugRecord(new RowczarzBug()));
                     allBugs.add(new BugRecord(new TfloraBug()));


                     allBugs.add(new BugRecord(new BehansonBug2()));
                     allBugs.add(new BugRecord(new BgreburBug()));
//                     allBugs.add(new BugRecord(new BsiebertBug()));
                     allBugs.add(new BugRecord(new CperryBug()));
					 allBugs.add(new BugRecord(new DaquigleBug()));
                     allBugs.add(new BugRecord(new DbaylesBug()));
//                     allBugs.add(new BugRecord(new ElieskeBug()));
                     allBugs.add(new BugRecord(new Kbosmabug()));
                     allBugs.add(new BugRecord(new MiericksBug()));
                     allBugs.add(new BugRecord(new RjamiesoBug()));
                     allBugs.add(new BugRecord(new AleagleBug()));
                     allBugs.add(new BugRecord(new DstamourBug()));
                     allBugs.add(new BugRecord(new AmyourenBug()));

 */                    //  Round2 additions

 //                    allBugs.add(new BugRecord(new ElieskeBug()));
               //      allBugs.add(new BugRecord(new BkrentBug2()));
 //                    allBugs.add(new BugRecord(new SraifordBug2()));
  //                   allBugs.add(new BugRecord(new CperryBug2()));
                 //    allBugs.add(new BugRecord(new EspreheBug2()));
			//		 allBugs.add(new BugRecord(new SraifordBug()));
                 //    allBugs.add(new BugRecord(new RandomBug()));

allBugs.add(new BugRecord(new JhornBug()));

				   }


		        num_bugs = allBugs.size();

		        for(int i=0; i<NUM_ROWS;i++)
		            for(int j=0; j<NUM_COLS;j++)
		                cells[i][j] = new LinkedList();

		        for(int i=0;i<num_bugs;i++)
		            {
				      BugRecord current_bug_record = (BugRecord) allBugs.get(i);
		              current_bug_record.row = (int) (Math.random() * NUM_ROWS);
		              current_bug_record.column = (int) (Math.random() * NUM_COLS);
		              cells[current_bug_record.row]
		                   [current_bug_record.column].add(current_bug_record);
		   			}

                t= 0;                      //  Start timer at 0.
                g = getGraphics();

		  	  }


 		 private static int [] neighborhood(BugRecord a_bug_record)
	 	       {
				 int row = a_bug_record.row;
				 int column = a_bug_record.column;

				 int north = row-1;
				 int south = row+1;
				 int east = column-1;
				 int west = column+1;

				 north = (NUM_ROWS+north)%NUM_ROWS;  // implement wrap-around
				 south = (NUM_ROWS+south)%NUM_ROWS;
				 east = (NUM_COLS+east)%NUM_COLS;
				 west = (NUM_COLS+west)%NUM_COLS;

				 int Moore[] = new int[10];

				 Moore[0]=cells[row][column].indexOf(a_bug_record);
				 Moore[1]=cells[north][west].size();
				 Moore[2]=cells[north][column].size();;
				 Moore[3]=cells[north][east].size();
				 Moore[4]=cells[row][west].size();
				 Moore[5]=cells[row][column].size();
				 Moore[6]=cells[row][east].size();
				 Moore[7]=cells[south][west].size();
				 Moore[8]=cells[south][column].size();
				 Moore[9]=cells[south][east].size();

				 return Moore;
			   }

         public static void move_Bugs()
		      {
				num_bugs = allBugs.size();
				BugRecord current_bug_record;
	            for(int i=0; i<num_bugs; i++)
	                {
				      current_bug_record = (BugRecord) allBugs.get(i);
					  current_bug_record.move(neighborhood(current_bug_record));
				    }
		      }

		 public static void feed_Bugs()
		      {
			   num_bugs = allBugs.size();
			   BugRecord current_bug_r;
			   int pos_in_stack;
               for(int i=0; i<num_bugs; i++)
                   {
					current_bug_r = (BugRecord) allBugs.get(i);
					pos_in_stack =
					     cells[current_bug_r.row][current_bug_r.column].indexOf(current_bug_r);
					if(pos_in_stack   < CELL_FOOD_CAPACITY)
					     {
							 current_bug_r.health += CELL_FOOD_CAPACITY - pos_in_stack;
							 current_bug_r.eat_this(CELL_FOOD_CAPACITY - pos_in_stack);
						 }
				    }
		       }

		 public static void reproduce_Bugs()
		      {
			   num_bugs = allBugs.size();
			   BugRecord current_bug_r;
			   int pos_in_stack;
               for(int i=0; i<num_bugs; i++)
                   {
					current_bug_r = (BugRecord) allBugs.get(i);
					if(current_bug_r.want_to_reproduce(current_cost_of_reproduction)  &&
					   Math.random() < PROBABILITY_OF_REPRODUCTION)
					  {
						  //  Create offspring bug record (with offspring bug inside!)
						      BugRecord offspring_bug_r = current_bug_r.reproduce(current_cost_of_reproduction);

						  //  Randomly place offspring in Moore neighborhood of parent.
						      offspring_bug_r.row = current_bug_r.row + (int) (Math.random()*3)-1;
						      offspring_bug_r.column = current_bug_r.column + (int) (Math.random()*3)-1;
						  //  Implement wrap-around effect
						      offspring_bug_r.row = (offspring_bug_r.row+BugWorld.NUM_ROWS) % BugWorld.NUM_ROWS;
						      offspring_bug_r.column = (offspring_bug_r.column+BugWorld.NUM_COLS) % BugWorld.NUM_COLS;

						  //  Add offspring to the bug world
		                      allBugs.add(offspring_bug_r);
		                      num_bugs++;
                              cells[offspring_bug_r.row][offspring_bug_r.column].addLast(offspring_bug_r);
		   			}
				    }
		       }


         public static void squash_Bugs()
               {
				 BugRecord current_bug_record;
				 int row; int column;
				 int num_above;  // number of bugs on your back.
				 for(int i=0; i<allBugs.size(); i++)
				    {
					  current_bug_record = (BugRecord) allBugs.get(i);
					  row = current_bug_record.row;
					  column = current_bug_record.column;
					  num_above = cells[row][column].size() - 1
					               - cells[row][column].indexOf(current_bug_record);
					  if(Math.random()*5 < num_above)
					    {
						  current_bug_record.health = -100;
						  System.out.println("The bug "+
						                      current_bug_record.theBug.getName()+
						                      " gets squished!");
					     }
				     }


			    }

	     public static void remove_dead_bugs()
	           {
				 BugRecord current_bug_record;
				 for(int i=0; i< allBugs.size(); i++)
				     {
					   current_bug_record = (BugRecord) allBugs.get(i);
					   if(current_bug_record.health < 0)
					      {
							allBugs.remove(current_bug_record);
							cells[current_bug_record.row]
							     [current_bug_record.column].remove(current_bug_record);
					      }
				      }
				}

         public void paint(Graphics g)
              {
				g.clearRect(50,50,NUM_COLS*CELL_WIDTH+50,NUM_ROWS*CELL_HEIGHT+50);
				num_bugs = allBugs.size();
				g.drawString("Population size:  " + num_bugs, 600, 800);
				System.out.println("Population size:  " + num_bugs);
                for (int i=0; i < num_bugs; i++)
                     ((BugRecord) allBugs.get(i)).paint(g);
               }

  //  Methods needed to implement the "WindowListener" interface

     public void windowOpened(WindowEvent win_ev)  {}
     public void windowClosing(WindowEvent win_ev)  // Catches click on
                 {                                  //  the window's "X"
                  dispose();
                  }
     public void windowClosed(WindowEvent win_ev)   { System.exit(0);}
     public void windowActivated(WindowEvent win_ev)  {}
     public void windowDeactivated(WindowEvent win_ev)  {}
     public void windowIconified(WindowEvent win_ev)  {}
     public void windowDeiconified(WindowEvent win_ev)  {}

  //  Methods needed to implement the "MouseListener" interface

     public void mouseClicked(MouseEvent me)
                  {
                    System.out.println("mouse click at " +
                                       me.getX() + ", " + me.getY());
                    }
     public void mouseEntered(MouseEvent me)  {}
     public void mouseExited(MouseEvent me)  {}
     public void mousePressed(MouseEvent me)  {}
     public void mouseReleased(MouseEvent me)  {}


        }



 class BugRecord
       {
		   public Bug theBug;
		   public int health;
		   public int row;
		   public int column;
		   public int position_in_column;

		   public BugRecord(Bug new_bug)
		          {theBug = new_bug;  health = BugWorld.INIT_HEALTH;}

		   public boolean want_to_reproduce(int cost)
		          {
					if(health > cost)
					  return theBug.want_to_reproduce(cost);
					  else return false;
				  }

		   public BugRecord reproduce(int cost)
		          {

					  BugRecord offspring_r = new BugRecord(theBug.reproduce(cost));

					  //  Now make me, the parent, pay for reproduction!
					         health = health - cost;
					         theBug.lose_this(cost);

					  return offspring_r;

				  }
		   public void eat_this(int food_amt)
		        {
					theBug.eat_this(food_amt);
				}

		   public void move(int [] theNeighborhood)
		      {
			    int the_move = theBug.your_move(theNeighborhood);

			    if(the_move < 1 || the_move > 9)
			       {
					 System.out.println("Illegal move by "+
					                     theBug.getName()+", results in death");
					 health = -100;
				    }
			    else   // Legal moves
			     if(the_move==5)
			         {
						 health -= BugWorld.COST_OF_STAYING_STILL;
						 theBug.lose_this(BugWorld.COST_OF_STAYING_STILL);
					 }
			         else
			          {
						BugWorld.cells[row][column].remove(this);

					    if(the_move == 1 || the_move == 4 || the_move == 7)
			                column--;                           // move left
			                else if (the_move == 3 || the_move == 6 || the_move == 9)
			                         column++;     // move right
			            if(the_move <= 3)
			                row--;                             // move up
			               else if(the_move >= 7)
			               row++;                             // move down
			            health -= BugWorld.COST_OF_MOVING;
			            theBug.lose_this(BugWorld.COST_OF_MOVING);

			            //  Implement wrap-around effect
		                   row = (row+BugWorld.NUM_ROWS) % BugWorld.NUM_ROWS;
			               column = (column+BugWorld.NUM_COLS) % BugWorld.NUM_COLS;
			            //  Add self to top of new cell stack.
			               BugWorld.cells[row][column].addLast(this);
			    	  }



		       }


		   public void paint(Graphics g)
		          {theBug.paint(g.create(column * BugWorld.CELL_WIDTH+50,
		                                 row * BugWorld.CELL_HEIGHT+50,
		                                 BugWorld.CELL_WIDTH+50,
		                                 BugWorld.CELL_HEIGHT+50));}

		}

