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

                                 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 "CourierChessGame.h"
#include "Courier_Chess_Types.h"


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


CourierDecurion CourierDecurion::courierDecurion;
CourierQueen CourierQueen::courierQueen;
CourierWazir CourierWazir::courierWazir;
CourierBishop CourierBishop::courierBishop;
CourierGeneral CourierGeneral::courierGeneral;
CourierKing CourierKing::courierKing;
CourierPawn CourierPawn::courierPawn;
CourierFerz CourierFerz::courierFerz;
CourierKnight CourierKnight::courierKnight;
CourierElephant CourierElephant::courierElephant;
CourierRook CourierRook::courierRook;


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


CourierChessGame::CourierChessGame( Board &board, Player &whitePlayer, Player &blackPlayer ):
	Game(board, whitePlayer, blackPlayer)
{
	// ***************************************** //
	// ***                                   *** //
	// ***  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 38 pieces have been captured
	endgameCaptureThreshold = 38;

	//	razoring and futility pruning margins
	razorMargin = 5000;
	futilityMargin = 3250;
	extendedFutilityMargin = 4500;

	// *** 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( 0 );
	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( 0 );
	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.
	CourierKnight::courierKnight.gameInt1 = 10;
	CourierBishop::courierBishop.gameInt1 = 6;
	CourierElephant::courierElephant.gameInt1 = 5;
	CourierFerz::courierFerz.gameInt1 = 2;
	CourierWazir::courierWazir.gameInt1 = 2;
	CourierGeneral::courierGeneral.gameInt1 = 3;
	

	// *** INITIALIZATION *** //
	board.Initialize( this, BITBOARD_128 );
}
void CourierChessGame::AddPlayerPieceTypes()
{
	//	add the piece types that each player may have
	board.AddPlayerPieceTypeBothPlayers( CourierPawn::courierPawn );
	board.AddPlayerPieceTypeBothPlayers( CourierRook::courierRook );
	board.AddPlayerPieceTypeBothPlayers( CourierKnight::courierKnight );
	board.AddPlayerPieceTypeBothPlayers( CourierElephant::courierElephant );
	board.AddPlayerPieceTypeBothPlayers( CourierBishop::courierBishop );
	board.AddPlayerPieceTypeBothPlayers( CourierGeneral::courierGeneral );
	board.AddPlayerPieceTypeBothPlayers( CourierKing::courierKing );
	board.AddPlayerPieceTypeBothPlayers( CourierFerz::courierFerz );
	board.AddPlayerPieceTypeBothPlayers( CourierWazir::courierWazir );
}

void CourierChessGame::InitializeBoard()
{
	//	clear out all piece references
	for( int player = 0; player < 2; player++ )
		for( int x = 0; x < 9; x++ )
		{
			if( x < 2 )
				rooks[player][x] = NULL;
			minorPieces[player][x] = NULL;
		}

	//	add the piece types that each player may have
	AddPlayerPieceTypes();

	//	change names appropriately
	CourierFerz::courierFerz.SetFullName( "Queen" );
	CourierFerz::courierFerz.SetNotation( "Q" );
	CourierFerz::courierFerz.SetBitmapFileName( PIECE_SET_OLD_WORLD, 0, "images\\old_world\\WQueen.bmp" );
	CourierFerz::courierFerz.SetBitmapFileName( PIECE_SET_OLD_WORLD, 1, "images\\old_world\\BQueen.bmp" );
	CourierBishop::courierBishop.SetFullName( "Courier" );
	CourierBishop::courierBishop.SetNotation( "C" );
	CourierElephant::courierElephant.SetFullName( "Bishop" );
	CourierElephant::courierElephant.SetNotation( "B" );
	CourierElephant::courierElephant.SetBitmapFileName( PIECE_SET_OLD_WORLD, 0, "images\\old_world\\WBishop.bmp" );
	CourierElephant::courierElephant.SetBitmapFileName( PIECE_SET_OLD_WORLD, 1, "images\\old_world\\BBishop.bmp" );

	//	place pieces
	char *FEN = "rnbcmk1scbnr/1ppppp1pppp1/6q5/p5p4p/P5P4P/6Q5/1PPPPP1PPPP1/RNBCMK1SCBNR";
	if( LookupStringParameter( "array", FEN ) )
		useOpeningBook = false;
	board.PlacePiecesByFEN( FEN );

	//	opening book
	if( useOpeningBook )
	{
		Book *pBook = new Book( &board );
		board.SetOpeningBook( pBook, "openings\\CourierChess.txt" );
	}

	board.PostInitialize();
}

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

	if( pieceType == CourierRook::courierRook )
	{
		AddPieceToSet( newPiece, rooks, 2 );
	}
	else if( pieceType == CourierKnight::courierKnight ||
		     pieceType == CourierElephant::courierElephant || 
		     pieceType == CourierBishop::courierBishop || 
		     pieceType == CourierGeneral::courierGeneral || 
		     pieceType == CourierWazir::courierWazir || 
		     pieceType == CourierFerz::courierFerz )
	{
		//	add to minorPieces
		AddPieceToSet( newPiece, minorPieces, 9 );
	}
	else if( newPiece->IsPawn() )
	{
		if( newPiece->GetPlayerNumber() == 0 )
			newPiece->GetPromotionZone().AddToZone( 84, 95 );
		else
			newPiece->GetPromotionZone().AddToZone( 0, 11 );
	}

	return newPiece;
}

