#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "TicTacToe.h"
#include "patterns.h"



#if 0
//985125
int main()
{
  int winner,x;
  unsigned long totalDraws = 0;
  unsigned long totalXWins = 0;
  unsigned long totalYWins = 0;
  unsigned long totalScore = 0;

  TicTacToeGame * ttt = new TicTacToeGame();

//  while (1)
  for (x=0; x<100000; x++)
  {
    ttt->SetWinPatterns(winPatterns, almostWinPatterns);
    winner = ttt->PlayTicTacToeGame();
    
    
    if (winner == TIE)
      totalScore+=5;
    if (winner == X)
      totalScore+=10;

//    printf("TotalScore: %ld\n", totalScore);
    
    
    /*
    if (winner == TIE)
      totalDraws ++;
    if (winner == X)
      totalXWins++;
    if (winner == Y)
      totalYWins++;

    printf("Draws: %ld\n", totalDraws);
    printf("XWins: %ld\n", totalXWins);
    printf("OWins: %ld\n\n", totalYWins);
*/
  
  }
  printf("TotalScore: %ld\n", totalScore);

  return 0;
}

#endif


#if 1
//86320
#define getrandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))

#define VECSIZE      288 

#define POPULATION   20
#define MUTATIONRATE 0.900
#define XOVERRATE    0.667
#define ACC          1000
#define GENERATIONS  100
#define TOP_COUNT    10
#define GAMES_TOP_PLAY 1000

double eval(int *pj, int _testVecs[][VECSIZE]);
int FindMaxFitness (double * _fitness);
void CombineVecs(int * _vecA, int *_vecB);
void initVecs(int * _vec);
void DumpTopFitness(double _topFitness, unsigned long _count, 
                    unsigned long _topFitnessCount, int * _vec);
void FindMates(double * _fitness, int *_mates);
void Decode(int * vec, unsigned long *a, unsigned long *b, unsigned long *c);
void XOverMutate(int * _vecA, int * _vecB);
void FindFitness(double *_fitness, double * maxFitness, 
                 double *avgFitness, double *minFitness, int * _topFitness);

void GenerateWinPatterns(int *pj, unsigned long * _almostWinPatterns, 
                                  unsigned long * _winPatterns);


int main()
{
  int xVec[POPULATION][VECSIZE];
  int oVec[POPULATION][VECSIZE];

  int xTopVecs[TOP_COUNT][VECSIZE];
  int oTopVecs[TOP_COUNT][VECSIZE];

  int tempXVec[POPULATION][VECSIZE];
  int tempOVec[POPULATION][VECSIZE];
  int topXVec[VECSIZE];
  int topOVec[VECSIZE];
  double xFitness[POPULATION], topXFitness, avgXFitness, minXFitness;
  double oFitness[POPULATION], topOFitness, avgOFitness, minOFitness;
  int mates[POPULATION]; 

 
  int i,z,y;
  unsigned long count = 0;
  unsigned long topFitnessCount=0;
  int topVecFitness[TOP_COUNT];
  FILE * myFile;
  srand(time(NULL));
  topXFitness = 0;
  topOFitness = 0;
  
  for(i = 0; i < POPULATION; i++)
  { 
    initVecs(xVec[i]);
    initVecs(oVec[i]);
  }
  for (int x = 0; x< TOP_COUNT; x++)
  {
    memcpy(&xTopVecs[x], &xVec[x], VECSIZE*sizeof(int));
    memcpy(&oTopVecs[x], &oVec[x], VECSIZE*sizeof(int));
  }  //Some defaults for gen 0...

//  while (1)
 for (y = 0; y< 30; y++)
 {
  for ( z = 0; z < GENERATIONS; z++)
  {

    for (i=0; i < POPULATION; i++)
    { //Need to have something in xTopVecs.. Not much I admit, but SOMETHING
      xFitness[i] = eval(xVec[i], oTopVecs);
      oFitness[i] = eval(oVec[i], xTopVecs);
    }
    topVecFitness[TOP_COUNT];
    FindFitness(xFitness, &topXFitness, &avgXFitness, &minXFitness, 
                topVecFitness);
    for (int x = 0; x< TOP_COUNT; x++)
    {
      memcpy(&xTopVecs[x], &xVec[topVecFitness[x]], VECSIZE*sizeof(int));
    }
    FindFitness(oFitness, &topOFitness, &avgOFitness, &minOFitness, 
                topVecFitness);
    for (int x = 0; x< TOP_COUNT; x++)
    {
      memcpy(&oTopVecs[x], &oVec[topVecFitness[x]], VECSIZE*sizeof(int));
    }



    count++;    
    topFitnessCount = count;
    fprintf(stderr, "%d\t%lf\t%lf\t%lf\n", count, topXFitness, avgXFitness, minXFitness);
    fprintf(stderr, "%d\t%lf\t%lf\t%lf\n", count, topOFitness, avgOFitness, minOFitness);

    int maxXFitness = FindMaxFitness (xFitness);
    int maxOFitness = FindMaxFitness (oFitness);
    FindMates(xFitness, mates);
    for (int x = 0; x< POPULATION; x++)
    {
      memcpy(tempXVec[x], xVec[x], VECSIZE*sizeof(int));
    }
    for (i = 0; i < POPULATION ; i++)
    {
      if (getrandom(0,ACC) < XOVERRATE * ACC)
        XOverMutate(xVec[i], tempXVec[mates[i]]);
    }

    FindMates(oFitness, mates);
    for (int x = 0; x< POPULATION; x++)
    {
      memcpy(tempOVec[x], oVec[x], VECSIZE*sizeof(int));
    }
    for (i = 0; i < POPULATION ; i++)
    {
      if (getrandom(0,ACC) < XOVERRATE * ACC)
        XOverMutate(oVec[i], tempOVec[mates[i]]);
    }


    //calling eval twice????
/*
    for (i=0; i < POPULATION; i++)
    {
      xFitness[i] = eval(xVec[i]);
    }

    FindFitness(xFitness, &topXFitness, &avgXFitness, &minXFitness);
    count++;    
    topFitnessCount = count;
//    if (count % 1000 == 0)
    { 
      printf("%d\t%lf\t%lf\t%lf\n", count, topXFitness, avgXFitness, minXFitness);
//      fprintf(myFile, "%d\t%lf\t%lf\t%lf\n", z, topFitness, avgFitness, minFitness);
//      DumpTopFitness(topFitness, count, 
//                      topFitnessCount, vec[POPULATION-1]);
    }
*/
  }
// fclose(myFile);
 }
}

