////////////////////////////////////////////////////////////////////
// The Jewel Game                                                 //
// by Nick Cash                                                   //
//                                                                //
// Fall 2007                                                      //
// 810:161 - Artificial Intelligence                              //
//                                                                //
// game.cpp - Contains class implementations and worker functions //
////////////////////////////////////////////////////////////////////

// standard includes
#include <cstdlib>
#include <queue>
#include <stack>
#include <map>
#include <list>
#include <iostream>

// custom includes
#include "game.h"

// setup the namespace so we dont have "std::" everywhere
using namespace std;

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

// Constructor
// Jewel class functions
cJewel::cJewel()
{
  type = '\0'; // set to NULL by default              
}

// Constructor
// Make the jewel with a type given
cJewel::cJewel(char c)
{
  if ( c != 'D' && c != 'R' && c != 'E' )
    Error("\n\n***Error*** Jewel::Jewel(char) -> character input not a vaild type.\n\n");

  type = c;
}

// Destructor
cJewel::~cJewel()
{
}

// Set the type
inline void cJewel::SetType(char c)
{
  if ( c != 'D' && c != 'R' && c != 'E' )
    Error("\n\n***Error*** Jewel::SetType(char) -> character input not a vaild type.\n\n");

  type = c;       
}


// Update the jewel type with the preincrement operator;
// Diamond -> Ruby -> Emerald
const cJewel& cJewel::operator++()
{
  switch ( type )
  {
     case 'D':
              type = 'R';
              break;
         
     case 'R':
              type = 'E';
              break;
              
     case 'E':
              type = 'D';
              break;
         
     default:
              Error("\n\n***Error*** Jewel::operator++ -> default case reached.\n\n");
              break;
  }
  
  return *this;
}

// End Jewel class functions

// GameBoard class functions

// constructor
cGameBoard::cGameBoard()
{
 // default data
 spell = 0;
 depth = 1;

 // up our node storing counter
 ++nodes_stored;
}

//constructor
// Initialize via string
cGameBoard::cGameBoard(std::string s)
{
   // verify our data     
   if ( s.length() != 9 )
     Error("\n\n***Error*** cGameBoard::cGameBoard(string) -> s.length() != 9\n\n");
    
   // check string for bad letters
   for ( int i = 0; i < s.length(); i++ )
   {
    char c = toupper(s[i]); // capitalize our letter
    
    if ( c != 'D' && c != 'R' && c != 'E' )
     Error("\n\n***Error*** cGameBoard::cGameBoard(string) -> bad letter in string\n\n");
    
    // if good, save the capitalized version
    s[i] = c;
   }
   
   // add the data
   {
     int str_i = 0;

     // we can assume length 9 now
     for ( int row_i = 0; row_i < 3; row_i++ )
     {
        for ( int col_i = 0; col_i < 3; col_i++, str_i++ )
         board[row_i][col_i].SetType(s[str_i]);
     }
   }
   
   // set default data
   spell = 0;
   depth = 1;
   
   // up our node storing counter
   ++nodes_stored;
}

// destructor
cGameBoard::~cGameBoard()
{
   // down our node storing counter
   --nodes_stored;
}

// update jewel and adjacent jewels
// uses overridden preincrement operator for ease
void cGameBoard::UpdateJewel( int row, int col )
{
   ++board[row][col]; // update the jewel itself

   // up
   if ( row != 0 )
    ++board[row-1][col];

   // down
   if ( row != 2 )
    ++board[row+1][col];
    
   // left
   if ( col != 0 )
    ++board[row][col-1];

   // right
   if ( col != 2 )
    ++board[row][col+1];
}

// Print out the board
void cGameBoard::Print( void )
{
    cout << endl; // newline
     
    // Go through each row and column, print out the jewel type
    for ( int row_i = 0; row_i < 3; row_i++ )
    {
      for ( int col_i = 0; col_i < 3; col_i++ )
       cout << board[row_i][col_i].GetType() << ' ';
      
      cout << endl;
    }
    
    // Print out depth
    cout << endl << "Depth: " << depth << endl;
}

// Check to see if all nodes have the same type as node 1
// If so, we have a winning board
bool cGameBoard::CheckWin( void )
{
    char c = board[0][0].GetType(); // grab the type to compare to

    // check each column and row
    for ( int row_i = 0; row_i < 3; row_i++ )
    {
      for ( int col_i = 0; col_i < 3; col_i++ )
       if ( board[row_i][col_i].GetType() != c )
        return false;
    }
    
    return true;
}

// end GameBoard class functions

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

// functions

// SetupGameBoard
// - This function grabs and verifys input for the board, then sets the board
// - If a second argument is provided, it changes the search performed
// - Returns false on bad data, returns true if initialization went well
bool SetupGameBoard(cGameBoard &board, int argc, char *argv[])
{
   string s;

   // do we have any command line input?
   if (argc < 2)
    return false;

   // grab our user input from the second argument
   s = argv[1];

   // verify our data     
   if ( s.length() != 9 ) // we have too many or too few letters
     return false;
    
   // check string for bad letters
   for ( int i = 0; i < s.length(); i++ )
   {
    char c = toupper(s[i]); // capitalize our letter
    
    // compare against the three types
    if ( c != 'D' && c != 'R' && c != 'E' )
     return false;
    
    // if good, save the capitalized version
    s[i] = c;
   }
   
   // finally, intialize the board
   board = s;
   
   // check to see if we change our search function
   if ( argc > 2 )
   {
    int x = atoi(argv[2]);
    search_function = ((x == 1 || x == 2) ? x : 0); // if x = 1 or 2, set to x, otherwise default to 0
   }

   return true;
}

// Send error input here
void Error(const char* str)
{
   cout << str; // print error message
   cin.get();   // add a pause
   abort();     // bail out of program
}

// Generate all 9 variations of the parent tree and add them to the list
// Use font variable to tell if we add to the front or back of the list
void GenerateBranches( const cGameBoard& parent, std::list<cGameBoard*>& lst, bool front )
{
  cGameBoard* ptr = NULL;
  int spell_counter = 1;

  // we are exapnding another node
  nodes_expanded++;

  // Generate 9 new board, same as the parent just with 1 altered jewel
  for ( int row_i = 0; row_i < 3; row_i++ )
  {
    for ( int col_i = 0; col_i < 3; col_i++ )
    {
      ptr = new cGameBoard;
      *ptr = parent; // copy parent data

      // set this game boards data
      ptr->SetDepth(parent.GetDepth()+1);
      ptr->SetSpell(spell_counter++);
      ptr->UpdateJewel(row_i,col_i); // use the counters to update the jewel in our array

      // add to the list
      if ( front == true )
        lst.push_front(ptr);
      else
        lst.push_back(ptr);
    }
  }
}

// Cosmetic function to make spell list more readable
// Adds spaces between every other character
string AddSpaces( const string& str )
{
  string ret;
  
  for ( int i = 0; i < str.length(); i++ )
  {
     ret += str[i];
     ret += ' ';
  }
  
  return ret;
}