
/***************************************************************************

                                 ChessV

                   COPYRIGHT (C) 2005 BY GREGORY STRONG

This file is part of ChessV.  ChessV is free software; you can redistribute
it and/or modify it under the terms of the GNU General Public License as 
published by the Free Software Foundation; either version 2 of the License, 
or (at your option) any later version.

ChessV is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
more details; the file 'COPYING' contains the License text, but if for
some reason you need a copy, please write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

****************************************************************************/


#include "StdAfx.h"
#include "../../ChessV.h"
#include "../../PieceType.h"
#include "../../Piece.h"
#include "../../Direction.h"
#include "../../Rand.h"
#include "../../GameParameters.h"
#include "UnicornChessGame.h"
#include "Decimal_Types.h"

#include "Decimal_Data.h"
//	this include file contains data matricies for square bonuses,
//	outpost bonuses, etc.  it also contains some macros used here.


//	instances of the PieceTypes
UnicornChessPawn UnicornChessPawn::unicornChessPawn;
DecimalUnicorn DecimalUnicorn::decimalUnicorn;
DecimalLion DecimalLion::decimalLion;


void StartUnicornChessGame()
{
	if( !justLoaded )
		int rtn = (int) ::DialogBox( theInstance, MAKEINTRESOURCE(IDD_UNICORN_CHESS_SELECT_DIALOG), NULL, 
			reinterpret_cast<DLGPROC>(UnicornChess_SelectDlgProc) );

	theBoard = new Board( 10, 10 );
	CreatePlayers( "White", "Black", whiteComp, blackComp );
}

Game *CreateUnicornChessGame( Board &brd, Player &plr0, Player &plr1 )
{
	return new UnicornChessGame( brd, plr0, plr1 );
}

UnicornChessGame::UnicornChessGame( Board &board, Player &whitePlayer, Player &blackPlayer ):
	Game(board, whitePlayer, blackPlayer),
	enPassant(true)
{
	// ***************************************** //
	// ***                                   *** //
	// ***  set rules and evaluation values  *** //
	// ***                                   *** //
	// ***************************************** //

	//	implement 50-move draw rule (100 half-moves)
	autoDrawPeriod = 100;

	//	turn on use of pawn structure evaluation
	usePawnStructureEvaluation = true;

	//	turn off null-move after 24 pieces have been captured
	endgameCaptureThreshold = 24;

	//	razoring and futility pruning margins
	razorMargin = 4000;
	futilityMargin = 2000;
	extendedFutilityMargin = 2500;

	//	don't promote by replacement in Unicorn Grand Chess
	GrandChessPawn::grandChessPawn.GetPromotion().SetPromotionCapability( PromoteToVariableTypes );

	// *** PHASES *** //

	//	we have to set up the phases we wish to have in this game;
	//	we will keep it simple and have 3: opening, midgame, and endgame
	nPhases = 3;

	//	settings for phase 0 (opening)
	phases[0].SetNumber( 0 );
	phases[0].SetMobilityFactor( 0 );
	phases[0].SetPawnDeficiencyFactor( 8 );
	phases[0].SetSquareValuesFactor( 1 );
	phases[0].SetKingSafetyFactor( 6 );
	phases[0].SetTropismFactor( 6 );

	//	settings for phase 1 (midgame)
	phases[1].SetNumber( 1 );
	phases[1].SetMobilityFactor( 0 );
	phases[1].SetPawnDeficiencyFactor( 10 );
	phases[1].SetSquareValuesFactor( 1 );
	phases[1].SetKingSafetyFactor( 8 );
	phases[1].SetTropismFactor( 10 );
	
	//	settings for phase 2 (endgame)
	phases[2].SetNumber( 2 );
	phases[2].SetMobilityFactor( 0 );
	phases[2].SetPawnDeficiencyFactor( 16 );
	phases[2].SetSquareValuesFactor( 1 );
	phases[2].SetKingSafetyFactor( 0 );
	phases[2].SetTropismFactor( 10 );

	// *** OUTPOSTS *** //

	//	we wish to be able to give bonuses to pieces that are 'posted'-
	//	that is, which are centrally located and cannot be reached by
	//	enemy pawns (because adjacent pawns are gone, or have advanced
	//	beyond the piece).  we also wish to be able to give different
	//	bonuses for different types of minor pieces (a posted Knight is
	//	worth more than a posted Bishop.)

	//	we will use the user variable 'gameInt1' in the PieceType class
	//	to store the 'outpost factor' for a given piece.  this value will
	//	be multiplied by the square-values stored in the 'outpost' array.
	DecimalKnight::decimalKnight.gameInt1 = 10;
	DecimalLion::decimalLion.gameInt1 = 9;
	DecimalBishop::decimalBishop.gameInt1 = 7;


	// *** INITIALIZATION *** //

	//	initialize hashes
	enPassantHashMap = new HashMap( board.GetNumberOfSquares() );
	castlingHash0k = rand_32();
	castlingHash0q = rand_32();
	castlingHash1k = rand_32();
	castlingHash1q = rand_32();

	//	basic initialization
	board.Initialize( this, BITBOARD_128 );
}