void FindMates(double * _fitness, int *_mates)
{
  double totalFitness = 0;
  int ranges[POPULATION];

  for (int x = 0; x< POPULATION; x++)
  {
    totalFitness += _fitness[x];
    ranges[x] = (int)totalFitness;
  }
  for (int x = 0; x< POPULATION; x++)
  {
//Potential issue here person can mate with himself
    int location = getrandom(0,(int)totalFitness);
    _mates[x] = POPULATION;
    for (int y = 0; y< POPULATION ; y++)
    {
      if (location <= ranges[y])
      {
        _mates[x] = y;
        break;
      }
    }
  }
}
void FindFitness(double *_fitness, double * maxFitness, 
                 double *avgFitness, double *minFitness, int * _topFitness)
{
  double total=0;

  *maxFitness = 0;
  *minFitness = 10000;
  *avgFitness = 0;

  for (int y = 0; y< TOP_COUNT; y++)
  {
    _topFitness[y] = -1;
  }


  for (int x = 0; x<POPULATION;x++)
  {
    if (_fitness[x] > *maxFitness)
      *maxFitness = _fitness[x];
    if (_fitness[x] < *minFitness)
      *minFitness = _fitness[x];

    for (int y = TOP_COUNT-1; y>= 0; y--)
    {
      if (_topFitness[y] == -1)
      {
        _topFitness[y] = x;
        break;

      }
      else if (_fitness[x] > _fitness[_topFitness[y]])
      {
        _topFitness[y] = x;
        break;

      }
    }
    total+= _fitness[x];
  }


//  for (int y = 0; y< TOP_COUNT; y++)
//    printf("Top: %d\n", _topFitness[y]);
  //printf("TOTAL: %lf\t%lf\n", total, total/POPULATION);
  *avgFitness = total/POPULATION;

}
int FindMaxFitness (double  * _fitness)
{
  double maxFitness = 0;
  int maxLoc = 0;
 
  for (int x = 0; x< POPULATION; x++)
  {
    if ( _fitness[x] > maxFitness)
    {
      maxFitness = _fitness[x];
      maxLoc = x;
    }
  }
  return maxLoc;
}

void XOverMutate(int * _vecA, int * _vecB)
{
  int x;
  int xOverPoint = getrandom(0, VECSIZE-1);
  for (x = 0; x < VECSIZE; x++)
  {
    if (x > xOverPoint)
      _vecA[x] = _vecB[x];

    if (!(getrandom(0,int(1/MUTATIONRATE))))
    {
      if (_vecA[x] == 0)
        _vecA[x] = 1;
      else
        _vecA[x] = 0;
    }
  }
}


