/*! 
*******************************************************************************
  \file    random-ttt.c
  \author  Matthew Mead
  \par     email: mmead\@digipen.edu
  \date    01/16/2010
  \brief   Test driver for the TicTacToe plugin.
  
  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.
  
  To run this program from the command line:
  
      ./random-ttt {count}

  where count is the number of games to play. For example:
    
      ./random-ttt 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.
  
  Currently, this program always generates the same set of random numbers each
  time it is run. To enable randomness between runs, uncomment the call to 
  srand() in the main() function at the bottom of this file.
*******************************************************************************/
#include <stdio.h>  /* printf, sprintf, fflush */
#include <stdlib.h> /* rand, srand, atoi       */
#include <time.h>   /* time                    */

#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 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 */
static char Board[9] = {0};

/* generate random numbers between low and high */
static int RandomInt(int low, int high)
{
  int number;
  number = rand() % (high - low + 1) + low;
  return number;
}


/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*
  If you wanted to make this test client intelligent, this is likely the only 
  function you'd have to change. You'd also have to know  which player is 
  moving so you could pick the best next move based on which player is making
  the move. The player that is currently moving is in the variable gPlayer.
  
  Currently, this function simply picks a random move
*/
static int GetNextMove(void)
{
  int position;
  
  /* Keep choosing a random square until a valid (e.g. empty) square is found */
  do 
  {
    position = RandomInt(0, 8);
  }while (Board[position] != 0);
  
  return position;
}
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/



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

static 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 = 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 = 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, Player 2:%i, Draws: %i, Games Played:%i, Games Left:%i\n",
          gWins[1], gWins[2], gDraws, 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;

    /* Uncomment the call to srand to have randomness every time the program is run. */ 
    /* It's easier to debug a program that is repeatable each time. Once the         */
    /* program is correct, enable the randomness.                                    */
  /* srand(time(NULL)); */
  
    /* 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);
        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;
}