/*! 
*******************************************************************************
  \file    ttt-driver.c
  \author  Matthew Mead
  \par     email: mmead\@digipen.edu
  \date    8/27/2012
  \brief   Test driver for the TicTacToe plugin and dual player files.
  
  This test driver interfaces with the TicTacToe plugin directly. It does not
  require networking or a server. The plugin is the same code that the server
  uses so this should test the client exactly like the server would. The 
  benefit of this is that this client can play about 1,000,000 games per second.
  
  This is a slightly modified version of another TTT driver. This driver does
  not include the code for the players to generate a move. Instead, that code
  is in two separate files, one for each player. This driver expects those
  files to implement GetNextMove1 and GetNextMove2, for player 1 and player 2,
  respectively.
  
  To run this program from the command line:
  
      ./ttt-dual {count}

  where count is the number of games to play. For example:
    
      ./ttt-dual 10000
    
  will play 10,000 games of Tic-Tac-Toe and display the statistics. Look at 
  the Options below to enable/disable more/less output. There is a makefile
  that should be included with this file, other wise:
  
  gcc -O2 ttt-driver.c ttt-p1.c ttt-p2.c TicTacToe.c -o ttt-dual
  
  On modern hardware (Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz) this program
  will play about 800,000 games per second. This varis depending on the
  intelligence of the players, i.e. random playing or intelligent playing.
  
*******************************************************************************/
#include <stdio.h>  /* printf, sprintf, fflush */
#include <stdlib.h> /* atoi                    */

#include "TicTacToe.h"
#include "PluginInterface.h"

/*
  The server creates its own Player structs internally. I'm just using this
  as a place-holder for the server's player. It can be anything actually.
*/
struct Player
{
  int dummy;
};

/* The game that is created by the server (i.e. plugin) */
static struct Game* gGame = 0;

/* Options */
static int gSilent = 1;      /* Produce very little output (just displays totals)           */
static int gVerbose = 0;     /* Produce copious amounts of output during play               */
static int gDrawBoards = 0;  /* Show the board at the end                                   */

static int gPlayer = 1;      /* Which player is currently making a move                     */
static int gOther = 2;       /* Which player is not making a move                           */
static int gCount = 1;       /* The number of games to play                                 */
static int gWins[3] = {0};   /* gWins[1] counts wins for player 1, gWins[2] counts player 2 */
static int gLosses[3] = {0}; /* same as gWins except keeps track of losses                  */
static int gDraws = 0;       /* Total number of draws                                       */
static int gGamesPlayed = 0; /* The number of games played thus far                         */

/* For drawing the board on the screen */
static const char EMPTY_SQUARE = '.';
static const char SYMBOLS[] = {' ', 'X', 'O'};

/* Track how many moves it took for a win i.e. 
    gWinsMovesX[7]++ means that X won on move #7
*/
static int gWinsMovesX[10] = {0};
static int gWinsMovesO[10] = {0};
static void *gPlayers[2] = {0};

/* The representation of the tic-tac-toe board */
char Board[9] = {0};


/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/* Implemented externally */
int GetNextMove1(void); /* Player 1's move */
int GetNextMove2(void); /* Player 2's move */

static int GetNextMove(void)
{
  int position;

  if (gPlayer == 1)
    position = GetNextMove1();
  else
    position = GetNextMove2();

  return position;
}
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/



static void ClearBoard(void)
{
  int i;
  for (i = 0; i < 9; i++)
    Board[i] = 0;
}

void DumpBoard(void)
{
  /*char *board = gGame->board;*/
  
    /* For each row */
  int i;
  for (i = 0; i < 9; i++)
  {
    int ch = Board[i];
    if ( !ch )
      printf("%2c", EMPTY_SQUARE);
    else
      printf("%2c", ch);
    
    if (((i + 1) % 3) == 0)
      printf("\n");
  }
  printf("\n");
}

static void AddOutput(const char* str, int newline)
{ 
  printf("%s", str);
  if (newline)
    printf("\n");
  
  fflush(stdout);
}