void CombineVecs(int * _vecA, int *_vecB)
{
  int totalHits = 0;
  int hits[VECSIZE];

  for (int x = 0; x < VECSIZE; x++)
  {
    hits[x] = 0;
  }
  while (totalHits < (int) (VECSIZE / 2))
  {
    int temp = getrandom(0, VECSIZE-1);
    if (hits[temp] == 0)
    {
      hits[temp] = 1;
      totalHits ++;
    }
  }
  for (int x = 0; x< VECSIZE; x++)
  {
    if (hits[x])
    {
//      printf("Setting %d\n", x);
      _vecA[x] = _vecB[x];
    }
  }


}



void initVecs(int * _vec)
{
  for (int x = 0; x < VECSIZE; x++)
  {
    _vec[x] = getrandom(0,1);
  }
}





void DumpTopFitness(double _topFitness, unsigned long _count, 
                    unsigned long _topFitnessCount, int * _vec)
{
  unsigned long winPatterns[8];
  unsigned long almostWinPatterns[24];
  int x,y;

  memset(winPatterns, 0, 8 * sizeof (unsigned long));
  memset(almostWinPatterns, 0, 24 * sizeof (unsigned long));



  for (x = 0; x < 24; x ++)
  {
    for (y=0; y<9; y++)
    {
      if (_vec[x*9+y])
      {
        almostWinPatterns[x] |= 1<<y;
      }

    }
  }
//  for (x = 217; x < 288; x++)
  for (x = 0; x < 8; x++)
  {
    for (y=0; y<9; y++)
    {
      if (_vec[217+x*9+y])
      {
        winPatterns[x] |= 1<<y;
      }

    }

  }

  for (x = 0; x<24;x++)
  {
    printf ("  %ld,\n", almostWinPatterns[x]);
  }
  printf("\n");
  for (x = 0; x<8;x++)
  {
    printf ("  %ld,\n", winPatterns[x]);
  }
  printf("\n");

  printf("Top Fitness after %ld iterations: %lf [%ld]\n", _count, _topFitness,
              _topFitnessCount);
  for(int i = 0; i < VECSIZE; i++)
    printf("%d ", _vec[i]);
  printf("\n");

}

double eval(int *pj, int _testVecs[][VECSIZE])
{
  int totalScore = 0;
  int winner;
  int x;
  
  unsigned long almostWinPatterns[24];
  unsigned long winPatterns[8];

  memset(almostWinPatterns, 0, 24 * sizeof(unsigned long));
  memset(almostWinPatterns, 0, 8 * sizeof(unsigned long));
  GenerateWinPatterns (pj, almostWinPatterns, winPatterns);

  TicTacToeGame * ttt = new TicTacToeGame();
  for (x = 0; x < GAMES_TOP_PLAY; x++)
  {
    ttt->SetXWinPatterns(winPatterns, almostWinPatterns);
    for (int y = 0; y < TOP_COUNT; y++)
    {
      unsigned long oppAlmost[24];
      unsigned long oppWin[8];
      memset(oppAlmost, 0, 24 * sizeof(unsigned long));
      memset(oppWin, 0, 8 * sizeof(unsigned long));
      GenerateWinPatterns (_testVecs[y], oppAlmost, oppWin);
      ttt->SetOWinPatterns(oppWin, oppAlmost);
      
      winner = ttt->PlayTicTacToeGame();
//      printf("%d %d\n",x,y);
//      usleep (1); 
//      printf("%d %d\n",x,y);
      printf("WINNER: %d:%ld\n", winner, totalScore);
      int scoreMult;
      if (!ttt->DidIGoFirst())
        scoreMult = 2;
      else
        scoreMult = 1;
      if (winner == TIE)
        totalScore += 5*scoreMult;
      if (winner == X)
        totalScore += 10*scoreMult;
      printf("WINNER: %d:%ld\n", winner, totalScore);
    }
  }
  //  printf("TOTALSCORE: %ld\n", totalScore);
  delete (ttt);
  return double(totalScore);


}

void GenerateWinPatterns(int *pj, unsigned long * _almostWinPatterns, 
                                  unsigned long * _winPatterns)
{
  int x,y;

  for (x = 0; x < 24; x ++)
  {
    for (y=0; y<9; y++)
    {
      if (pj[x*9+y])
      {
        _almostWinPatterns[x] |= 1<<y;
      }

    }
  }
//  for (x = 217; x < 288; x++)
  for (x = 0; x < 8; x++)
  {
    for (y=0; y<9; y++)
    {
      if (pj[217+x*9+y])
      {
        _winPatterns[x] |= 1<<y;
      }

    }
  }
}
#endif
