import java.awt.Color;

public class Genome {

  private double gene[];
  private static int redGene,greenGene,blueGene;

  public static final int length = 23;

  // Moves are either "cooperate" ("nice") or "defect" ("nasty")
  public static final int COOPERATE = 0;
  public static final int DEFECT = 1;

  /* 1: first move, prob of defect
   * 2: leave if partner cooperated
   * 3: leave if partner defected
   * 4-7: second move, probs of defect
   *   4: if both cooperated; 5: if partner defected; 6: if self defected; 7: if both defected
   * 8-23: third move, probs of defect
   *   self cooperated last play 8-15;
   *     self defected last play 16-23
   *   partner cooperated last play 8-11,16-19;
   *     partner defected last play 12-15,20-23
   *   self cooperated previous play 8-9,12-13,16-17,20-21;
   *     self defected previous play 10-11,14-15,18-19,22-23
   *   partner cooperated previous play 8,10,12,14,16,18,20,22;
   *     partner defected previous play 8,11,13,15,17,19,21,23
   */

  // 1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23
  public static final double[] NICE = 
   {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
  public static final double[] NASTY = 
   {1.0,0.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
  public static final double[] TIT4TAT = 
   {0.0,0.0,0.0,0.0,1.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0};
  public static final double[] MOTH =
   {0.0,0.0,1.0,0.0,0.5,0.0,0.5,0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,0.0,0.0,0.0,0.5,0.5,0.5,0.5};
  public static final double[] HITNRUN =
   {1.0,1.0,1.0,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5};
  public static final double[] SANTA = 
   {0.0,1.0,1.0,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5};
  public static final double[] NASMOTH = 
   {1.0,0.0,1.0,1.0,0.5,1.0,0.5,1.0,1.0,1.0,1.0,0.5,0.5,0.5,0.5,1.0,1.0,1.0,1.0,0.5,0.5,0.5,0.5};
  public static final double[] NNHRUN = 
   {0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
   

  public Genome() {
    gene = new double[length];
  }

  public static int getLength() { return length; }

  public int firstMove() {
    return (Math.random() > gene[0]) ? 0 : 1;
  }

  public boolean leave (int myLastPlay, int otherLastPlay) {
    return (Math.random() > gene[1 + otherLastPlay])? false : true;
  }

  public int secondMove (int myLastPlay, int otherLastPlay) {
    return (Math.random() >  gene[3 + 2*myLastPlay + otherLastPlay]) ? 0 : 1;
  }

  public int thirdMove  (int myLastPlay, int otherLastPlay,
                         int myPreviousPlay, int otherPreviousPlay) {
    // return secondMove(myLastPlay, otherLastPlay);
    return (Math.random() > gene[7 + 8*myLastPlay + 4*otherLastPlay +
                            2*myPreviousPlay + otherPreviousPlay]) ? 0 : 1;
  }

  public void randomize() {
    for (int i=0; i<length; ++i){
      gene[i] = MOTH[i];
      if (MOTH[i] == 0.5)
        gene[i]=Math.random();
    }
  } // randomize

  public Genome duplicate() {
    Genome other = new Genome();
    for (int i=0; i<length; ++i)
        other.gene[i] = gene[i];
    return other;
   } // duplicate

  public void mutate(double amount) {
    for (int i=0; i<length; ++i)
      gene[i] = adjust(gene[i],amount);
  } // mutate

  private static double adjust(double value, double mutationAmount) {
    double newValue = value + mutationAmount*2.0*Math.random() - mutationAmount;
    if (newValue < 0.0)
      newValue = 0.0;
    else if (newValue > 1.0)
      newValue = 1.0;
    return newValue;
  } // adjust

  public static Genome cross(Genome first, Genome second) {
    Genome third = new Genome();
    for (int i=0; i<length; ++i)
      third.gene[i] = (Math.random() < 0.5)? first.gene[i] : second.gene[i];
    return third;
  } // cross


  public String toString() {
    String s = "";
    for (int i=0; i<length; ++i)
      s = s+digit(gene[i]);
    return s;
  } // toString

  private String digit(double prob) {
    if (prob == 1.0)
      return "*";
    else
      return Integer.toString((int)(10.0*prob));
  }

 public static Stats[] getStats(Genome G[]) {
    Stats stats[] = new Stats[length];
    int n = G.length;
    for (int i=0; i<length; ++i) {
      stats[i] = new Stats();
      double sum = 0.0;
      double sumOfSquares = 0.0;
      for (int j=0; j<n; ++j) {
        double value = G[j].gene[i];
        sum += value;
        sumOfSquares += value*value;
      } // for j
      if (n > 0)
        stats[i].mean = sum/n;
      else
        stats[i].mean = 0.0;
      if (n > 1)
        stats[i].standardDeviation = Math.sqrt((sumOfSquares-sum*stats[i].mean)/(n-1));
      else
        stats[i].standardDeviation = 0.0;
    } // for i
    return stats;
  } // getStats

  public static void setColorGenes(int redGene, int greenGene, int blueGene) {
    Genome.redGene = redGene;
    Genome.greenGene = greenGene;
    Genome.blueGene = blueGene;
  }

  public static void setRedGene(int redGene) { Genome.redGene = redGene; }
  public static void setGreenGene(int greenGene) { Genome.greenGene = greenGene; }
  public static void setBlueGene(int blueGene) { Genome.blueGene = blueGene; }
  public static int getRedGene() {return redGene;}
  public static int getGreenGene() {return greenGene;}
  public static int getBlueGene() {return blueGene;}

  public static Color chooseColor(double r, double g, double b) {
    return new Color((float)r, (float)(g), (float)(b));
  }

  public Color toColor() {
    return chooseColor(gene[redGene],gene[greenGene],gene[blueGene]);
  } //toColor


} // Genome

