/*----------------------------------------------------------------------+
|      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.                                              |
+----------------------------------------------------------------------*/

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 Strategy maximumStrategy;
  private int cumulativeScore[];

  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,new GenStrategy(p,"Live",gen));
    }
    Q = new Player[population];
    strategyData = new int[population];
    maximumStrategy = P[0].S;
    cumulativeScore = new int[population];
  }//createPlayers


  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]);
    }
    analyzeStrategies();
  }

  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.S.firstMove();
      B.thisMove = B.S.firstMove();
    } else if (A.movenumber == 1) {
      A.thisMove = A.S.secondMove(A.previousMove,B.previousMove,A.score);
      B.thisMove = B.S.secondMove(B.previousMove,A.previousMove,B.score);
    } else  {
      A.thisMove = A.S.thirdMove(A.lastMove,B.lastMove,
      A.previousMove,B.previousMove,A.score);
      B.thisMove = B.S.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.S.leave(A.thisMove,B.thisMove,A.score) ||
                B.S.leave(B.thisMove,A.thisMove,B.score)) {
      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 (int n) {  // n animals of the herd die
    for (int d=0; d<n; ++d) {
      int dp;
      do
        dp = (int)(Math.random()*population);
      while (!P[dp].exists()) ;
      P[dp].remove();
    }
  } // death
  
  public void birth () { // Create new players for the voided ones
    // 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())
        P[p].set(p,selectStrategy(cumulativeScore));
  }  // birth

  
  private Strategy selectStrategy(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].S;
  } // selectStrategy

  
  private void analyzeStrategies() {
    int max=0;
    for (int s=0; s<population; ++s)
      strategyData[s] = 0;
    for (int p=0; p<population; ++p) {
      int stratNum = P[p].S.strategyNumber;
      strategyData[stratNum]++;
      if (strategyData[stratNum] > max) {
        max = strategyData[stratNum];
        maximumStrategy = P[p].S;
      }
    } // for
  } // analyzeStrategies() 
    
  public int[] getStrategyData() {
    return strategyData;
  }
  
  public Strategy getMaxStrategy() {
    return maximumStrategy;
  }


} // Herd.java
