////////////////////////////////////////////////////////////////////
// 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;
}