 //////////////////////////////////////////////////////////////////////////////
//
// File: game.cc
//
// Purpose: This is the implementation for the Game class.
//
// Authors:
//   txe  Travis Emmitt
//
// Modifications:
//   14-APR-1998  txe  Initial creation
//   15-APR-1998  txe  Added SetDebug(), comments, alphabetized
//   16-APR-1998  txe  Added NeuralPlayer, input files
//   19-APR-1998  txe  Added multiple matches, SchoolRun
//   20-APR-1998  txe  Ported to UNIX, debugged schooling
//   21-APR-1998  txe  Moved school related stuff to School class
//   22-APR-1998  txe  Added wts_file to LoadPlayers()
//   23-APR-1998  txe  Using static debug, changed constructor
//   24-APR-1998  txe  Improved interface
//
//////////////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "board.h"
#include "player.h"
#include "referee.h"
#include "hplayer.h"
#include "nplayer.h"
#include "tplayer.h"
#include "game.h"

/////////////////////////////////////////////////////////////////////////

Game::Game  (char *name, char *players_file, char *rules_file)
    : Debug (name) {

  ref = NULL;

  if (!LoadRules (rules_file)) {
    ERR << "couldn't load rules, program terminated\n";
    exit (-1);
  }

  if (!LoadPlayers (players_file)) {
    ERR << "couldn't load players, program terminated\n";
    exit (-1);
  }

  ASSERT (ref != NULL);

  ref->ShowPlayers (players);
  ref->ShowRules   (board, win_boards);
}

/////////////////////////////////////////////////////////////////////////

int Game::LoadPlayers (char *filename) {
  ASSERT (filename != NULL);

  DEBUG(1) << "\nLoading player definitions from '" << filename << "'...\n";

  FILE *fp;
  char line[LINE_LEN+1], type[MAX_LEN+1], abil[MAX_LEN+1], temp[NAME_LEN+1];
  char arch_file[MAX_LEN+1] = "", wts_file[MAX_LEN+1] = "";
  int i, ability;

  if ((fp = fopen (filename, "rt")) == NULL) {
    cout << "Error opening players file '" << filename << "'\n";
    return 0;
  }

 // get number of players //

  SKIP_COMMENTS (fp, line);
  sscanf (line, "%d", &num_players);

 // get each player's type and ability //

  if ((players[0] = new Referee ("Referee")) == NULL) {
    ERR << name << " couldn't create players[0]; out of memory!\n";
    exit (-1);
  }
  ref = (Referee *) players[0];

  for (i = 1; i <= num_players; i++) {
    SKIP_COMMENTS (fp, line);
    sscanf (line, "%s %s %s %s", type, abil, arch_file, wts_file);

    switch (type[0]) {
    case 'h' : case 'H':
      sprintf (temp, "Human_%d", i);
      players[i] = new HumanPlayer (temp, i);
      break;
      
    case 'n' : case 'N':
      sprintf (temp, "Neural_%d", i);
      players[i] = new NeuralPlayer (temp, i, arch_file, wts_file);
      break;
      
    case 't' : case 'T':
      switch (abil[0]) {
      case 'i' : case 'I' : case '0' : ability = IDIOT;   break;
      case 'r' : case 'R' : case '1' : ability = ROOKIE;  break;
      case 'a' : case 'A' : case '2' : ability = AVERAGE; break;
      case 's' : case 'S' : case '3' : ability = SKILLED; break;
      case 'e' : case 'E' : case '4' : ability = EXPERT;  break;
      case 'p' : case 'P' : case '5' : ability = PERFECT; break;
      default :
	cout << "Error reading '" << filename << "' line: " << line;
	cout << "Invalid player ability level '" << abil << "'\n";
	return 0;
      }
      sprintf (temp, "Trad_%s_%d", ability_string[ability], i);
      players[i] = new TradPlayer (temp, i, ability);
      break;
      
    default :
      cout << "Invalid player type '" << type << "'\n";
      fclose (fp);
      return 0;
    }

    if (players[i] == NULL) {
      ERR << name << " couldn't create players[" << i << "]; out of memory\n";
      fclose (fp);
      return 0;
    }
  }

  DEBUG(3) << "\n";
  fclose (fp);

  players[num_players+1] = NULL;
  return num_players;
}