void CourierChessGame::DeletePiece
	( Piece *piece )
{
	RemovePieceFromSet( piece, rooks, 2 );
	RemovePieceFromSet( piece, minorPieces, 9 );
}

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

bool CourierChessGame::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 < 14 )
	{
		//	we are in the opening until gameInt1 reaches 14;
		//	in the opening, we increment gameInt1 for the following moves:
		//		- any capture
		//		- the first move of any piece
		//		- any pawn push
		if( gameRecord.materialCaptured > 0 ||
			(gameRecord.pieceMoved->GetFlags() & (FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE)) == FLAGS_HAS_MOVED ||
			gameRecord.pieceMoved->IsPawn() )
			gameRecord.gameInt1++;
	}

	return true;
}

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

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

		//	in the opening we consider the following things:
		//		- give bonus for developing minor pieces
		//		- give a penalty for moving pieces twice

		//	penalize undeveloped minor pieces
		if( minorPieces[0][0] != NULL && !minorPieces[0][0]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][1] != NULL && !minorPieces[0][1]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][2] != NULL && !minorPieces[0][2]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][3] != NULL && !minorPieces[0][3]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][4] != NULL && !minorPieces[0][4]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][5] != NULL && !minorPieces[0][5]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][6] != NULL && !minorPieces[0][6]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][7] != NULL && !minorPieces[0][7]->HasMoved() )
			eval -= 50;
		if( minorPieces[0][8] != NULL && !minorPieces[0][8]->HasMoved() )
			eval -= 50;
		if( minorPieces[1][0] != NULL && !minorPieces[1][0]->HasMoved() )
			eval += 50;
		if( minorPieces[1][1] != NULL && !minorPieces[1][1]->HasMoved() )
			eval += 50;
		if( minorPieces[1][2] != NULL && !minorPieces[1][2]->HasMoved() )
			eval += 50;
		if( minorPieces[1][3] != NULL && !minorPieces[1][3]->HasMoved() )
			eval += 50;
		if( minorPieces[1][4] != NULL && !minorPieces[1][4]->HasMoved() )
			eval += 50;
		if( minorPieces[1][5] != NULL && !minorPieces[1][5]->HasMoved() )
			eval += 50;
		if( minorPieces[1][6] != NULL && !minorPieces[1][6]->HasMoved() )
			eval += 50;
		if( minorPieces[1][7] != NULL && !minorPieces[1][7]->HasMoved() )
			eval += 50;
		if( minorPieces[1][8] != NULL && !minorPieces[1][8]->HasMoved() )
			eval += 50;

		//	don't block the king or queen's pawns in the opening!!!