/*
  Creates the game and adds a player as Player 1
*/
static int driver_CreateGame(void)
{
  struct Player *player = NULL;
  
  if (gVerbose)
    AddOutput("Creating game...", 1);
  
  gGame = (struct Game *)CreateGame();
  if (!gGame)
  {
    AddOutput("Error: Can't create game.", 1);
    return 0;
  }
  
  player = (struct Player *)malloc(sizeof (struct Player));
  if (AddPlayer(gGame, player))
  {
    AddOutput("Error: Can't add player 1.", 1);
    DestroyGame(gGame);
    free(player);
    return 0;
  }
  
  gPlayers[0] = player;
  return 1;
}

/*
  Joining the game simply adds a second player to the game.
*/
static int driver_JoinGame(void)
{
  struct Player *player;
  if (gVerbose)
  {
    AddOutput("Joining game with player 2", 1);
  }
  
  if (!gGame)
  {
    AddOutput("Error: Can't join a game: No game created.", 1);
    return 0;
  }

  player = (struct Player *)malloc(sizeof(struct Player));
  if (AddPlayer(gGame, player))
  {
    AddOutput("Error: Can't add player 2.", 1);
    free(player);
    return 0;
  }
  
  gPlayers[1] = player;
  return 1;
}

/*
  After the game is created and both players (TicTacToe) are added, 
  the game can be started. The server will do this automatically after
  bot players are in the game and bot players have sent the "Ready"
  message.
*/
static int driver_StartGame(void)
{
  int count;
  
  if (gVerbose)
    AddOutput("Starting game...", 1);
  
  if (!gGame)
  {
    AddOutput("Error: Can't start a game: No game created.", 1);
    return 0;
  }

    /* Sanity check */
  count = PlayerCount(gGame);
  if (count != 2)
  {
    printf("count = %i\n", count);
    AddOutput("Can't start game: requires 2 players.", 1); 
    return 0;
  }
    /* 
      This should always succeed because the only check the plugin makes
      is to see that there are 2 players in the game. We just checked that
      case above.
     */
  if (StartGame(gGame))
  {
    AddOutput("Error: Can't start game.", 1);
    return 0;
  }
  
  return 1;
}

/*
  Clean up the game.
*/
static void driver_DestroyGame(void)
{
  if (gVerbose)
    AddOutput("Destroying game...", 1);

  DestroyGame(gGame);
  free(gPlayers[0]);
  free(gPlayers[1]);
}

/*
  Wrapper function for getting a game going.
*/
static int NewGame(void)
{
  if (!driver_CreateGame())
      return 0;
  
  if (!driver_JoinGame())
      return 0;
  
  if (!driver_StartGame())
      return 0;
  
  if (gVerbose)
  {
    int count = PlayerCount(gGame);
    printf("Player count: %i\n", count);
  }
  
  ClearBoard();

  return 1;
}

/*
  Deals with any command line parameters our driver wants to accept.
  Your driver can do anything here.
*/
static int init(int argc, char **argv)
{
  if (argc < 2)
  {
    printf("Usage: %s {count}\n", argv[0]);
    printf("\n");
    printf("where: count is the number of games to play.\n");
    return 0;
  }
  gCount = atoi(argv[1]);
  
  if (gVerbose)
  {
    printf("Plugin information\n");
    printf("----------------------------\n");
    printf("       Name: %s\n", Name());
    printf("     Author: %s\n", Author());
    printf("       Type: %s\n", Type());
    printf("    Players: %i\n", NumberOfPlayers());
    printf("    Version: %s\n", Version());
    printf("API Version: %s\n", APIVersion());
    printf("----------------------------\n");
  }
  
  return 1;
}