void UnicornChessGame::AddPlayerPieceTypes()
{
	//	add the piece types that each player may have
	board.AddPlayerPieceTypeBothPlayers( DecimalRook::decimalRook );
	board.AddPlayerPieceTypeBothPlayers( DecimalBishop::decimalBishop );
	board.AddPlayerPieceTypeBothPlayers( DecimalKnight::decimalKnight );
	board.AddPlayerPieceTypeBothPlayers( DecimalQueen::decimalQueen );
	board.AddPlayerPieceTypeBothPlayers( DecimalKing::decimalKing );
	board.AddPlayerPieceTypeBothPlayers( DecimalUnicorn::decimalUnicorn );
	board.AddPlayerPieceTypeBothPlayers( DecimalChancellor::decimalChancellor );

	//	pawn types vary, depending on the variant being played
	if( !strcmp( selectedVariant->name, "Unicorn Chess" ) )
	{
		board.AddPlayerPieceTypeBothPlayers( UnicornChessPawn::unicornChessPawn );
	}
	else if( !strcmp( selectedVariant->name, "Unicorn Great Chess" ) )
	{
		board.AddPlayerPieceTypeBothPlayers( UnicornChessPawn::unicornChessPawn );
		board.AddPlayerPieceTypeBothPlayers( DecimalLion::decimalLion );
	}
	else if( !strcmp( selectedVariant->name, "Unicorn Grand Chess" ) )
	{
		board.AddPlayerPieceTypeBothPlayers( GrandChessPawn::grandChessPawn );
		board.AddPlayerPieceTypeBothPlayers( DecimalLion::decimalLion );
	}
}

void UnicornChessGame::InitializeBoard()
{
	//	add the piece types that each player may have
	AddPlayerPieceTypes();

	//	clear out all piece references
	for( int player = 0; player < 2; player++ )
	{
		queens[player] = NULL;
		for( int x = 0; x < 6; x++ )
		{
			minorPieces[player][x] = NULL;
			if( x < 5 )
			{
				majorPieces[player][x] = NULL;
				if( x < 2 )
				{
					rooks[player][x] = NULL;
					knights[player][x] = NULL;
					bishops[player][x] = NULL;
					lions[player][x] = NULL;
					unicorns[player][x] = NULL;
					chancellors[player][x] = NULL;
				}
			}
		}
	}
	for( int i = 0; i < 10; i++ )
	{
		pawns[0][i] = NULL;
		pawns[1][i] = NULL;
	}

	//	FEN notation of position setup
	char *FEN = NULL;

	//	text filename of opening book
	char *book = NULL;

	// *** UNICORN CHESS *** //
	if( !strcmp( selectedVariant->name, "Unicorn Chess" ) )
	{
		FEN = "crubqkburc/ppppnnpppp/4pp4/10/10/10/10/4PP4/PPPPNNPP/CRUBQKBURC";
		book = "openings\\UnicornChess.txt";
		variant = 0;
	}
	else if( !strcmp( selectedVariant->name, "Unicorn Great Chess" ) )
	{
		FEN = "crnbukbnrq/ppppllpppp/4pp4/10/10/10/10/4PP4/PPPPLLPPPP/CRNBUKBNRQ";
		book = "openings\\UnicornGreatChess.txt";
		variant = 1;
	}
	else if( !strcmp( selectedVariant->name, "Unicorn Grand Chess" ) )
	{
		FEN = "r8r/cnlbukblnq/pppppppppp/10/10/10/10/PPPPPPPPPP/CNLBUKBLNQ/R8R";
		book = "openings\\UnicornGrandChess.txt";
		variant = 2;
	}

	//	place pieces
	if( LookupStringParameter( "array", FEN ) )
		useOpeningBook = false;
	board.PlacePiecesByFEN( FEN );

	// *** OPENING BOOK *** //
	if( useOpeningBook )
	{
		//	select the name of the opening book for 
		//	this particular combination of armies
		Book *pBook = new Book( &board );
		board.SetOpeningBook( pBook, book );
	}

	LookupBoolParameter( "en-passant", enPassant );

	board.PostInitialize();
}

