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

                                 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 "AngelsAndDevilsGame.h"

#include "Capablanca_Types.h"
//	this include file contains the piece types; although these pieces 
//	move the same as conventional Chess pieces, they have different 
//	classes because they have different material values on this board.

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


// *** DEBUG MEMORY ALLOCATION *** //
#ifdef _DEBUG
#define new SAFE_NEW
#endif


CapablancaAngel CapablancaAngel::capablancaAngel;
CapablancaDevil CapablancaDevil::capablancaDevil;


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


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

	//	turn on special Check testing.  this is necessary for 
	//	games in which the goal is to Checkmate the King, but 
	//	there are "special moves" handled by the AddSpecialMoves 
	//	function, that could attack the King.  in this case, you
	//	must set specialCheckTesting to true, and override the 
	//	IsInCheck() function.
	specialCheckTesting = true;

	//	turn off static exchange evaluation, since the 
	//	the static exchange evaluator cannot handle the 
	//	indirect capture capabilities of Angels or Devils
	useStaticExchangeEvaluation = false;

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

	//	implement 50-move draw rule
	autoDrawPeriod = 100;

	//	turn off null-move and futility pruning 
	//	after 30 pieces have been captured
	endgameCaptureThreshold = 30;

	//	razoring and futility pruning margins
	razorMargin = 10500;
	futilityMargin = 4000;
	extendedFutilityMargin = 6500;

	// *** 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( 1 );
	phases[0].SetPawnDeficiencyFactor( 8 );
	phases[0].SetSquareValuesFactor( 1 );
	phases[0].SetKingSafetyFactor( 4 );
	phases[0].SetTropismFactor( 6 );

	//	settings for phase 1 (midgame)
	phases[1].SetNumber( 1 );
	phases[1].SetMobilityFactor( 1 );
	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.
	CapablancaKnight::capablancaKnight.gameInt1 = 10;
	CapablancaBishop::capablancaBishop.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_96 );
}

void AngelsAndDevilsGame::AddPlayerPieceTypes( char *gameName )
{
	//	add the piece types that each player may have
	board.AddPlayerPieceTypeBothPlayers( CapablancaRook::capablancaRook );
	board.AddPlayerPieceTypeBothPlayers( CapablancaBishop::capablancaBishop );
	board.AddPlayerPieceTypeBothPlayers( CapablancaKnight::capablancaKnight );
	board.AddPlayerPieceTypeBothPlayers( CapablancaQueen::capablancaQueen );
	board.AddPlayerPieceTypeBothPlayers( CapablancaKing::capablancaKing );
	board.AddPlayerPieceTypeBothPlayers( CapablancaPawn::capablancaPawn );
	board.AddPlayerPieceType( CapablancaAngel::capablancaAngel, 0 );
	board.AddPlayerPieceType( CapablancaDevil::capablancaDevil, 1 );
}

void AngelsAndDevilsGame::Initialize()
{
	//	first, let the base class Initialize() do its thing
	Game::Initialize();

	//	now that board.PostInitialize() has completed, we can 
	//	initialize the BitBoards for detecting Checks from Devils
	for( int square = 0; square < 80; square++ )
	{
		int directions[4] = { DIRECTION_N, DIRECTION_E, DIRECTION_S, DIRECTION_W };
		for( int dir = 0; dir < 4; dir++ )
		{
			int newSquare = board.GetMovementMatrix( directions[dir] )[square];
			if( newSquare != -1 )
			{
				int oppositeSquare = board.GetOppositeMovementMatrix( directions[dir] )[square];
				if( oppositeSquare != -1 )
					devilAttackMatrix[square].SetBit( newSquare );
				newSquare = board.GetMovementMatrix( directions[dir] )[newSquare];
				if( newSquare != -1 )
				{
					devilAttackMatrix[square].SetBit( newSquare );
					int clockwiseSquare = board.GetMovementMatrix( directions[(dir + 1) % 4] )[newSquare];
					if( clockwiseSquare != -1 )
						devilAttackMatrix[square].SetBit( clockwiseSquare );
					int counterClockwiseSquare = board.GetMovementMatrix( directions[(dir + 3) % 4] )[newSquare];
					if( counterClockwiseSquare != -1 )
						devilAttackMatrix[square].SetBit( counterClockwiseSquare );
					newSquare = board.GetMovementMatrix( directions[dir] )[newSquare];
					if( newSquare != -1 )
						devilAttackMatrix[square].SetBit( newSquare );
				}
			}
		}
	}
}

