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

                                 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 "Board.h"
#include "Piece.h"
#include "PawnHashtable.h"
#include "Statistics.h"


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


PawnHashtable::PawnHashtable
	( Board &theBoard, 
	  unsigned sizeInMB ):
		board(theBoard)
{
	int i = 1;
	unsigned long int max_hash = 1;
	unsigned long int element_size = sizeof(PawnHash);

	//	compute the maximum hash element, based upon size desired
	while( max_hash * element_size <= sizeInMB << 19 )
		max_hash = 1 << i++;

	//	compute the hash mask, for computing hash table indices
	hashMask = max_hash - 1;

	//	allocate hash table memory, and report on the allocation
	table = new PawnHash[max_hash];
	if( table == NULL )
	{
		ASSERT(false);
		//	TO DO: exit on failure
	}
}

int PawnHashtable::Lookup
	( PawnHash **ppPawnHashOut )
{
	/*	look-up our hash  */
	PawnHash *pHash = table + (hashMask & board.GetPrimaryPawnHash());
	STAT_INCREMENT(phtLookupCount);
	
	if( pHash->IsValid() &&
		pHash->GetPrimaryHash() == board.GetPrimaryPawnHash() &&
		pHash->GetSecondaryHash() == board.GetSecondaryPawnHash() )
	{
		STAT_INCREMENT(phtMatchCount);
		*ppPawnHashOut = pHash;
		return pHash->GetScore();
	}
	else
	{
		//	this position doesn't exist in our pawn hashtable, so
		//	we need to evaluate it now, and store the information
		int eval = EvaluatePawnStructure( pHash );
		pHash->primaryHash = board.GetPrimaryPawnHash();
		pHash->secondaryHash = board.GetSecondaryPawnHash();
		pHash->score = eval;
		pHash->hashType = ValidHash;
		*ppPawnHashOut = pHash;
		return eval;
	}
}

