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

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


Hashtable::Hashtable
	( Board &theBoard,
	  unsigned sizeInMB ):
		board(theBoard)
{
	table = NULL;
	SetSize( sizeInMB );
}

Hashtable::~Hashtable()
{
	if( table != NULL )
		delete[] table;
}

void Hashtable::SetSize
	( unsigned sizeInMB )
{
	unsigned long int element_size = sizeof(Hash);
	tableSize = 1;
	int i = 1;

	if( table != NULL )
		delete[] table;

	if( sizeInMB == 0 )
		table = NULL;
	else
	{
		/*	compute the maximum hash element, based upon size desired  */
		while( tableSize * element_size <= sizeInMB << 19 )
			tableSize = 1 << i++;

		/*	compute the hash mask, for computing hash table indices  */
		hashMask = (tableSize - 1) & ~1;

		/*	allocate our hash table's memory, and report on the allocation  */
		table = new Hash[tableSize];
		if( table == NULL )
		{
			ASSERT(false);
			//	TO DO: exit on failure
		}
	}
}

void Hashtable::Store
	( int depth, 
	  int score, 
	  HashType hashType,
	  Movement move )
{
	if( table == NULL )
		return;

	//	store a position into the hash table
	Hash *pHash;

	//	look for the correct place to put the hash
	pHash = &(table[hashMask & board.GetPrimaryHash()]);

	//	don't replace if the info in the hash table is more
	//	accurate than what we have now
	if( pHash->GetType() != NoHash && depth < pHash->GetDepth() )
		pHash = &(table[(hashMask & board.GetPrimaryHash()) | 1]);
	else
	{
		Hash *moveTo = &(table[(hashMask & board.GetPrimaryHash()) | 1]);
		moveTo->primaryHash = pHash->primaryHash;
		moveTo->secondaryHash = pHash->secondaryHash;
		moveTo->depth = pHash->depth;
		moveTo->move = pHash->move;
		moveTo->score = pHash->score;
		moveTo->type = pHash->type;
	}

	//	adjust for mate scores
	if( score > INFINITY - 100 ) 
		score += board.GetCurrentDepth();
	else if( score < -INFINITY + 100 ) 
		score -= board.GetCurrentDepth();

	//	store our hash info
	pHash->primaryHash = board.GetPrimaryHash();
	pHash->secondaryHash = board.GetSecondaryHash();
	pHash->depth = depth;
	pHash->move = move;
	pHash->score = score;
	pHash->type = hashType;
}

HashType Hashtable::Lookup
	( int depthRemaining, 
	  int currentDepth,
	  int &score,
	  Movement &move )
{
	if( table == NULL )
		return NoHash;

	//	lookup current position in the hash table
	Hash *pHash = &(table[hashMask & board.GetPrimaryHash()]);
	STAT_INCREMENT(ttLookupCount)

	if( pHash->GetType() == NoHash || 
		pHash->GetPrimaryHash() != board.GetPrimaryHash() ||
		pHash->GetSecondaryHash() != board.GetSecondaryHash() )
		//	position not in first slot (the replace if deeper
		//	slot), so check the second slot (always replace)
		pHash = &(table[(hashMask & board.GetPrimaryHash()) | 1]);

	if( pHash->GetType() != NoHash &&
		pHash->GetPrimaryHash() == board.GetPrimaryHash() &&
		pHash->GetSecondaryHash() == board.GetSecondaryHash() )
	{
		STAT_INCREMENT(ttLookupMatchCount)
		score = pHash->GetScore();
		move = pHash->GetMove();

		if( pHash->GetDepth() == 0 )
			return Quiescent;

		if( pHash->GetDepth() < depthRemaining && !MATESCORE(score) )
			return PoorDraft;
		
		//	adjust our score if it is a mate score
		if( score > INFINITY - MAX_DEPTH ) 
			score -= board.GetCurrentDepth();
		else if( score < -INFINITY + MAX_DEPTH ) 
			score += board.GetCurrentDepth();

		return pHash->GetType();
	}

	return NoHash;
}

void Hashtable::ClearAll()
{
	for( unsigned long l = 0; l < tableSize; l++ )
		table[l].type = NoHash;
}
