/*----------------------------------------------------------------------+
|      Title:	Herd.java                                               |
|                                                                       |
|      Author:	David E. Joyce                                          |
|           Department of Mathematics and Computer Science              |
|           Clark University                                            |
|           Worcester, MA 01610-1477                                    |
|           U.S.A.                                                      |
|                                                                       |
|           http://aleph0.clarku.edu/~djoyce/                           |
|           djoyce@clarku.edu                                           |
|                                                                       |
|      Date:    May, 2002.                                              |
+----------------------------------------------------------------------*/

import java.awt.*;

public class Herd {

  private int population;
  private int payoff[][];
  private Player P[];  // array of players
  private Player Q[];  // array of quitters
  private int nQ;      // number of quitters
  private int strategyData[];
  private int cumulativeScore[];
  private Color genomeColor[];

  public Herd(int populationIn) {
    population = populationIn;
    // Create the players
    P = new Player[population];
    for (int p=0; p<population; ++p) {
      Genome gen = new Genome();
      gen.randomize();
      P[p] = new Player(p,gen);
    }
    Q = new Player[population];
    strategyData = new int[population];
    cumulativeScore = new int[population];
    genomeColor = new Color[population];  
  }//createPlayers

  public Player getRandomPlayer() {
    return P[(int)(Math.random()*population)];
  }

  public void runMatch(int lengthOfMatch, int payoffIn[][]) {
    payoff = payoffIn;
    // Put all the players in the quitters array
    nQ = 0;
    for (int p=0; p<P.length; ++p)
      Q[nQ++] = P[p];
    // Clear the scores
    for (int p=0; p<P.length; ++p)
      P[p].score = 0;
    // now do all the plays
    for (int r=0; r<lengthOfMatch; ++r) {
      matchQuitters();
      // let each player have a turn
      for (int i=0; i<P.length; ++i)
        if (P[i].partner != -1 && i < P[i].partner)
          play(P[i],P[P[i].partner]);
    } //for
  } // runMatch
  
  private void matchQuitters() {
    //Simply shuffle them
    for (int i=0; i<nQ-1; ++i) {
      int j = i + (int)((nQ-i)*Math.random());
      Player Temp = Q[i];
      Q[i] = Q[j];
      Q[j] = Temp;
    }
    // now adjacent pairs will pair off, starting at the end
    while (nQ>=2) {
      --nQ;
      Q[nQ].partner = Q[nQ-1].id;
      Q[nQ-1].partner = Q[nQ].id;
      Q[nQ].movenumber = 0;
      Q[nQ-1].movenumber = 0;
      --nQ;
    }
  } // matchQuitters



  private void play(Player A, Player B) {
    // determine what move each player is going to make
    if (A.movenumber == 0) {
      A.thisMove = A.G.firstMove();
      B.thisMove = B.G.firstMove();
    } else if (A.movenumber == 1) {
      A.thisMove = A.G.secondMove(A.previousMove,B.previousMove,A.score);
      B.thisMove = B.G.secondMove(B.previousMove,A.previousMove,B.score);
    } else  {
      A.thisMove = A.G.thirdMove(A.lastMove,B.lastMove,
      A.previousMove,B.previousMove,A.score);
      B.thisMove = B.G.thirdMove(B.lastMove,A.lastMove,
      B.previousMove,A.previousMove,B.score);
    }
    // add their payoffs
    A.score += payoff[A.thisMove][B.thisMove];
    B.score += payoff[B.thisMove][A.thisMove];
    // does ether want to break the partnership?
    if (A.G.leave(A.thisMove,B.thisMove) ||
                B.G.leave(B.thisMove,A.thisMove)) {
      A.partner = -1;
      B.partner = -1;
      Q[nQ++] = A;
      Q[nQ++] = B;
    } else {
      A.previousMove = A.lastMove;
      B.previousMove = B.lastMove;
      A.lastMove = A.thisMove;
      B.lastMove = B.thisMove;
      ++A.movenumber;
      ++B.movenumber;
    }
    return;
  } // play

    public void death (double deathRate) {  // animals die at the given rate
    for (int p=0; p<population; ++p)
      if (Math.random() < deathRate)
        P[p].remove();
  } // death
  
  // Create new players to replace the dead ones. Use asexual reproduction
  // according to the rate; sexual otherwise.
  public void birth (double asexualRate) { 
    // Compute the cumulative scores across the players
    int cum=0;
    for (int p=0; p<population;++p) {
      if (P[p].id != -1)
        cum += P[p].score;
      cumulativeScore[p] = cum;
    }
    // Now, for each removed player, select another.
    for (int p=0; p<population;++p)
      if (!P[p].exists()) 
        if (Math.random()<asexualRate) 
          P[p].set(p, selectGenome(cumulativeScore));
        else {
          Genome mom = selectGenome(cumulativeScore);
          Genome dad = selectGenome(cumulativeScore);
          P[p].set(p,Genome.cross(mom,dad));
        } //if
  }  // birth

  public void mutate(double mutationRate) {
    for (int p=0; p<population; ++p)
      if (Math.random() < mutationRate)
        P[p].G = P[p].G.mutate(0.1);
  } // mutate

  private Genome selectGenome(int cumulativeScore[]) {
    // Simply randomly select an existing player's strategy weighted by its score
    int score =(int)(Math.random()*cumulativeScore[population-1]);
    int p;
    for (p=0; p<population; ++p)
      if (cumulativeScore[p]>=score)
        break;
    if (p == population) {// error
      System.out.println(" Error in Herd.selectStrategy.");
      System.exit(0);
    }
    return P[p].G;
  } // selectStrategy

  public Color[] getGenomeColor() {
    for (int p=0; p<population; ++p)
      genomeColor[p] = P[p].G.toColor();
    return genomeColor;
  } //getGenomeColor

  public Stats[] getStats() {
    return Genome.getStats(P);
  } //getStats

} // Herd.java