int PawnHashtable::EvaluatePawnStructure
	( PawnHash *pHash )
{
	int eval = 0;
	int nFiles = board.GetNumberOfFiles();

	//	arrays to store the number of pawns each player has on each file
	int player0pawns[14];
	int player1pawns[14];
	int player0backPawn[14];
	int player1backPawn[14];

	for( int file = 0; file < nFiles + 2; file++ )
	{
		player0pawns[file] = 0;
		player1pawns[file] = 0;
		player0backPawn[file] = 128;
		player1backPawn[file] = 0;
	}

	//	loop through all player0's pawns
	for( int pawnCount = 0; pawnCount < board.nPawns[0]; pawnCount++ )
	{
		Piece *pawn = board.pawns[0][pawnCount];
		if( !pawn->IsCaptured() )
		{
			int pawnFile = pawn->GetSquareNumber() % nFiles;
			int pawnRank = pawn->GetSquareNumber() / nFiles;
			player0pawns[pawnFile + 1]++;
			if( pawnRank < player0backPawn[pawnFile + 1] )
				player0backPawn[pawnFile + 1] = pawnRank;
		}
	}

	//	loop through all player1's pawns
	for( int pawnCount = 0; pawnCount < board.nPawns[1]; pawnCount++ )
	{
		Piece *pawn = board.pawns[1][pawnCount];
		if( !pawn->IsCaptured() )
		{
			int pawnFile = pawn->GetSquareNumber() % nFiles;
			int pawnRank = pawn->GetSquareNumber() / nFiles;
			player1pawns[pawnFile + 1]++;
			if( pawnRank > player1backPawn[pawnFile + 1] )
				player1backPawn[pawnFile + 1] = pawnRank;
		}
	}

	for( int pawnCount = 0; pawnCount < board.nPawns[0]; pawnCount++ )
	{
		Piece *pawn = board.pawns[0][pawnCount];
		if( !pawn->IsCaptured() )
		{
			int pawnFile = pawn->GetSquareNumber() % nFiles;
			int pawnRank = pawn->GetSquareNumber() / nFiles;
			bool isolated = false;
			bool backward = false;

			if( player0pawns[pawnFile] == 0 && 
				player0pawns[pawnFile + 2] == 0 )
			{
				//	isolated pawn
				eval -= (int) 5;
				isolated = true;
			}
			if( player0backPawn[pawnFile] > pawnRank && 
				player0backPawn[pawnFile + 2] > pawnRank )
			{
				//	backward pawn
				eval -= (int) 5;
				backward = true;
			}
			if( player1pawns[pawnFile + 1] == 0 )
			{
				//	penalize weak, exposed pawns
				if( backward )
					eval -= 2;
				if( isolated )
					eval -= 2;
			}
			if( player0backPawn[pawnFile + 1] < pawnRank )
			{
				//	doubled, trippled, etc.
				eval -= 3;
			}
			if( pawnRank >= player1backPawn[pawnFile + 1] &&
				pawnRank >= player1backPawn[pawnFile] &&
				pawnRank >= player1backPawn[pawnFile + 2] )
			{
				//	passed pawn
				eval += 6;
				if( !isolated )
					eval += 2;
			}
		}
	}

	for( int pawnCount = 0; pawnCount < board.nPawns[1]; pawnCount++ )
	{
		Piece *pawn = board.pawns[1][pawnCount];
		if( !pawn->IsCaptured() )
		{
			int pawnFile = pawn->GetSquareNumber() % nFiles;
			int pawnRank = pawn->GetSquareNumber() / nFiles;
			bool isolated = false;
			bool backward = false;

			if( player1pawns[pawnFile] == 0 && 
				player1pawns[pawnFile + 2] == 0 )
			{
				//	isolated pawn
				eval += (int) 5;
				isolated = true;
			}
			if( player1backPawn[pawnFile] < pawnRank && 
				player1backPawn[pawnFile + 2] < pawnRank )
			{
				//	backward pawn
				eval += (int) 5;
				backward = true;
			}
			if( player0pawns[pawnFile + 1] == 0 )
			{
				//	penalize weak, exposed pawns
				if( backward )
					eval += 2;
				if( isolated )
					eval += 2;
			}
			if( player1backPawn[pawnFile + 1] > pawnRank )
			{
				//	doubled, trippled, etc.
				eval += 3;
			}
			if( pawnRank <= player0backPawn[pawnFile + 1] &&
				pawnRank <= player0backPawn[pawnFile] &&
				pawnRank <= player0backPawn[pawnFile + 2] )
			{
				//	passed pawn
				eval -= 6;
				if( !isolated )
					eval -= 2;
			}
		}
	}

	//	store information into the pawn hash about which
	//	files are open, and where the backmost pawns are;
	//	this is important for giving bonuses to rooks on 
	//	open files, and determining whether a knight or
	//	bishop is in an 'outpost' (or whatever else a game may need)
	//	NOTE: this is unrolled and not looped for maximum speed
	pHash->nPawnsPerFile[0][0] = (char) player0pawns[1];
	pHash->nPawnsPerFile[0][1] = (char) player0pawns[2];
	pHash->nPawnsPerFile[0][2] = (char) player0pawns[3];
	pHash->nPawnsPerFile[0][3] = (char) player0pawns[4];
	pHash->nPawnsPerFile[0][4] = (char) player0pawns[5];
	pHash->nPawnsPerFile[0][5] = (char) player0pawns[6];
	pHash->nPawnsPerFile[0][6] = (char) player0pawns[7];
	pHash->nPawnsPerFile[0][7] = (char) player0pawns[8];
	pHash->nPawnsPerFile[0][8] = (char) player0pawns[9];
	pHash->nPawnsPerFile[0][9] = (char) player0pawns[10];
	pHash->nPawnsPerFile[0][10] = (char) player0pawns[11];
	pHash->nPawnsPerFile[0][11] = (char) player0pawns[12];
	pHash->nPawnsPerFile[1][0] = (char) player1pawns[1];
	pHash->nPawnsPerFile[1][1] = (char) player1pawns[2];
	pHash->nPawnsPerFile[1][2] = (char) player1pawns[3];
	pHash->nPawnsPerFile[1][3] = (char) player1pawns[4];
	pHash->nPawnsPerFile[1][4] = (char) player1pawns[5];
	pHash->nPawnsPerFile[1][5] = (char) player1pawns[6];
	pHash->nPawnsPerFile[1][6] = (char) player1pawns[7];
	pHash->nPawnsPerFile[1][7] = (char) player1pawns[8];
	pHash->nPawnsPerFile[1][8] = (char) player1pawns[9];
	pHash->nPawnsPerFile[1][9] = (char) player1pawns[10];
	pHash->nPawnsPerFile[1][10] = (char) player1pawns[11];
	pHash->nPawnsPerFile[1][11] = (char) player1pawns[12];
	pHash->backPawnRank[0][0] = (char) player0backPawn[1];
	pHash->backPawnRank[0][1] = (char) player0backPawn[2];
	pHash->backPawnRank[0][2] = (char) player0backPawn[3];
	pHash->backPawnRank[0][3] = (char) player0backPawn[4];
	pHash->backPawnRank[0][4] = (char) player0backPawn[5];
	pHash->backPawnRank[0][5] = (char) player0backPawn[6];
	pHash->backPawnRank[0][6] = (char) player0backPawn[7];
	pHash->backPawnRank[0][7] = (char) player0backPawn[8];
	pHash->backPawnRank[0][8] = (char) player0backPawn[9];
	pHash->backPawnRank[0][9] = (char) player0backPawn[10];
	pHash->backPawnRank[0][10] = (char) player0backPawn[11];
	pHash->backPawnRank[0][11] = (char) player0backPawn[12];
	pHash->backPawnRank[1][0] = (char) player1backPawn[1];
	pHash->backPawnRank[1][1] = (char) player1backPawn[2];
	pHash->backPawnRank[1][2] = (char) player1backPawn[3];
	pHash->backPawnRank[1][3] = (char) player1backPawn[4];
	pHash->backPawnRank[1][4] = (char) player1backPawn[5];
	pHash->backPawnRank[1][5] = (char) player1backPawn[6];
	pHash->backPawnRank[1][6] = (char) player1backPawn[7];
	pHash->backPawnRank[1][7] = (char) player1backPawn[8];
	pHash->backPawnRank[1][8] = (char) player1backPawn[9];
	pHash->backPawnRank[1][9] = (char) player1backPawn[10];
	pHash->backPawnRank[1][10] = (char) player1backPawn[11];
	pHash->backPawnRank[1][11] = (char) player1backPawn[12];


	return eval;
}
