/*
  Battle.java

  Title:      Battle
  Author:     D. Joyce
  Description:  
*/


import java.awt.*;
//import java.awt.event.*;
import java.applet.*;

public class Battle extends Applet {
  // member declarations
  int simulations = 10;
  int nS = 8;  // number of strategies
  int initPlayersPerStrategy = 10;
  int nP = nS*initPlayersPerStrategy; // number of players, must be even
  Strategy S[] = new Strategy[nS];
  Player P[] = new Player[nP];
  int lengthOfMatch = 20;
  int numberOfMatches = 20;

  Player Q[] = new Player[nP];  // array of quitters
  int nQ = 0; // number of quitters

  boolean isStandalone = false;

  boolean showPlay = false;
  boolean showStats = false;

  public Battle() {
  }

  // Retrieve the value of an applet parameter
  public String getParameter(String key, String def) {
    return isStandalone ? System.getProperty(key, def) :
      (getParameter(key) != null ? getParameter(key) : def);
  }

  // Get info on the applet parameters
  public String[][] getParameterInfo() {
    return null;
  }

  // Get applet information
  public String getAppletInfo() {
    return "Applet Information";
  }

  // Initialize the applet
  public void init() {
    try {
      initComponents();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void initComponents() throws Exception {
    // the following code sets the frame's initial state
    //setLocation(new java.awt.Point(0, 0));
    setLayout(null);
    //setSize(new java.awt.Dimension(400, 300));

    // set up the Strategy array S
    S[0] = new Strategy(); //Nice Strategy
    S[0].name = "     Nice";
    S[1] = new Nasty();
    S[1].name = "    Nasty";
    S[2] = new TitForTat();
    S[2].name = "TitForTat";
    S[3] = new Moth();
    S[3].name = "     Moth";
    S[4] = new HitAndRun();
    S[4].name = "HitAndRun";
    S[5] = new Santa();
    S[5].name = "    Santa";
    S[6] = new NastyMoth();
    S[6].name = "NastyMoth";
    S[7] = new NNHitRun();
    S[7].name = " NNHitRun";

    //S[5] = new RandomNoMemory();
    //(RandomNoMemory) S[5].randomize();
    for (int s=0; s<nS; ++s)
      S[s].strategyNumber = s;

    for (int i=0; i<simulations; ++i)
      doSimulation();
  }

  void doSimulation() {
    // initialize Player array P and
    // put them all in the quitters array
    nQ = 0;
    for (int p=0; p<nP; ++p) {
      P[p] = new Player();
      P[p].id = p;
      P[p].S = S[p/initPlayersPerStrategy];
      Q[nQ++] = P[p];
    }
    // simulate the game
    if (showStats)
        System.out.println ("\nSimulation begins");
    for (int m=0; m<numberOfMatches; ++m) {
        if (showStats)
          System.out.println("\nMatch "+m+" begins");
        playMatch();
        if (showStats)
          System.out.println("Scores");
        reassignPlayers();
    }
    if (showStats)
      System.out.println("\nSimulation complete.");
    else {
      System.out.println("\nFinal populations:");
      for (int s=0; s<nS; ++s)
        if (S[s].pop!=0)
          System.out.println (S[s].name+": "+S[s].pop);
    }
  }

  void playMatch() {
    // first clear the scores
    for (int p=0; p<nP; ++p)
      P[p].score = 0;
    // now do all the plays
    for (int r=0; r<lengthOfMatch; ++r) {
      if (showPlay) System.out.println("\nRound "+r);
      matchQuitters();
      // let each player have a turn
      for (int i=0; i<nP; ++i)
        if (P[i].partner != -1 && i < P[i].partner) {
          if (showPlay)
            System.out.print("  p="+i
              +" s="+P[i].S.strategyNumber
              +" vs. p="+P[i].partner+" s="
              +P[P[i].partner].S.strategyNumber
              +"m="+P[i].movenumber+"    ");
          play(P[i],P[P[i].partner]);
        }
    }
  }

  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;
    }
  }

  void play(Player A, Player B) {
    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);
    }
    if (showPlay)
      System.out.print(A.thisMove+" vs. "+B.thisMove);
    if (showPlay)
      System.out.print("\n    Old scores:"+A.score+" vs "+B.score);
    if (A.thisMove == 0 && B.thisMove == 0) {
      A.score += 3;
      B.score += 3;
    } else if (A.thisMove == 0 && B.thisMove == 1) {
      A.score += 0;
      B.score += 5;
    } else if (A.thisMove == 1 && B.thisMove == 0) {
      A.score += 5;
      B.score += 0;
    } else { // (A.thisMove == 1 && B.thisMove == 1)
      A.score += 1;
      B.score += 1;
    }
    if (showPlay)
    System.out.println("\n    New Scores: "+A.score+" vs. "+B.score);
    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;
      if (showPlay)
        System.out.println("  Player quit.");
      } else {
        A.previousMove = A.lastMove;
        B.previousMove = B.lastMove;
        A.lastMove = A.thisMove;
        B.lastMove = B.thisMove;
        ++A.movenumber;
        ++B.movenumber;
      }
      return;
  }

  void reassignPlayers() {
      // compute total scores for the strategies
      int total[] = new int[nS];
      for (int p=0; p<nP; ++p) {
        int s=P[p].S.strategyNumber;
        total[s] += P[p].score;
        //System.out.println("P["+p+"].score="+P[p].score
        //  +" total["+s+"]="+total[s]);
      }
      // compute the grand total
      int grandTotal = 0;
      for (int s=0; s<nS; ++s) {
        grandTotal += total[s];
      }
      if (showStats)
        System.out.println ("Total score: "+grandTotal);
      // determine how many players there should be for
      // each strategy and assign the strategies
      int p = 0;
      for (int s=0; s<nS; ++s) {
        S[s].pop = total[s]*nP/grandTotal;
        if (total[s]!=0 && showStats)
          System.out.println (S[s].name+": "+total[s]
              + "   "+S[s].pop+" players");
        for (int j=0; j<S[s].pop; ++j)
          P[p++].S = S[s];
      }
      // Because of truncation, maybe not all players
      // have been assigned a strategy
      // Choose the remaining strategies randomly
      // weighted by the totals
      while (p<nP) {
        int f = (int)(grandTotal*Math.random());
        int s = nS;
        for (int i=grandTotal; i>f; i-=total[--s]); //no body for loop
        if (s==nS)
          System.out.println ("***  p="+p+"  f = "+f+"  s = "+s);
        S[s].pop++;
        P[p++].S = S[s];
      }
  }

  // Standard method to start the applet
  public void start() {}

  // Standard method to stop the applet
  public void stop() {}

  // Standard method to destroy the applet
  public void destroy() {}
}