/////////////////////////////////////////////////////////////////////////

int Game::LoadRules (char *filename) {
  ASSERT (filename != NULL);

  DEBUG(1) << "\nLoading rules definitions from '" << filename << "'...\n";

  FILE *fp;
  char line[LINE_LEN+1], temp[NAME_LEN+1], *ptr;
  int size_x, size_y, x, y, i, color;

  if ((fp = fopen (filename, "rt")) == NULL) {
    cout << "Error opening rules file '" << filename << "'\n";
    return 0;
  }

 // get variant name //

  SKIP_COMMENTS (fp, line);
  sscanf (line, "%s", temp);

 // get board size //

  SKIP_COMMENTS (fp, line);
  sscanf (line, "%d %d", &size_x, &size_y);

  if ((board = new Board (temp, size_x, size_y)) == NULL) {
    ERR << name << " couldn't create board; out of memory!\n";
    exit (-1);
  }

 // get board configuration //

  for (y = 0; y < size_y; y++) {
    SKIP_COMMENTS (fp, line);
    ptr = line;
    for (x = 0; x < size_x; x++) {
      sscanf (ptr, "%d", &color);
      board->SetColor (x, y, color);
      while (WHITESPACE (*ptr)) ptr++;
      while (BLACKSPACE (*ptr)) ptr++;
    }
  }
  board->Print (3);

 // get number of win boards //

  SKIP_COMMENTS (fp, line);
  sscanf (line, "%d", &num_win_boards);

 // load each win board's size and state //

  for (i = 0; i < num_win_boards; i++) {
    SKIP_COMMENTS (fp, line);
    sscanf (line, "%d %d", &size_x, &size_y);
    sprintf (temp, "Win Board %d", i);
    
    if ((win_boards[i] = new Board (temp, size_x, size_y)) == NULL) {
      ERR << name << " couldn't create win_boards[" << i
	  << "]; out of memory!\n";
      exit (-1);
    }
    
    for (y = 0; y < size_y; y++) {
      SKIP_COMMENTS (fp, line);
      ptr = line;
      for (x = 0; x < size_x; x++) {
	sscanf (ptr, "%d", &color);
	win_boards[i]->SetColor (x, y, color);
	while (WHITESPACE (*ptr)) ptr++;
	while (BLACKSPACE (*ptr)) ptr++;
      }
    }
    win_boards[i]->Print (3);
  }

  fclose (fp);
  DEBUG(3) << "\n";

  win_boards[num_win_boards] = NULL;
  return num_win_boards;
}

/////////////////////////////////////////////////////////////////////////

void Game::PrintWins () {
  cout << "\n*** Wins:  Draws (" << ref->GetNumWins() << ")";
  for (int i = 1; i <= num_players; i++) {
    ASSERT (players[i] != NULL);
    cout << "\t" << players[i]->GetName() << " ("
	 << players[i]->GetNumWins() << ")";
  }
  cout << "\t***\n";
}

/////////////////////////////////////////////////////////////////////////

void Game::PrintWins (int level) {
  if (debug >= level) {
    PrintWins ();
  }
}

/////////////////////////////////////////////////////////////////////////

int Game::Run (int num_matches, int num_games) {
  DEBUG(0) << "\n\n" << name << " running " << num_matches << " matches of "
	   << num_games << " games each\n\n";

  for (int i = 1; i <= num_matches; i++) {
    DEBUG(0) << "\nPlaying match " << i << " (" << num_games << " games)\n";
    
    ref->NewMatch    ();
    ref->ResetScores ();
    
    for (int j = 0; j < num_games; j++) {
      if (ref->NewGame() == QUIT) {
	DEBUG(0) << name << " quitting...\n";
        i = num_matches;
	break;
      }
      PrintWins (1);
    }
    
    DEBUG(0) << "\nFinal Score for match #" << i << " : ";
    PrintWins (0);
  }

  cout << "\n\nSuccess\n";
  return 1;
}

/////////////////////////////////////////////////////////////////////////