Piece *UnicornChessGame::AddPiece
	( PieceType &pieceType,
	  int nPlayer,
	  int nRank,
	  int nFile )
{
	Piece *newPiece = Game::AddPiece( pieceType, nPlayer, nRank, nFile );

	if( pieceType == DecimalQueen::decimalQueen )
	{
		queens[nPlayer] = newPiece;
		AddPieceToSet( newPiece, majorPieces, 5 );
	}
	else if( pieceType == DecimalRook::decimalRook )
	{
		AddPieceToSet( newPiece, rooks, 2 );
	}
	else if( pieceType == DecimalChancellor::decimalChancellor )
	{
		AddPieceToSet( newPiece, chancellors, 2 );
		AddPieceToSet( newPiece, majorPieces, 5 );
	}
	else if( pieceType == DecimalUnicorn::decimalUnicorn )
	{
		AddPieceToSet( newPiece, unicorns, 2 );
		AddPieceToSet( newPiece, majorPieces, 5 );
	}
	else if( pieceType == DecimalBishop::decimalBishop )
	{
		AddPieceToSet( newPiece, bishops, 2 );
		AddPieceToSet( newPiece, minorPieces, 6 );
	}
	else if( pieceType == DecimalKnight::decimalKnight )
	{
		AddPieceToSet( newPiece, knights, 2 );
		AddPieceToSet( newPiece, minorPieces, 6 );
	}
	else if( pieceType == DecimalLion::decimalLion )
	{
		AddPieceToSet( newPiece, lions, 2 );
		AddPieceToSet( newPiece, minorPieces, 6 );
		AddPieceToSet( newPiece, majorPieces, 5 );
	}
	else if( pieceType.IsPawn() )
	{
		AddPieceToSet( newPiece, pawns, 10 );
		if( newPiece->GetPlayerNumber() == 0 )
			newPiece->GetPromotionZone().AddToZone( 90, 99 );
		else
			newPiece->GetPromotionZone().AddToZone( 0, 9 );
	}
	return newPiece;
}

void UnicornChessGame::DeletePiece
	( Piece *piece )
{
	if( queens[0] == piece )
		queens[0] = NULL;
	if( queens[1] == piece )
		queens[1] = NULL;
	RemovePieceFromSet( piece, rooks, 2 );
	RemovePieceFromSet( piece, knights, 2 );
	RemovePieceFromSet( piece, bishops, 2 );
	RemovePieceFromSet( piece, lions, 2 );
	RemovePieceFromSet( piece, unicorns, 2 );
	RemovePieceFromSet( piece, chancellors, 2 );
	RemovePieceFromSet( piece, minorPieces, 6 );
	RemovePieceFromSet( piece, majorPieces, 5 );
	RemovePieceFromSet( piece, pawns, 10 );
}

PieceType **UnicornChessGame::GetPieceTypesRequired
	( int &nPieceTypes )
{
	nPieceTypes = board.GetPieceTypeCount();
	return( board.GetPieceTypes() );
}

