
// Simulator.java
// Andrew Davison, Nov 2018, ad@fivedots.coe.psu.ac.th

/* A simple predator-prey simulator, based on a field containing
 * rabbits and foxes.
 * 
 * @author David J. Barnes and Michael Kolling
 * @version 2006.03.30
 */


import java.util.*;
import java.awt.Color;


public class Simulator
{
  // Constants representing configuration information for the simulation.
  private static final int DEFAULT_WIDTH = 50;   // grid depth and width
  private static final int DEFAULT_DEPTH = 50;

  private static final double FOX_CREATION_PROBABILITY = 0.02;
  private static final double RABBIT_CREATION_PROBABILITY = 0.08;    

  // Lists of animals in the field. Separate lists are kept for ease of iteration.
  private List<Rabbit> rabbits;
  private List<Fox> foxes;

  private Field field;
  private Field updatedField; // for holding the changed field

  private int step; // current simulation step
  private SimulatorView view; // simulation GUI
    

  public Simulator()
  { this(DEFAULT_DEPTH, DEFAULT_WIDTH); }
    

  public Simulator(int depth, int width)
  // create a simulation field with the given size
  {
    if (width <= 0 || depth <= 0) {
      System.out.println("The dimensions must be greater than zero.");
      System.out.println("Using default values.");
      depth = DEFAULT_DEPTH;
      width = DEFAULT_WIDTH;
    }
    rabbits = new ArrayList<Rabbit>();
    foxes = new ArrayList<Fox>();
    field = new Field(depth, width);
    updatedField = new Field(depth, width);

    // create a GUI view of the field
    view = new SimulatorView(depth, width);
    view.setColor(Rabbit.class, Color.orange);
    view.setColor(Fox.class, Color.blue);
        
    reset();      // initialize animals and field
  }  // end of Simulator()

    

  public void simulate(int numSteps)
  /* Run the simulation for the given number of steps.
     Stop if it ceases to be viable. */
  {
    for (int step = 1; step <= numSteps && view.isViable(field); step++) {
      simulateOneStep();
      wait(50);
    }
  }
    

  private void simulateOneStep()
  /* Run the simulation for a single step.
     Iterate over the whole field updating the state of every
     fox and rabbit. */
  {
    step++;

    // provide a list for new rabbits
    List<Rabbit> newRabbits = new ArrayList<Rabbit>();        

    // update all the rabbits
    for (Iterator<Rabbit> it = rabbits.iterator(); it.hasNext();) {
      Rabbit rabbit = it.next();
      rabbit.run(updatedField, newRabbits);
      if (!rabbit.isAlive())
        it.remove();
    }

    // add new rabbits to the existing rabbits list
    rabbits.addAll(newRabbits);
        
    // provide a list for new foxes
    List<Fox> newFoxes = new ArrayList<Fox>();        

    // update all the foxes
    for (Iterator<Fox> it = foxes.iterator(); it.hasNext();) {
      Fox fox = it.next();
      fox.hunt(field, updatedField, newFoxes);
      if (!fox.isAlive())
        it.remove();
    }

    // add new foxes to the existing foxes list
    foxes.addAll(newFoxes);

    // swap the field and updatedField info
    Field temp = field;
    field = updatedField;
    updatedField = temp;
    updatedField.clear();   // wipe the 'old' field

    // display the 'new' (updated) field on screen
    view.showStatus(step, field);
  }  // end of simulateOneStep()


  private void wait(int milliseconds)
  // waits for a specified number of milliseconds
  {
    try {
      Thread.sleep(milliseconds);
    } 
    catch (InterruptedException e) { }   // ignore exception
  } // end of wait()


        
  private void reset()
  // reset the simulation
  {
    step = 0;
    rabbits.clear();
    foxes.clear();
    field.clear();
    updatedField.clear();
    populate(field);
        
    // show the starting state in the view
    view.showStatus(step, field);
  }  // end of reset()
    

  private void populate(Field field)
  // populate a field with foxes and rabbits
  {
    Random rand = new Random();
    field.clear();
    for (int row = 0; row < field.getDepth(); row++) {
      for (int col = 0; col < field.getWidth(); col++) {
        if (rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
          Fox fox = new Fox(true);
          fox.setLocation(row, col);
          foxes.add(fox);
          field.place(fox, row, col);
        }
        else if (rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
          Rabbit rabbit = new Rabbit(true);
          rabbit.setLocation(row, col);
          rabbits.add(rabbit);
          field.place(rabbit, row, col);
        }
        // else leave the location empty.
      }
    }
    Collections.shuffle(rabbits);
    Collections.shuffle(foxes);
  }  // end of populate()


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

  public static void main(String[] args) 
  {  
    Simulator sim = new Simulator(100, 100);     // default is 50x50
    sim.simulate(500);   // run for 500 steps
  }  // end of main()

}  // end of Simulator class