void AngelsAndDevilsGame::ChangeRulesByVariant
	( char *gameName, 
	  char *&array, 
	  char *&book )
{
	if( !strcmp( gameName, "Angels and Devils" ) )
	{
		//	place pieces
		array = "rdnbqkbndr/pppppppppp/10/10/10/10/PPPPPPPPPP/RANBQKBNAR";
		//	set name of opening book
		book = "openings\\AngelsAndDevils.txt";
	}
}

Piece *AngelsAndDevilsGame::AddPiece
	( PieceType &pieceType,
	  int nPlayer,
	  int nRank,
	  int nFile )
{
	Piece *newPiece = Game::AddPiece( pieceType, nPlayer, nRank, nFile );
	if( newPiece->IsPawn() )
	{
		if( newPiece->GetPlayerNumber() == 0 )
			newPiece->GetPromotionZone().AddToZone( 70, 79 );
		else
			newPiece->GetPromotionZone().AddToZone( 0, 9 );
	}
	return newPiece;
}

void AngelsAndDevilsGame::AddSpecialMoves
	( int currentPlayer, 
	  MovementList &stack, 
	  GameRec &gameRecord,
	  bool quiescentSearch )
{
	int directions[4] = { DIRECTION_N, DIRECTION_E, DIRECTION_W, DIRECTION_S };

	// *** ANGEL'S MOVES *** //
	if( board.GetCurrentPlayerNumber() == 0 )
	{
		BitBoard96 angels = CapablancaAngel::capablancaAngel.GetPieces( 0 );
		while( angels )
		{
			int startSquare = angels.GetFirstBit();
			angels.ToggleBit( startSquare );
			Piece *piece = board.GetSquareContents( startSquare );
			for( int j = 0; j < 4; j++ )
			{
				int captureSquares[4];
				int nCaptures = 0;
				int nSquare = board.GetMovementMatrix( directions[j] )[startSquare];
				if( nSquare != -1 )
				{
					if( board.GetSquareContents( nSquare ) != NULL && 
						board.GetSquareContents( nSquare )->GetPlayerNumber() != piece->GetPlayerNumber() )
						captureSquares[nCaptures++] = nSquare;
					nSquare = board.GetMovementMatrix( directions[j] )[nSquare];
					if( nSquare != -1 )
					{
						if( board.GetSquareContents( nSquare ) == NULL )
							if( nCaptures == 0 )
							{
								if( !quiescentSearch )
									stack.AddMove( startSquare, nSquare );
							}
							else
							{
								stack.BeginMoveAdd( UserMove1, startSquare, nSquare );
								stack.AddPickUp( startSquare );
								stack.AddPickUp( captureSquares[0] );
								stack.AddDrop( piece, nSquare );
								stack.EndMoveAdd( board.GetSquareContents( captureSquares[0] )->GetBaseValue() + 500 );
							}
						else
						{
							bool friendly = board.GetSquareContents( nSquare )->GetPlayerNumber() == piece->GetPlayerNumber();
							if( !friendly )
								captureSquares[nCaptures++] = nSquare;
							if( nCaptures == 1 && !friendly )
								stack.AddCapture( startSquare, nSquare );
							else if( !friendly )
							{
								stack.BeginMoveAdd( UserMove1, startSquare, nSquare );
								stack.AddPickUp( startSquare );
								stack.AddPickUp( nSquare );
								stack.AddPickUp( captureSquares[0] );
								stack.AddDrop( piece, nSquare );
								stack.EndMoveAdd( board.GetSquareContents( captureSquares[0] )->GetBaseValue() + 500 );
							}
						}
						nSquare = board.GetMovementMatrix( directions[j] )[nSquare];
						if( nSquare != -1 )
						{
							if( board.GetSquareContents( nSquare ) != NULL && 
								board.GetSquareContents( nSquare )->GetPlayerNumber() != piece->GetPlayerNumber() )
								captureSquares[nCaptures++] = nSquare;
							nSquare = board.GetMovementMatrix( directions[j] )[nSquare];
							if( nSquare != -1 )
							{
								if( board.GetSquareContents( nSquare ) == NULL )
									if( nCaptures == 0 )
									{
										if( !quiescentSearch )
											stack.AddMove( startSquare, nSquare );
									}
									else 
									{
										stack.BeginMoveAdd( UserMove1, startSquare, nSquare );
										stack.AddPickUp( startSquare );
										int captureValue = 0;
										for( int x = 0; x < nCaptures; x++ )
										{
											stack.AddPickUp( captureSquares[x] );
											captureValue += board.GetSquareContents( captureSquares[x] )->GetBaseValue();
										}
										stack.AddDrop( piece, nSquare );
										stack.EndMoveAdd( captureValue + 500 );
									}
								else if( board.GetSquareContents( nSquare )->GetPlayerNumber() != piece->GetPlayerNumber() )
									if( nCaptures == 0 )
										stack.AddCapture( startSquare, nSquare );
									else 
									{
										stack.BeginMoveAdd( UserMove1, startSquare, nSquare );
										stack.AddPickUp( startSquare );
										int captureValue = 0;
										for( int x = 0; x < nCaptures; x++ )
										{
											stack.AddPickUp( captureSquares[x] );
											captureValue += board.GetSquareContents( captureSquares[x] )->GetBaseValue();
										}
										stack.AddPickUp( nSquare );
										captureValue += board.GetSquareContents( nSquare )->GetBaseValue();
										stack.AddDrop( piece, nSquare );
										stack.EndMoveAdd( captureValue + 500 );
									}
							}
						}
					}
				}
			}
		}
	}
	// *** DEVIL'S MOVES *** //
	else
	{
		BitBoard96 devils = CapablancaDevil::capablancaDevil.GetPieces( 1 );
		while( devils )
		{
			int startSquare = devils.GetFirstBit();
			devils.ToggleBit( startSquare );
			Piece *piece = board.GetSquareContents( startSquare );
			for( int j = 0; j < 4; j++ )
			{
				int captureSquares[4];
				int nCaptures = 0;
				int nSquare = board.GetMovementMatrix( directions[j] )[startSquare];
				if( nSquare != -1 )
				{
					nSquare = board.GetMovementMatrix( directions[j] )[nSquare];
					if( nSquare != -1 )
					{
						//	look at adjacent squares for "burn" victims
						for( int k = 0; k < 4; k++ )
						{
							int adjacentSquare = board.GetMovementMatrix( directions[k] )[nSquare];
							if( adjacentSquare != -1 &&
								board.GetSquareContents( adjacentSquare ) != NULL && 
								board.GetSquareContents( adjacentSquare )->GetPlayerNumber() != piece->GetPlayerNumber() )
								//	this piece gets burned
								captureSquares[nCaptures++] = adjacentSquare;
						}
						if( board.GetSquareContents( nSquare ) == NULL )
						{
							if( nCaptures == 0 )
							{
								if( !quiescentSearch )
									stack.AddMove( startSquare, nSquare );
							}
							else
							{
								int captureValue = 0;
								stack.BeginMoveAdd( UserMove1, startSquare, nSquare );
								stack.AddPickUp( startSquare );
								for( int k = 0; k < nCaptures; k++ )
								{
									stack.AddPickUp( captureSquares[k] );
									captureValue += board.GetSquareContents( captureSquares[k] )->GetBaseValue();
								}
								stack.AddDrop( piece, nSquare );
								stack.EndMoveAdd( captureValue + 1000 );
							}
						}
						else if( board.GetSquareContents( nSquare )->GetPlayerNumber() != currentPlayer || 
								 board.GetSquareContents( nSquare )->GetType() != CapablancaKing::capablancaKing )
						{
							if( nCaptures == 0 )
								stack.AddCapture( startSquare, nSquare );
							else
							{
								int captureValue = board.GetSquareContents( nSquare )->GetBaseValue();
								stack.BeginMoveAdd( UserMove1, startSquare, nSquare );
								stack.AddPickUp( startSquare );
								for( int k = 0; k < nCaptures; k++ )
								{
									stack.AddPickUp( captureSquares[k] );
									captureValue += board.GetSquareContents( captureSquares[k] )->GetBaseValue();
								}
								stack.AddPickUp( nSquare );
								stack.AddDrop( piece, nSquare );
								stack.EndMoveAdd( captureValue + 1000 );
							}
						}
					}
				}
			}
		}
	}

	// *** EN PASSANT *** //
	if( enPassant )
		Game::AddEnPassantMoves( stack, gameRecord, quiescentSearch );

	// *** CASTLING *** //
	if( !quiescentSearch && board.GetCurrentPlayerNumber() == 0 && !board.FastCheckTest() )
	{
		//	white to move
		Piece *king = board.GetKing( 0 );
		if( !king->HasMoved() )
		{
			//	king side
			if( board.GetSquareContents( 9 ) != NULL &&
				!board.GetSquareContents( 9 )->HasMoved() )
			{
				if( board.GetSquareContents( 6 ) == NULL &&
					board.GetSquareContents( 7 ) == NULL &&
					board.GetSquareContents( 8 ) == NULL )
				{
					//	squares are vacant; make sure they are not attacked
					if( !(IsSquareAttacked( 6, 1 ) || 
						  IsSquareAttacked( 7, 1 ) ||
						  IsSquareAttacked( 8, 1 )) )
					{
						Piece *castlingPiece = board.GetSquareContents( 9 );
						stack.BeginMoveAdd( Castling, 5, 8 );
						stack.AddPickUp( 5 );
						stack.AddPickUp( 9 );
						stack.AddDrop( king, 8 );
						stack.AddDrop( castlingPiece, 7 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
			//	queen side
			if( board.GetSquareContents( 0 ) != NULL &&
				!board.GetSquareContents( 0 )->HasMoved() )
			{
				if( board.GetSquareContents( 4 ) == NULL && 
					board.GetSquareContents( 3 ) == NULL &&
					board.GetSquareContents( 2 ) == NULL && 
					board.GetSquareContents( 1 ) == NULL )
				{
					//	square are vacant; make sure they are not attacked
					if( !(IsSquareAttacked( 4, 1 ) || 
						  IsSquareAttacked( 3, 1 ) || 
						  IsSquareAttacked( 2, 1 )) )
					{
						Piece *castlingPiece = board.GetSquareContents( 0 );
						stack.BeginMoveAdd( Castling, 5, 2 );
						stack.AddPickUp( 5 );
						stack.AddPickUp( 0 );
						stack.AddDrop( king, 2 );
						stack.AddDrop( castlingPiece, 3 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
		}
	}
	else if( !quiescentSearch && board.GetCurrentPlayerNumber() == 1 && !board.FastCheckTest() )
	{
		//	black to move
		Piece *king = board.GetKing( 1 );
		if( !king->HasMoved() )
		{
			//	king side
			if( board.GetSquareContents( 79 ) != NULL &&
				!board.GetSquareContents( 79 )->HasMoved() )
			{
				if( board.GetSquareContents( 76 ) == NULL &&
					board.GetSquareContents( 77 ) == NULL &&
					board.GetSquareContents( 78 ) == NULL )
				{
					//	square are vacant; make sure they are not attacked
					if( !(IsSquareAttacked( 76, 0 ) || 
						  IsSquareAttacked( 77, 0 ) ||
						  IsSquareAttacked( 78, 0 )) )
					{
						Piece *castlingPiece = board.GetSquareContents( 79 );
						stack.BeginMoveAdd( Castling, 75, 78 );
						stack.AddPickUp( 75 );
						stack.AddPickUp( 79 );
						stack.AddDrop( king, 78 );
						stack.AddDrop( castlingPiece, 77 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
			//	queen side
			if( board.GetSquareContents( 70 ) != NULL &&
				!board.GetSquareContents( 70 )->HasMoved() )
			{
				if( board.GetSquareContents( 74 ) == NULL &&
					board.GetSquareContents( 73 ) == NULL && 
					board.GetSquareContents( 72 ) == NULL &&
					board.GetSquareContents( 71 ) == NULL )
				{
					//	squares are vacant; make sure they are not attacked
					if( !(IsSquareAttacked( 74, 0 ) || 
						  IsSquareAttacked( 73, 0 ) ||
						  IsSquareAttacked( 72, 0 )) )
					{
						Piece *castlingPiece = board.GetSquareContents( 70 );
						stack.BeginMoveAdd( Castling, 75, 72 );
						stack.AddPickUp( 75 );
						stack.AddPickUp( 70 );
						stack.AddDrop( king, 72 );
						stack.AddDrop( castlingPiece, 73 );
						stack.EndMoveAdd( 1000 );
					}
				}
			}
		}
	}
}

bool AngelsAndDevilsGame::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)

	ASSERT(!board.GetKing( 0 )->IsCaptured());
	ASSERT(!board.GetKing( 1 )->IsCaptured());

	if( gameRecord.gameInt1 < 15 )
	{
		//	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 &AngelsAndDevilsGame::AdjustEvaluation
	( int &eval,
	  PawnHash *pPawnHash )
{
	Phase *currentPhase = phases;

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

		//	in the opening we consider the following things:
		//		- give bonus for developing minor pieces
		//		- penalize the queen and rooks for moving before all minor pieces have moved
		//		- give a bonus for castling; give a penalty for losing the ability to castle
		//		- give a penalty for moving pieces twice

		//	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) * 2;
		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( board.GetKing( 0 )->GetFlags() & FLAGS_HAS_CASTLED )
			eval += factor5;
		else
		{
			if( board.GetKing( 0 )->HasMoved() || 
				((board.GetSquareContents( 9 ) == NULL ||
				  board.GetSquareContents( 9 )->HasMoved()) &&
				 (board.GetSquareContents( 0 ) == NULL ||
				  board.GetSquareContents( 0 )->HasMoved())) )
				eval -= factor5;
		}
		if( board.GetKing( 1 )->GetFlags() & FLAGS_HAS_CASTLED )
			eval -= factor5;
		else
		{
			if( board.GetKing( 1 )->HasMoved() || 
				((board.GetSquareContents( 79 ) == NULL ||
				  board.GetSquareContents( 79 )->HasMoved()) &&
				 (board.GetSquareContents( 70 ) == NULL ||
				  board.GetSquareContents( 70 )->HasMoved())) )
				eval += factor5;
		}

		//	don't block the king or queen's pawns in the opening!!!
		if( board.GetSquareContents( 24 ) != NULL &&
			board.GetSquareContents( 14 ) != NULL &&
			!board.GetSquareContents( 14 )->HasMoved() )
			eval -= 160  - (factor << 4);
		if( board.GetSquareContents( 25 ) != NULL &&
			board.GetSquareContents( 15 ) != NULL &&
			!board.GetSquareContents( 15 )->HasMoved() )
			eval -= 160  - (factor << 4);
		if( board.GetSquareContents( 54 ) != NULL &&
			board.GetSquareContents( 64 ) != NULL &&
			!board.GetSquareContents( 64 )->HasMoved() )
			eval += 160  - (factor << 4);
		if( board.GetSquareContents( 55 ) != NULL &&
			board.GetSquareContents( 65 ) != NULL &&
			!board.GetSquareContents( 65 )->HasMoved() )
			eval += 160  - (factor << 4);
	}
	else
		currentPhase = phases + 1;


	// *** 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.

	//	loop through all minor pieces
	BitBoard96 minors;
	minors = CapablancaKnight::capablancaKnight.GetPieces( 0 ) | 
		     CapablancaBishop::capablancaBishop.GetPieces( 0 );
	while( minors )
	{
		int square = minors.GetFirstBit();
		minors.ToggleBit( square );
		Piece *piece = board.GetSquareContents( square );
		if( outpost_10x8[square] > 0 && piece->GetType().gameInt1 > 0 &&
			((int) (pPawnHash->nPawnsPerFile[1][board.files[0][square]-1]) == 0 ||
			(int) (pPawnHash->backPawnRank[1][board.files[0][square]-1]) <= board.ranks[0][square]) &&
			((int) (pPawnHash->nPawnsPerFile[1][board.files[0][square]+1]) == 0 ||
			(int) (pPawnHash->backPawnRank[1][board.files[0][square]+1]) <= board.ranks[0][square]) )
		{
			//	basic bonus for player 0's posted piece
			eval += outpost_10x8[square] * piece->GetType().gameInt1;
			int pawnSquare1 = board.GetMovementMatrix( DIRECTION_SE )[square];
			int pawnSquare2 = board.GetMovementMatrix( DIRECTION_SW )[square];
			if( board.GetSquareContents( pawnSquare1 ) != NULL &&
				board.GetSquareContents( pawnSquare1 )->GetPlayerNumber() == 0 && 
				board.GetSquareContents( pawnSquare1 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval += (outpost_10x8[square] * piece->GetType().gameInt1) >> 1;
			if( board.GetSquareContents( pawnSquare2 ) != NULL &&
				board.GetSquareContents( pawnSquare2 )->GetPlayerNumber() == 0 && 
				board.GetSquareContents( pawnSquare2 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval += (outpost_10x8[square] * piece->GetType().gameInt1) >> 1;
		}
	}
	minors = CapablancaKnight::capablancaKnight.GetPieces( 1 ) | 
		     CapablancaBishop::capablancaBishop.GetPieces( 1 );
	while( minors )
	{
		int square = minors.GetFirstBit();
		minors.ToggleBit( square );
		Piece *piece = board.GetSquareContents( square );
		if( outpost_10x8[board.flipSquare[1][square]] > 0 && piece->GetType().gameInt1 > 0 &&
			((int) (pPawnHash->nPawnsPerFile[0][board.files[0][square]-1]) == 0 ||
			(int) (pPawnHash->backPawnRank[0][board.files[0][square]-1]) <= board.ranks[0][square]) &&
			((int) (pPawnHash->nPawnsPerFile[0][board.files[0][square]+1]) == 0 ||
			(int) (pPawnHash->backPawnRank[0][board.files[0][square]+1]) <= board.ranks[0][square]) )
		{
			//	basic bonus for player 1's posted piece
			eval -= outpost_10x8[square] * piece->GetType().gameInt1;
			int pawnSquare1 = board.GetMovementMatrix( DIRECTION_SE )[square];
			int pawnSquare2 = board.GetMovementMatrix( DIRECTION_SW )[square];
			if( board.GetSquareContents( pawnSquare1 ) != NULL && 
				board.GetSquareContents( pawnSquare1 )->GetPlayerNumber() == 1 && 
				board.GetSquareContents( pawnSquare1 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval -= (outpost_10x8[board.flipSquare[1][square]] * piece->GetType().gameInt1) >> 1;
			if( board.GetSquareContents( pawnSquare2 ) != NULL &&
				board.GetSquareContents( pawnSquare2 )->GetPlayerNumber() == 1 && 
				board.GetSquareContents( pawnSquare2 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval -= (outpost_10x8[board.flipSquare[1][square]] * piece->GetType().gameInt1) >> 1;
		}
	}

	// *** ROOK ON OPEN and SEMI-OPEN FILE BONUS *** //
	BitBoard96 rooks;
	rooks = CapablancaRook::capablancaRook.GetPieces( 0 );
	while( rooks )
	{
		int square = rooks.GetFirstBit();
		rooks.ToggleBit( square );
		if( pPawnHash->nPawnsPerFile[0][board.files[0][square]] == 0 )
		{
			//	file at least semi-open
			eval += 50;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][square]] == 0 )
				//	fully open file
				eval += 20;
		}
	}
	rooks = CapablancaRook::capablancaRook.GetPieces( 1 );
	while( rooks )
	{
		int square = rooks.GetFirstBit();
		rooks.ToggleBit( square );
		if( pPawnHash->nPawnsPerFile[1][board.files[0][square]] == 0 )
		{
			//	file at least semi-open
			eval -= 50;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][square]] == 0 )
				//	fully open file
				eval -= 20;
		}
	}

	//	TWO-BISHOPS BONUS
	//	TODO: temporarily removed

	return *currentPhase;
}

word32 AngelsAndDevilsGame::AdjustPrimaryHash
	( word32 primaryHash )
{
	if( !board.GetKing( 0 )->HasMoved() )
	{
		Piece *castlingPiece = board.GetSquareContents( 0 );
		if( castlingPiece != NULL && 
			!castlingPiece->HasMoved() )
			primaryHash ^= castlingHash0q;
		castlingPiece = board.GetSquareContents( 9 );
		if( castlingPiece != NULL && 
			!castlingPiece->HasMoved() )
			primaryHash ^= castlingHash0k;
	}
	else if( !board.GetKing( 1 )->HasMoved() )
	{
		Piece *castlingPiece = board.GetSquareContents( 70 );
		if( castlingPiece != NULL && 
			!castlingPiece->HasMoved() )
			primaryHash ^= castlingHash1q;
		castlingPiece = board.GetSquareContents( 79 );
		if( castlingPiece != NULL && 
			!castlingPiece->HasMoved() )
			primaryHash ^= castlingHash1k;
	}
	if( enPassant )
		return primaryHash ^ GetEnPassantHash( board.GetCurrentGameRecord(), enPassantHashMap );
	return primaryHash;
}

int AngelsAndDevilsGame::EnumeratePromotions
	( Piece *piece, 
	  int fromSquare,
	  int toSquare,
	  PieceType **promotions,
	  bool quiescentSearch )
{
	//	pawns promote only to standard types, not Angels or Devils
	promotions[0] = &CapablancaRook::capablancaRook;
	promotions[1] = &CapablancaKnight::capablancaKnight;
	promotions[2] = &CapablancaBishop::capablancaBishop;
	promotions[3] = &CapablancaQueen::capablancaQueen;
	return 4;
}

bool AngelsAndDevilsGame::IsInCheck
	( Piece *king )
{
	//	store King's square for convenience
	int kingSquare = king->GetSquareNumber();

	//	define array of orthogonal directions
	int directions[4] = { DIRECTION_N, DIRECTION_E, DIRECTION_W, DIRECTION_S };

	if( king->GetPlayerNumber() == 0 )
	{
		//	White King; check for attacks from Devils
		if( devilAttackMatrix[king->GetSquareNumber()] & 
			CapablancaDevil::capablancaDevil.GetPieces( 1 ) )
			//	yes, the King is checked by at least one Devil
			return true;
	}
	else if( king->GetRank() > 0 )
	{
		//	Black King; check for attacks from Angels
		//	NOTE: no point in checking if the King is on 
		//	his first rank.  This is a safe-zone from Angels
		for( int dir = 0; dir < 4; dir++ )
		{
			int nSquare = board.GetMovementMatrix( directions[dir] )[kingSquare];
			if( nSquare != -1 )
			{
				Piece *pieceOnSquare = board.GetSquareContents( nSquare );
				if( pieceOnSquare != NULL && 
					pieceOnSquare->GetType() == CapablancaAngel::capablancaAngel )
				{
					//	Angel is adjacent; he can jump over this King if he has 
					//	enough room on the board to jump over, and that square is 
					//	not occupied by a friendly (White) piece.
					int oppositeSquare = board.GetOppositeMovementMatrix( directions[dir] )[kingSquare];
					if( oppositeSquare != -1 )
					{
						if( board.GetSquareContents( oppositeSquare ) == NULL || 
							board.GetSquareContents( oppositeSquare )->GetPlayerNumber() == 1 )
							//	the Angel can capture this King.  we are in Check.
							return true;
						//	if the Angel could not capture on this square, we need to 
						//	look two squares further and see if it can land there
						oppositeSquare = board.GetOppositeMovementMatrix( directions[dir] )[oppositeSquare];
						if( oppositeSquare != -1 )
						{
							oppositeSquare = board.GetOppositeMovementMatrix( directions[dir] )[oppositeSquare];
							if( oppositeSquare != -1 )
							{
								if( board.GetSquareContents( oppositeSquare ) == NULL || 
									board.GetSquareContents( oppositeSquare )->GetPlayerNumber() == 1 )
									//	the Angel can capture this King.  we are in Check.
									return true;
							}
						}
					}
				}
				//	look at next square ... this square is two squares away
				nSquare = board.GetMovementMatrix( directions[dir] )[nSquare];
				if( nSquare != -1 )
				{
					Piece *pieceOnSquare = board.GetSquareContents( nSquare );
					if( pieceOnSquare != NULL && 
						pieceOnSquare->GetType() == CapablancaAngel::capablancaAngel )
					{
						//	Angel is two squares away.  definitely in Check.
						return true;
					}
					//	look at next square ... this square is three squares away
					nSquare = board.GetMovementMatrix( directions[dir] )[nSquare];
					if( nSquare != -1 )
					{
						Piece *pieceOnSquare = board.GetSquareContents( nSquare );
						if( pieceOnSquare != NULL && 
							pieceOnSquare->GetType() == CapablancaAngel::capablancaAngel )
						{
							//	Angel is three squares away.  we are in Check if the 
							//	square in the opposite direction is available or 
							//	contains a Black piece.
							int oppositeSquare = board.GetOppositeMovementMatrix( directions[dir] )[kingSquare];
							if( oppositeSquare != -1 )
							{
								if( board.GetSquareContents( oppositeSquare ) == NULL || 
									board.GetSquareContents( oppositeSquare )->GetPlayerNumber() == 1 )
									//	the Angel can capture this King.  we are in Check.
									return true;
							}
						}
						//	look at next square ... this square is four squares away
						nSquare = board.GetMovementMatrix( directions[dir] )[nSquare];
						if( nSquare != -1 )
						{
							Piece *pieceOnSquare = board.GetSquareContents( nSquare );
							if( pieceOnSquare != NULL && 
								pieceOnSquare->GetType() == CapablancaAngel::capablancaAngel )
								//	Angel is four squares away; definitely in Check.
								return true;
						}
					}
				}
			}
		}
	}
	return Game::IsInCheck( king );
}