//		if( board.GetSquareContents( D3 ) != NULL &&
//			board.GetSquareContents( D2 ) != NULL &&
//			!board.GetSquareContents( D2 )->HasMoved() )
//			eval -= 160;
//		if( board.GetSquareContents( E3 ) != NULL &&
//			board.GetSquareContents( E2 ) != NULL &&
//			!board.GetSquareContents( E2 )->HasMoved() )
//			eval -= 160;
	}
	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
	for( int x = 0; x < 9 && pPawnHash != NULL; x++ )
	{
		if( minorPieces[0][x] != NULL &&
			!minorPieces[0][x]->IsCaptured() &&
			outpost_12x8[minorPieces[0][x]->GetSquareNumber()] > 0 &&
			minorPieces[0][x]->GetType().gameInt1 > 0 &&
			((int) (pPawnHash->nPawnsPerFile[1][board.files[0][minorPieces[0][x]->GetSquareNumber()]-1]) == 0 ||
			 (int) (pPawnHash->backPawnRank[1][board.files[0][minorPieces[0][x]->GetSquareNumber()]-1]) <= board.ranks[0][minorPieces[0][x]->GetSquareNumber()]) &&
			((int) (pPawnHash->nPawnsPerFile[1][board.files[0][minorPieces[0][x]->GetSquareNumber()]+1]) == 0 ||
			 (int) (pPawnHash->backPawnRank[1][board.files[0][minorPieces[0][x]->GetSquareNumber()]+1]) <= board.ranks[0][minorPieces[0][x]->GetSquareNumber()]) )
		{
			//	basic bonus for player 0's posted piece
			eval += outpost_12x8[minorPieces[0][x]->GetSquareNumber()] *
			        minorPieces[0][x]->GetType().gameInt1;
			int pawnSquare1 = board.GetMovementMatrix( DIRECTION_SE )[minorPieces[0][x]->GetSquareNumber()];
			int pawnSquare2 = board.GetMovementMatrix( DIRECTION_SW )[minorPieces[0][x]->GetSquareNumber()];
			if( board.GetSquareContents( pawnSquare1 ) != NULL &&
				board.GetSquareContents( pawnSquare1 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval += (outpost_12x8[minorPieces[0][x]->GetSquareNumber()] *
				         minorPieces[0][x]->GetType().gameInt1) >> 1;
			if( board.GetSquareContents( pawnSquare2 ) != NULL &&
				board.GetSquareContents( pawnSquare2 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval += (outpost_12x8[minorPieces[0][x]->GetSquareNumber()] *
				         minorPieces[0][x]->GetType().gameInt1) >> 1;
		}
		if( minorPieces[1][x] != NULL &&
			!minorPieces[1][x]->IsCaptured() &&
			outpost_12x8[board.flipSquare[1][minorPieces[1][x]->GetSquareNumber()]] > 0 &&
			minorPieces[1][x]->GetType().gameInt1 > 0 &&
			((int) (pPawnHash->nPawnsPerFile[0][board.files[0][minorPieces[1][x]->GetSquareNumber()]-1]) == 0 ||
			 (int) (pPawnHash->backPawnRank[0][board.files[0][minorPieces[1][x]->GetSquareNumber()]-1]) >= board.ranks[0][minorPieces[1][x]->GetSquareNumber()]) &&
			((int) (pPawnHash->nPawnsPerFile[0][board.files[0][minorPieces[1][x]->GetSquareNumber()]+1]) == 0 ||
			 (int) (pPawnHash->backPawnRank[0][board.files[0][minorPieces[1][x]->GetSquareNumber()]+1]) >= board.ranks[0][minorPieces[1][x]->GetSquareNumber()]) )
		{
			//	basic bonus for player 1's posted piece
			eval -= outpost_12x8[board.flipSquare[1][minorPieces[1][x]->GetSquareNumber()]] *
			        minorPieces[1][x]->GetType().gameInt1;
			int pawnSquare1 = board.GetMovementMatrix( DIRECTION_NE )[minorPieces[1][x]->GetSquareNumber()];
			int pawnSquare2 = board.GetMovementMatrix( DIRECTION_NW )[minorPieces[1][x]->GetSquareNumber()];
			if( board.GetSquareContents( pawnSquare1 ) != NULL &&
				board.GetSquareContents( pawnSquare1 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval -= (outpost_12x8[board.flipSquare[1][minorPieces[1][x]->GetSquareNumber()]] *
				         minorPieces[1][x]->GetType().gameInt1) >> 1;
			if( board.GetSquareContents( pawnSquare2 ) != NULL &&
				board.GetSquareContents( pawnSquare2 )->GetType().IsPawn() )
				//	additional 50% bonus for this pawn defender
				eval -= (outpost_12x8[board.flipSquare[1][minorPieces[1][x]->GetSquareNumber()]] *
				         minorPieces[1][x]->GetType().gameInt1) >> 1;
		}
	}

	// *** ROOK ON OPEN and SEMI-OPEN FILE BONUS *** //
	if( pPawnHash != NULL )
	{
		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 += 50;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][rooks[0][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 20;
		}
		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 += 50;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][rooks[0][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 20;
		}
	}
	if( pPawnHash != NULL )
	{
		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 -= 50;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][rooks[1][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 20;
		}
		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 -= 50;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][rooks[1][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 20;
		}
	}
	return *currentPhase;
}

word32 CourierChessGame::AdjustPrimaryHash
	( word32 primaryHash )
{
	return primaryHash;
}

void CourierChessGame::DefaultSettings()
{
	squareColor1 = RGB(255, 255, 220);
	squareColor2 = RGB(150, 175, 161);
	pieceColor1 = RGB(255, 255, 255);
	pieceColor2 = RGB(0, 0, 0);
	borderColor = RGB(108, 0, 0);
	boardDisplayType = BOARD_IS_CHECKERED;
	selectedPieceSet = PIECE_SET_STANDARD;
}