void UnicornChessGame::AddSpecialMoves
	( int currentPlayer, 
	  MovementList &stack, 
	  GameRec &gameRecord, 
	  bool quiescentSearch )
{
	//	check for en-passant
	if( enPassant )
		Game::AddEnPassantMoves( stack, gameRecord, quiescentSearch );

	//	see if we can castle
	if( !quiescentSearch && variant != 2 && !board.FastCheckTest() && 
		board.GetCurrentPlayerNumber() == 0 )
	{
		//	white to move
		Piece *king = board.GetKing( 0 );
		if( !king->HasMoved() && !board.FastCheckTest() )
		{
			//	king side
			if( rooks[0][1] != NULL &&
				!rooks[0][1]->IsCaptured() &&
				!rooks[0][1]->HasMoved() )
			{
				if( board.GetSquareContents( 6 ) == NULL &&
					board.GetSquareContents( 7 ) == NULL )
				{
					//	squares are vacant; make sure they are not attacked
					if( 
						!IsSquareAttacked( 6, 1 ) && 
						!IsSquareAttacked( 7, 1 ) )
					{
						stack.BeginMoveAdd( Castling, 5, 7 );
						stack.AddPickUp( 5 );
						stack.AddPickUp( 8 );
						stack.AddDrop( king, 7 );
						stack.AddDrop( rooks[0][1], 6 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
			//	queen side
			if( rooks[0][0] != NULL &&
				!rooks[0][0]->IsCaptured() &&
				!rooks[0][0]->HasMoved() )
			{
				if( board.GetSquareContents( 4 ) == NULL &&
					board.GetSquareContents( 3 ) == NULL && 
					board.GetSquareContents( 2 ) == NULL )
				{
					//	square are vacant; make sure they are not attacked
					if( !IsSquareAttacked( 4, 1 ) && 
						!IsSquareAttacked( 3, 1 ) )
					{
						stack.BeginMoveAdd( Castling, 5, 3 );
						stack.AddPickUp( 5 );
						stack.AddPickUp( 1 );
						stack.AddDrop( king, 3 );
						stack.AddDrop( rooks[0][0], 4 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
		}
	}
	else if( !quiescentSearch && variant != 2 && !board.FastCheckTest() && 
		     board.GetCurrentPlayerNumber() == 1 )
	{
		//	black to move
		Piece *king = board.GetKing( 1 );
		if( !king->HasMoved() )
		{
			//	king side
			if( rooks[1][1] != NULL &&
				!rooks[1][1]->IsCaptured() &&
				!rooks[1][1]->HasMoved() )
			{
				if( board.GetSquareContents( 96 ) == NULL &&
					board.GetSquareContents( 97 ) == NULL )
				{
					//	square are vacant; make sure they are not attacked
					if( !IsSquareAttacked( 96, 0 ) && 
						!IsSquareAttacked( 97, 0 ) )
					{
						stack.BeginMoveAdd( Castling, 95, 97 );
						stack.AddPickUp( 95 );
						stack.AddPickUp( 98 );
						stack.AddDrop( king, 97 );
						stack.AddDrop( rooks[1][1], 96 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
			//	queen side
			if( rooks[1][0] != NULL &&
				!rooks[1][0]->IsCaptured() &&
				!rooks[1][0]->HasMoved() )
			{
				if( board.GetSquareContents( 94 ) == NULL &&
					board.GetSquareContents( 93 ) == NULL && 
					board.GetSquareContents( 92 ) == NULL )
				{
					//	squares are vacant; make sure they are not attacked
					if( !IsSquareAttacked( 94, 0 ) && 
						!IsSquareAttacked( 93, 0 ) )
					{
						stack.BeginMoveAdd( Castling, 95, 93 );
						stack.AddPickUp( 95 );
						stack.AddPickUp( 91 );
						stack.AddDrop( king, 93 );
						stack.AddDrop( rooks[1][0], 94 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
		}
	}
}

bool UnicornChessGame::MoveBeingMade
	( MoveInfo &moveInfo,
	  GameRec &gameRecord )
{
	// *** UPDATE gameInt1 *** //

	//	gameInt1 is a 'user' field in the game records that a game can use
	//	for any purpose.  we will use it to track the forward progress of 
	//	the game, so we can determine which Phase we are in ('opening', 
	//	'midgame', or 'endgame' in our case)

	if( gameRecord.gameInt1 < 20 )
	{
		//	we are in the opening until gameInt1 reaches 20
		//	in the opening, we increment gameInt1 for the following moves:
		//		- any capture
		//		- the first move of any piece
		//		- any pawn push to rank 5+ (code value of 4+)
		//		- in the special case of castling, add 3 to gameInt1
		if( moveInfo.type == Castling )
			gameRecord.gameInt1 += 3;
		else if( gameRecord.materialCaptured > 0 ||
			(gameRecord.pieceMoved->GetFlags() & (FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE)) == FLAGS_HAS_MOVED ||
			(gameRecord.pieceMoved->IsPawn() && 
				board.ranks[gameRecord.pieceMoved->GetPlayerNumber()][gameRecord.pieceMoved->GetSquareNumber()] >= 4) )
			gameRecord.gameInt1++;
	}

	return true;
}

Phase &UnicornChessGame::AdjustEvaluation
	( int &eval,
	  PawnHash *pPawnHash )
{
	Phase *currentPhase = phases;

	if( board.GetCurrentGameRecord().gameInt1 < 20 )
	{
		// *** The OPENING *** //

		//	in the opening we consider the following things:
		//		- give bonus for developing the knights
		//		- penalize the queen, unicorn(s), and lions for moving early
		//		- give a bonus for castling; give a penalty for losing the ability to castle
		//		- give a penalty for moving pieces twice, and a bigger penalty for moving 3 times
		//		- penalize the blocking of center pawns

		//	use this "factor" to scale down the adjustments to be applied here, 
		//	so that the adjustments become smaller and smaller as we leave the 
		//	opening and enter the mid-game
		int factor = MAX(board.GetCurrentGameRecord().gameInt1 - 10, 0);
		int factor6 = 640 - (factor << 6);	//   \ 
		int factor5 = 320 - (factor << 5);  //    \_  different adjustments
		int factor4 = 160 - (factor << 4);  //    /   from large to small
		int factor3 = 80 - (factor << 3);   //   / 

		if( variant != 2 )
		{
			if( board.GetKing( 0 )->GetFlags() & FLAGS_HAS_CASTLED )
				eval += factor5;
			else
			{
				if( board.GetKing( 0 )->HasMoved() || 
					(rooks[0][0]->HasMoved() &&
					rooks[0][1]->HasMoved()) )
					eval -= factor5;
			}
			if( board.GetKing( 1 )->GetFlags() & FLAGS_HAS_CASTLED )
				eval -= factor5;
			else
			{
				if( board.GetKing( 1 )->HasMoved() || 
					(rooks[1][0]->HasMoved() &&
					rooks[1][1]->HasMoved()) )
					eval += factor5;
			}
		}

		//	give a bonus for developing knights
		for( int i = 0; i < 2; i++ )
		{
			if( knights[0][i] != NULL &&
				(knights[0][i]->GetFlags() & FLAGS_HAS_MOVED_MASK) == FLAGS_HAS_MOVED )
				eval += factor3;
			if( knights[1][i] != NULL &&
				(knights[1][i]->GetFlags() & FLAGS_HAS_MOVED_MASK) == FLAGS_HAS_MOVED )
				eval -= factor3;
		}

		//	give a penalty for moving major pieces (marshall, archbishop, lions, and queen)
		for( int i = 0; i < 5; i++ )
		{
			if( majorPieces[0][i] != NULL && (majorPieces[0][i]->GetFlags() & FLAGS_HAS_MOVED) )
				eval -= factor5;
			if( majorPieces[1][i] != NULL && (majorPieces[1][i]->GetFlags() & FLAGS_HAS_MOVED) )
				eval += factor5;
		}

		//	penalize pieces for moving twice or three times
		for( int i = 0; i < board.GetNumberOfPieces( 0 ); i++ )
		{
			Piece *piece = board.GetPiece( 0, i );
			if( !piece->IsCaptured() )
			{
				if( !piece->GetType().IsRoyal() )
				{
					switch( piece->GetFlags() & FLAGS_HAS_MOVED_MASK ) 
					{
					  case FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE:
						eval -= factor5;
						break;
					  case FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE | FLAGS_HAS_MOVED_THREE_TIMES:
						eval -= factor6;
						break;
					  default:
						break;
					}
				}
			}
		}
		for( int i = 0; i < board.GetNumberOfPieces( 1 ); i++ )
		{
			Piece *piece = board.GetPiece( 1, i );
			if( !piece->IsCaptured() )
			{
				if( !piece->GetType().IsRoyal() )
				{
					switch( piece->GetFlags() & FLAGS_HAS_MOVED_MASK ) 
					{
					  case FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE:
						eval += factor5;
						break;
					  case FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE | FLAGS_HAS_MOVED_THREE_TIMES:
						eval += factor6;
						break;
					  default:
						break;
					}
				}
			}
		}

		// *** DON'T BLOCK CENTER PAWNS IN OPENING *** //
		if( board.GetSquareContents( 34 ) != NULL &&
			board.GetSquareContents( 24 ) != NULL &&
			!board.GetSquareContents( 24 )->HasMoved() )
			eval -= factor5;
		if( board.GetSquareContents( 35 ) != NULL &&
			board.GetSquareContents( 25 ) != NULL &&
			!board.GetSquareContents( 25 )->HasMoved() )
			eval -= factor5;
		if( variant != 2 )
		{
			if( board.GetSquareContents( 23 ) != NULL &&
				board.GetSquareContents( 13 ) != NULL &&
				!board.GetSquareContents( 13 )->HasMoved() )
				eval -= factor5;
			if( board.GetSquareContents( 26 ) != NULL &&
				board.GetSquareContents( 16 ) != NULL &&
				!board.GetSquareContents( 16 )->HasMoved() )
				eval -= factor5;
		}
		else
		{
			if( board.GetSquareContents( 33 ) != NULL &&
				board.GetSquareContents( 23 ) != NULL &&
				!board.GetSquareContents( 23 )->HasMoved() )
				eval -= factor5;
			if( board.GetSquareContents( 36 ) != NULL &&
				board.GetSquareContents( 26 ) != NULL &&
				!board.GetSquareContents( 26 )->HasMoved() )
				eval -= factor5;
		}

		if( board.GetSquareContents( 64 ) != NULL &&
			board.GetSquareContents( 74 ) != NULL &&
			!board.GetSquareContents( 74 )->HasMoved() )
			eval += factor5;
		if( board.GetSquareContents( 65 ) != NULL &&
			board.GetSquareContents( 75 ) != NULL &&
			!board.GetSquareContents( 75 )->HasMoved() )
			eval += factor5;
		if( variant != 2 )
		{
			if( board.GetSquareContents( 73 ) != NULL &&
				board.GetSquareContents( 83 ) != NULL &&
				!board.GetSquareContents( 83 )->HasMoved() )
				eval += factor5;
			if( board.GetSquareContents( 76 ) != NULL &&
				board.GetSquareContents( 86 ) != NULL &&
				!board.GetSquareContents( 86 )->HasMoved() )
				eval += factor5;
		}
		else
		{
			if( board.GetSquareContents( 63 ) != NULL &&
				board.GetSquareContents( 73 ) != NULL &&
				!board.GetSquareContents( 73 )->HasMoved() )
				eval += factor5;
			if( board.GetSquareContents( 66 ) != NULL &&
				board.GetSquareContents( 76 ) != NULL &&
				!board.GetSquareContents( 76 )->HasMoved() )
				eval += factor5;
		}
	}
	else if( board.GetNumberOfCapturedPieces() > 30 ||
		board.GetMaterial( 0 ) - board.GetPawnMaterial( 0 ) + 
		board.GetMaterial( 1 ) - board.GetPawnMaterial( 1 ) < 20000 )
		//	we are in the end-game
		currentPhase = phases + 2;
	else
		//	we are in the mid-game
		currentPhase = phases + 1;


	if( pPawnHash != NULL )
	{
		// *** OUTPOSTS *** //

		//	outposts are minor pieces in central squares that cannot be driven off by enemy pawns.
		//	the basic output bonus is the value of the piece's square in the 'outpost' array, times
		//	the value of the piece type's 'outpost factor' (stored in 'gameInt1'.)

		//	a posted piece that is protected by one of its own pawns is extra good, so
		//	it gets 150% of the above bonus.  a posted piece protected by two of its
		//	own pawns is really, really good, because if the posted piece is captured,
		//	the player will have a protected, passed pawn.  a posted piece that is thus
		//	protected by 2 pawns gets double the standard output bonus.
		for( int x = 0; x < 6; x++ )
		{
			Piece *piece = minorPieces[0][x];
			if( piece != NULL && !piece->IsCaptured() && piece->GetType().gameInt1 > 0 &&
				outpost_10x10[piece->GetSquareNumber()] > 0 &&
				((int) (pPawnHash->nPawnsPerFile[1][board.files[0][piece->GetSquareNumber()]-1]) == 0 ||
				(int) (pPawnHash->backPawnRank[1][board.files[0][piece->GetSquareNumber()]-1]) <= board.ranks[0][piece->GetSquareNumber()]) &&
				((int) (pPawnHash->nPawnsPerFile[1][board.files[0][piece->GetSquareNumber()]+1]) == 0 ||
				(int) (pPawnHash->backPawnRank[1][board.files[0][piece->GetSquareNumber()]+1]) <= board.ranks[0][piece->GetSquareNumber()]) )
			{
				//	basic bonus for player 0's posted piece
				eval += outpost_10x10[piece->GetSquareNumber()] * piece->GetType().gameInt1;
				int pawnSquare1 = board.GetMovementMatrix( DIRECTION_SE )[piece->GetSquareNumber()];
				int pawnSquare2 = board.GetMovementMatrix( DIRECTION_SW )[piece->GetSquareNumber()];
				if( board.GetSquareContents( pawnSquare1 ) != NULL &&
					board.GetSquareContents( pawnSquare1 )->GetType().IsPawn() )
					//	additional 50% bonus for this pawn defender
					eval += (outpost_10x10[piece->GetSquareNumber()] *
							piece->GetType().gameInt1) >> 1;
				if( board.GetSquareContents( pawnSquare2 ) != NULL &&
					board.GetSquareContents( pawnSquare2 )->GetType().IsPawn() )
					//	additional 50% bonus for this pawn defender
					eval += (outpost_10x10[piece->GetSquareNumber()] *
							piece->GetType().gameInt1) >> 1;
			}

			piece = minorPieces[1][x];
			if( piece != NULL && !piece->IsCaptured() &&
				outpost_10x10[board.flipSquare[1][piece->GetSquareNumber()]] > 0 &&
				piece->GetType().gameInt1 > 0 &&
				((int) (pPawnHash->nPawnsPerFile[0][board.files[0][piece->GetSquareNumber()]-1]) == 0 ||
				(int) (pPawnHash->backPawnRank[0][board.files[0][piece->GetSquareNumber()]-1]) >= board.ranks[0][piece->GetSquareNumber()]) &&
				((int) (pPawnHash->nPawnsPerFile[0][board.files[0][piece->GetSquareNumber()]+1]) == 0 ||
				(int) (pPawnHash->backPawnRank[0][board.files[0][piece->GetSquareNumber()]+1]) >= board.ranks[0][piece->GetSquareNumber()]) )
			{
				//	basic bonus for player 1's posted piece
				eval -= outpost_10x10[board.flipSquare[1][piece->GetSquareNumber()]] *
						piece->GetType().gameInt1;
				int pawnSquare1 = board.GetMovementMatrix( DIRECTION_NE )[piece->GetSquareNumber()];
				int pawnSquare2 = board.GetMovementMatrix( DIRECTION_NW )[piece->GetSquareNumber()];
				if( board.GetSquareContents( pawnSquare1 ) != NULL &&
					board.GetSquareContents( pawnSquare1 )->GetType().IsPawn() )
					//	additional 50% bonus for this pawn defender
					eval -= (outpost_10x10[board.flipSquare[1][piece->GetSquareNumber()]] *
							piece->GetType().gameInt1) >> 1;
				if( board.GetSquareContents( pawnSquare2 ) != NULL &&
					board.GetSquareContents( pawnSquare2 )->GetType().IsPawn() )
					//	additional 50% bonus for this pawn defender
					eval -= (outpost_10x10[board.flipSquare[1][piece->GetSquareNumber()]] *
							piece->GetType().gameInt1) >> 1;
			}
		}

		// *** ROOK and CHANCELLOR on OPEN and SEMI-OPEN FILE BONUS *** //
		if( rooks[0][0] != NULL && !rooks[0][0]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[0][board.files[0][rooks[0][0]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval += 60;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][rooks[0][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 40;
		}
		if( rooks[0][1] != NULL && !rooks[0][1]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[0][board.files[0][rooks[0][1]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval += 60;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][rooks[0][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 40;
		}
		if( chancellors[0][0] != NULL && !chancellors[0][0]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[0][board.files[0][chancellors[0][0]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval += 60;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][chancellors[0][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 40;
		}
		if( chancellors[0][1] != NULL && !chancellors[0][1]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[0][board.files[0][chancellors[0][1]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval += 60;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][chancellors[0][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 40;
		}

		if( rooks[1][0] != NULL && !rooks[1][0]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[1][board.files[0][rooks[1][0]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval -= 60;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][rooks[1][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 40;
		}
		if( rooks[1][1] != NULL && !rooks[1][1]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[1][board.files[0][rooks[1][1]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval -= 60;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][rooks[1][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 40;
		}
		if( chancellors[1][0] != NULL && !chancellors[1][0]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[1][board.files[0][chancellors[1][0]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval -= 60;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][chancellors[1][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 40;
		}
		if( chancellors[1][1] != NULL && !chancellors[1][1]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[1][board.files[0][chancellors[1][1]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval -= 60;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][chancellors[1][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 40;
		}
	}

	//	TWO-BISHOPS BONUS
	//	give bonus for having both bishops
	if( bishops[0][0] != NULL && bishops[0][1] != NULL &&
		(!bishops[0][0]->IsCaptured() && !bishops[0][1]->IsCaptured()) )
		eval += 250;
	if( bishops[1][0] != NULL && bishops[1][1] != NULL &&
		(!bishops[1][0]->IsCaptured() && !bishops[1][1]->IsCaptured()) )
		eval -= 250;

	return *currentPhase;
}

int UnicornChessGame::EnumeratePromotions
	( Piece *piece, 
	  int fromSquare,
	  int toSquare,
	  PieceType **promotions )
{
	promotions[0] = &DecimalQueen::decimalQueen;
	promotions[1] = &DecimalUnicorn::decimalUnicorn;
	promotions[2] = &DecimalChancellor::decimalChancellor;
	return 3;
}


word32 UnicornChessGame::AdjustPrimaryHash
	( word32 primaryHash )
{
	//	check for castling privledges
	if( !board.GetKing( 0 )->HasMoved() )
	{
		if( rooks[0][0] != NULL &&
			!rooks[0][0]->HasMoved() )
			primaryHash ^= castlingHash0q;
		if( rooks[0][1] != NULL &&
			!rooks[0][1]->HasMoved() )
			primaryHash ^= castlingHash0k;
	}
	else if( !board.GetKing( 1 )->HasMoved() )
	{
		if( rooks[1][0] != NULL &&
			!rooks[1][0]->HasMoved() )
			primaryHash ^= castlingHash1q;
		if( rooks[1][1] != NULL &&
			!rooks[1][1]->HasMoved() )
			primaryHash ^= castlingHash1k;
	}

	if( enPassant )
		return primaryHash ^ GetEnPassantHash( board.GetCurrentGameRecord(), enPassantHashMap );
	return primaryHash;
}