/*
  One of the goals of this test client is to accumulate lots of statistics
  about the game and display them after running many games.
*/
static void CalcGameOverStats(int movenum)
{
  int winner = GetPlayerNumber(gGame, GetNextPlayer(gGame));
  char buffer[100];
  
  if (winner == 0)
  {
    sprintf(buffer, "The game is a draw!");
  }
  else
    sprintf(buffer, "Game over: Player %i is the winner after %i moves!", winner, movenum);
  
  if (gVerbose) 
    AddOutput(buffer, 1);
  
  if (winner == 0)
    gDraws++;
  else if (winner == gPlayer)
  {
    gWins[gPlayer]++;
    gLosses[gOther]++;
  }
  else
  {
    gWins[gOther]++;
    gLosses[gPlayer]++;
  }
  
  if (winner == 1)
    gWinsMovesX[movenum]++;
  else if (winner == 2)
    gWinsMovesO[movenum]++;
  
  if (!gSilent)
  {           
    printf("Wins - Player 1:%i, Player 2:%i, Draws: %i, Games Played:%i, Games Left:%i\n",
            gWins[1], gWins[2], gDraws, gGamesPlayed, gCount - gGamesPlayed);
  }
}

static void DisplayGrandTotals(void)
{
  int i;
  
  printf("Grand Total:\n");
  printf("Wins - Player 1:%i(%.2f%%), Player 2:%i(%.2f%%), Draws: %i(%.2f%%), Games Played:%i, Games Left:%i\n",
          gWins[1], (double)gWins[1] / gGamesPlayed * 100, 
          gWins[2], (double)gWins[2] / gGamesPlayed * 100,
          gDraws, (double)gDraws / gGamesPlayed * 100,
          gGamesPlayed, gCount - gGamesPlayed);
          
  printf("Player 1 won this many games on move number: ");
  for (i = 5; i <= 9; i++)
    printf("%i:%i   ", i, gWinsMovesX[i]);
  printf("\n"); 
    
  printf("Player 2 won this many games on move number: ");
  for (i = 5; i <= 9; i++)
    printf("%i:%i   ", i, gWinsMovesO[i]);
  printf("\n"); 
}

/*
  This is the loop where the games are "played". 
*/
int main(int argc, char **argv)
{
  int i;
  
  if (!init(argc, argv))
    return 0;

    /* Loop once for each game */
  for (i = 0; i < gCount; i++)
  {
    int i, movenum = 0;

      /* Set up a new game each time */
    NewGame(); 
    
      /* Player 1 always goes first (X's) */
    gPlayer = 1;
    
      /* There are at most 9 moves in 3x3 Tic-Tac-Toe */
    for (i = 0; i < 9; i++)
    {
      int player;
      char buf[10];
      
        /* GetNextMove() call will return the current player's (i.e. gPlayer) next move.  */
        /* The "intelligence" of this function determines the intelligence of the client. */
      int move = GetNextMove();
      
        /* Set the move to the current player's symbol (i.e. 'X' or 'O') */     
      Board[move] = SYMBOLS[gPlayer];
      movenum++;

        /* Call the plugin to verify that the move is valid */      
      sprintf(buf, "%i", move);
      ValidateMove(gGame, GetPlayer(gGame, gPlayer), buf);

        /* Display board on the screen if we're debugging */
      if (gVerbose)
        DumpBoard();
      
        /* Game ended with a winner or a draw */
      if (GetGameStatus(gGame) == PLUGIN_GAME_STATUS_OVER_WINNER || GetGameStatus(gGame) == PLUGIN_GAME_STATUS_OVER_DRAW)
      {
          /* Calculate and print statistics */
        CalcGameOverStats(movenum);
        if (gDrawBoards)
          DumpBoard();
        /*
        if (GetGameStatus(gGame) == PLUGIN_GAME_STATUS_OVER_WINNER && gPlayer == 2)
        {
          DumpBoard();  
          return 0;
        }
        */
          
        break;
      }
      
        /* Toggle next player */
      player = GetPlayerNumber(gGame, GetPlayer(gGame, gPlayer));
      gPlayer = (player == 1) ? 2 : 1;
      gOther = (gPlayer == 1) ? 2 : 1; 
    }
    gGamesPlayed++;
    driver_DestroyGame();
  }
  
  DisplayGrandTotals();
  return 0;
}