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

                                 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 "FloatVector.h"
#include "Games/ChessGames/ChessGame.h"
#include "MovementList.h"
#include "GameParameters.h"
#include "ChessV.h"
#include "Phase.h"
#include "Statistics.h"
#include "GUI/Pen.h"
#include "GUI/Brush.h"


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


Board::Board
	( int ranks, 
	  int files ):
		hashTable(*this, transpositionTableSize),
		pawnHashTable(*this)
{
	pEvalHashtable = new EvalHashtable( *this, evaluationCacheSize );
	nRanks = ranks;
	nFiles = files;
	nSquares = ranks * files;
	primaryHash = 0;
	secondaryHash = 0;
	primaryPawnHash = 0;
	secondaryPawnHash = 0;
	currentDepth = 1;
	currentPlayer = 0;
	maximumDepth = 40;
	pieceLifted = NULL;
	outfile = NULL;
	thinking = false;
	showStats = false;
	pBook = NULL;
	currentLine = new char[2400];
	openingBookFilename = NULL;
	currentLine[0] = '\0';
	rootCurrentPlayer = 0;
	rootPrimaryHash = 0;
	rootSecondaryHash = 0;
	gamePly = 1;
	abortSearch = false;
	positionTestList.SetBoard( this );
	squareContents = new Piece *[nRanks*nFiles];
	int x;
	for( x = 0; x < nRanks * nFiles; x++ )
		squareContents[x] = NULL;

	pieces[0] = new Piece *[MAX_PIECES];
	pieces[1] = new Piece *[MAX_PIECES];
	kings[0] = NULL;
	kings[1] = NULL;
	nPieces[0] = 0;
	nPieces[1] = 0;
	nCapturedPieces = 0;
	material[0] = 0;
	material[1] = 0;
	pawnMaterial[0] = 0;
	pawnMaterial[1] = 0;
	midgameEvalPST[0] = 0;
	midgameEvalPST[1] = 0;
	rootScore = 0;
	lastRootScore = 0;
	pawns[0] = new Piece *[MAX_PAWNS];
	nPawns[0] = 0;
	pawns[1] = new Piece *[MAX_PAWNS];
	nPawns[1] = 0;

	pieceTypes = new PieceType *[MAX_PIECE_TYPES];
	nPieceTypes = 0;

	playerPieceTypes[0] = new PieceType *[MAX_PIECE_TYPES];
	nPlayerPieceTypes[0] = 0;
	playerPieceTypes[1] = new PieceType *[MAX_PIECE_TYPES];
	nPlayerPieceTypes[1] = 0;

	movementMatrix = new int *[MAX_DIRECTIONS];
	boardWrapsAround = false;
	squareValues.SetSize( nRanks * nFiles );

	//	initialize history look-up table
	for( x = 0; x < MAX_PLY; x++ )
		history[x].SetSize( nSquares * nSquares );

	movementLists = new MovementList[MAX_PLY];
	for( x = 0; x < MAX_PLY; x++ )
		movementLists[x].SetBoard( this );

	for( x = 0; x < MAX_DIRECTIONS; x++ )
	{
		attackRange[0][x] = 0;
		attackRange[1][x] = 0;
	}

	theBoard = this;

	squareWidth  = 56;
	squareHeight = 56;
	borderWidth  = 27;

	evalRandomization[0]  =  -11;
	evalRandomization[1]  =   43;
	evalRandomization[2]  =  -31;
	evalRandomization[3]  =  -19;
	evalRandomization[4]  =   36;
	evalRandomization[5]  =   12;
	evalRandomization[6]  =  -44;
	evalRandomization[7]  =  -13;
	evalRandomization[8]  =   27;
	evalRandomization[9]  =   14;
	evalRandomization[10] =  -47;
	evalRandomization[11] =   48;
	evalRandomization[12] =  -29;
	evalRandomization[13] =   -6;
	evalRandomization[14] =   22;
	evalRandomization[15] =    0;

	if( files == 10 )
	{
		openingMoveEvalForPawnBasedOnFile[0] = -200;
		openingMoveEvalForPawnBasedOnFile[1] = -40;
		openingMoveEvalForPawnBasedOnFile[2] = -10;
		openingMoveEvalForPawnBasedOnFile[3] = 55;
		openingMoveEvalForPawnBasedOnFile[4] = 150;
		openingMoveEvalForPawnBasedOnFile[5] = 150;
		openingMoveEvalForPawnBasedOnFile[6] = 55;
		openingMoveEvalForPawnBasedOnFile[7] = -10;
		openingMoveEvalForPawnBasedOnFile[8] = -40;
		openingMoveEvalForPawnBasedOnFile[9] = -200;
	}
	else if( files == 8 )
	{
		openingMoveEvalForPawnBasedOnFile[0] = -100;
		openingMoveEvalForPawnBasedOnFile[1] = -10;
		openingMoveEvalForPawnBasedOnFile[2] = 55;
		openingMoveEvalForPawnBasedOnFile[3] = 150;
		openingMoveEvalForPawnBasedOnFile[4] = 150;
		openingMoveEvalForPawnBasedOnFile[5] = 55;
		openingMoveEvalForPawnBasedOnFile[6] = -10;
		openingMoveEvalForPawnBasedOnFile[7] = -100;
	}
	else if( files == 6 )
	{
		openingMoveEvalForPawnBasedOnFile[0] = -80;
		openingMoveEvalForPawnBasedOnFile[1] = 35;
		openingMoveEvalForPawnBasedOnFile[2] = 100;
		openingMoveEvalForPawnBasedOnFile[3] = 100;
		openingMoveEvalForPawnBasedOnFile[4] = 35;
		openingMoveEvalForPawnBasedOnFile[5] = -80;
	}
	else if( files == 12 )
	{
		openingMoveEvalForPawnBasedOnFile[0] = -200;
		openingMoveEvalForPawnBasedOnFile[1] = -40;
		openingMoveEvalForPawnBasedOnFile[2] = -10;
		openingMoveEvalForPawnBasedOnFile[3] = 55;
		openingMoveEvalForPawnBasedOnFile[4] = 85;
		openingMoveEvalForPawnBasedOnFile[5] = 150;
		openingMoveEvalForPawnBasedOnFile[6] = 150;
		openingMoveEvalForPawnBasedOnFile[7] = 55;
		openingMoveEvalForPawnBasedOnFile[8] = 85;
		openingMoveEvalForPawnBasedOnFile[9] = -10;
		openingMoveEvalForPawnBasedOnFile[10] = -40;
		openingMoveEvalForPawnBasedOnFile[11] = -200;
	}

	//	set identifiers for ranks and files
	char *parameter;
	if( LookupStringParameter( "files", parameter ) )
	{
		int cursor = 0;
		int len = (int) strlen( parameter );
		for( int x = 0; cursor < len && x < nFiles; x++ )
		{
			//	skip whitespace
			while( cursor < len && 
				(parameter[cursor] == ' ' || parameter[cursor] == '\t') )
				cursor++;
			char id[3];
			if( cursor < len )
			{
				if( (parameter[cursor] >= 'a' && parameter[cursor] <= 'z') || 
					(parameter[cursor] >= 'Z' && parameter[cursor] <= 'Z') || 
					(parameter[cursor] >= '0' && parameter[cursor] <= '9') || 
					 parameter[cursor] == '-' || parameter[cursor] == '_' )
				{
					id[0] = parameter[cursor++];
					if( cursor < len && 
						((parameter[cursor] >= 'a' && parameter[cursor] <= 'z') || 
						 (parameter[cursor] >= 'Z' && parameter[cursor] <= 'Z') || 
						 (parameter[cursor] >= '0' && parameter[cursor] <= '9') || 
						  parameter[cursor] == '-' || parameter[cursor] == '_') )
					{
						id[1] = parameter[cursor++];
						id[2] = '\0';
					}
					else
						id[1] = '\0';
				}
			}
			//	skip whitespace
			while( cursor < len && 
				(parameter[cursor] == ' ' || parameter[cursor] == '\t') )
				cursor++;
			if( parameter[cursor] == ',' )
				cursor++;
			else if( x < nFiles - 1 )
			{
				::MessageBox( theWindow, "Error in $files declaration; ',' expected", 
					"Game Load ERROR", MB_ICONHAND | MB_OK );
				break;
			}
			strcpy( namesOfFiles[x], id );
		}
	}
	else
	{
		for( int f = 0; f < nFiles; f++ )
		{
			namesOfFiles[f][0] = 'a' + f;
			namesOfFiles[f][1] = '\0';
		}
	}
	if( LookupStringParameter( "ranks", parameter ) )
	{
		int cursor = 0;
		int len = (int) strlen( parameter );
		for( int x = 0; cursor < len && x < nRanks; x++ )
		{
			//	skip whitespace
			while( cursor < len && 
				(parameter[cursor] == ' ' || parameter[cursor] == '\t') )
				cursor++;
			char id[3];
			if( cursor < len )
			{
				if( (parameter[cursor] >= 'a' && parameter[cursor] <= 'z') || 
					(parameter[cursor] >= 'Z' && parameter[cursor] <= 'Z') || 
					(parameter[cursor] >= '0' && parameter[cursor] <= '9') || 
					 parameter[cursor] == '-' || parameter[cursor] == '_' )
				{
					id[0] = parameter[cursor++];
					if( cursor < len && 
						((parameter[cursor] >= 'a' && parameter[cursor] <= 'z') || 
						 (parameter[cursor] >= 'Z' && parameter[cursor] <= 'Z') || 
						 (parameter[cursor] >= '0' && parameter[cursor] <= '9') || 
						  parameter[cursor] == '-' || parameter[cursor] == '_') )
					{
						id[1] = parameter[cursor++];
						id[2] = '\0';
					}
					else
						id[1] = '\0';
				}
			}
			//	skip whitespace
			while( cursor < len && 
				(parameter[cursor] == ' ' || parameter[cursor] == '\t') )
				cursor++;
			if( parameter[cursor] == ',' )
				cursor++;
			else if( x < nRanks - 1 )
			{
				::MessageBox( theWindow, "Error in $ranks declaration; ',' expected", 
					"Game Load ERROR", MB_ICONHAND | MB_OK );
				break;
			}
			strcpy( namesOfRanks[x], id );
		}
	}
	else
	{
		for( int r = 0; r < nRanks; r++ )
		{
			sprintf( namesOfRanks[r], "%d", r + 1 );
		}
	}

	//	(re)initialize all piece types
	int nextID = 0;
	PieceType *pType = PieceType::GetFirstType();
	while( pType != NULL )
	{
		pType->SetTypeID( nextID++ );
		pType->Initialize( personality );
		pType = pType->GetNextType();
	}

	//	set primary directions
	directions = new Direction [MAX_DIRECTIONS];
	directions[DIRECTION_N] = Direction(  0,  1 );
	directions[DIRECTION_NE] = Direction(  1,  1 );
	directions[DIRECTION_NW] = Direction( -1,  1 );
	directions[DIRECTION_S] = Direction(  0, -1 );
	directions[DIRECTION_SE] = Direction(  1, -1 );
	directions[DIRECTION_SW] = Direction( -1, -1 );
	directions[DIRECTION_E] = Direction(  1,  0 );
	directions[DIRECTION_W] = Direction( -1,  0 );
	directionCount = 8;

	//	zero out immobilizedSquares matrix
	for( int depth = 0; depth < MAX_DEPTH; depth++ )
		for( int square = 0; square < MAX_SQUARES; square++ )
			immobilizedSquares[depth][square] = 0;

	//	initialize distances, ranks, files
	for( int x = 0; x < nSquares; x++ )
	{
		int fromRank = x / nFiles;
		int fromFile = x % nFiles;
		flipSquare[0][x] = x;
		flipSquare[1][x] = nSquares - x - 1;
		mirrorSquare[0][x] = x;
		mirrorSquare[1][x] = (nRanks - fromRank - 1) * nFiles + fromFile;
		this->ranks[0][x] = fromRank;
		this->ranks[1][x] = nRanks - fromRank - 1;
		this->files[0][x] = fromFile;
		this->files[1][x] = GetNumberOfFiles() - fromFile - 1;
		for( int y = 0; y < nSquares; y++ )
		{
			int toRank = y / nFiles;
			int toFile = y % nFiles;
			distance[x][y] = MAX(MAX(fromRank-toRank,toRank-fromRank),
				                 MAX(fromFile-toFile,toFile-fromFile));
			hv_distance[x][y] = MIN(MAX(fromRank-toRank,toRank-fromRank),
				                    MAX(fromFile-toFile,toFile-fromFile));
		}
	}

	::InvalidateRect( theWindow, NULL, false );
}

void Board::AddPieceToBoard
	( Piece &piece, 
	  int rank, 
	  int file )
{
	int squareNumber = rank * GetNumberOfFiles() + file;
	int playerNumber = piece.GetPlayerNumber();

	SetSquareContents( squareNumber, &piece );
	pieces[playerNumber][nPieces[playerNumber]++] = &piece;
	piece.SetBoard( *this, rank, file );
	material[piece.GetPlayerNumber()] += piece.GetBaseValue();
	if( piece.IsPawn() )
		pawnMaterial[piece.GetPlayerNumber()] += piece.GetBaseValue();
	//midgameEvalPST[piece.GetPlayerNumber()] += 

	//	see if we've already got this piece type,
	//	and add it if not, using helper function AddPieceType()
	bool found = false;
	PieceType &pieceType = piece.GetType();
	int x;
	for( x = 0; x < nPieceTypes && !found; x++ )
	{
		if( (*pieceTypes[x]) == pieceType )
			found = true;
	}
	if( !found )
		AddPieceType( pieceType );

	if( pieceType.IsRoyal() )
		//	the King, keep a reference to this piece
		kings[piece.GetPlayerNumber()] = &piece;

	if( pieceType.IsPawn() )
		//	a pawn, add to the pawns array
		pawns[piece.GetPlayerNumber()][(nPawns[piece.GetPlayerNumber()])++] = &piece;

	//	check the playerPieceTypes array to see if this is a
	//	new type for this player
	found = false;
	for( x = 0; x < nPlayerPieceTypes[playerNumber] && !found; x++ )
	{
		if( (*playerPieceTypes[playerNumber][x]) == piece.GetType() )
			found = true;
	}
	if( !found )
	{
		playerPieceTypes[playerNumber][nPlayerPieceTypes[playerNumber]] = &piece.GetType();
		nPlayerPieceTypes[playerNumber]++;
	}

	// *** BITBOARDS *** //

	switch( bitBoardType )
	{
	  case BITBOARD_64:
		static_cast<PieceType64 &>(piece.GetType()).SetPiece( piece.GetPlayerNumber(), squareNumber );
		bb64_blocker.SetBit( squareNumber );
		bb64_blocker90.SetBit( rotate90[squareNumber] );
		bb64_blocker45.SetBit( rotate45[squareNumber] );
		bb64_blocker135.SetBit( rotate135[squareNumber] );
		bb64_friends[piece.GetPlayerNumber()].SetBit( squareNumber );
		break;

	  case BITBOARD_96:
		static_cast<PieceType96 &>(piece.GetType()).SetPiece( piece.GetPlayerNumber(), squareNumber );
		bb96_blocker.SetBit( squareNumber );
		bb96_blocker90.SetBit( rotate90[squareNumber] );
		bb96_blocker45.SetBit( rotate45[squareNumber] );
		bb96_blocker135.SetBit( rotate135[squareNumber] );
		bb96_friends[piece.GetPlayerNumber()].SetBit( squareNumber );
		break;

	  case BITBOARD_128:
		static_cast<PieceType128 &>(piece.GetType()).SetPiece( piece.GetPlayerNumber(), squareNumber );
		bb128_blocker.SetBit( squareNumber );
		bb128_blocker90.SetBit( rotate90[squareNumber] );
		bb128_blocker45.SetBit( rotate45[squareNumber] );
		bb128_blocker135.SetBit( rotate135[squareNumber] );
		bb128_friends[piece.GetPlayerNumber()].SetBit( squareNumber );
		break;

	  case BITBOARD_NONE:
		break;
	}
}

void Board::AddPieceType
	( PieceType &pieceType )
{
	pieceTypes[nPieceTypes++] = &pieceType;

	//	see if we have all these movement directions yet, and add
	//	them if we haven't
	for( int x = 0; x < pieceType.GetMoveCapabilityCount(); x++ )
	{
		MovementCapability &move = pieceType.GetMoveCapability( x );
		bool found = false;
		int y;
		for( y = 0; y < directionCount && !found; y++ )
			if( directions[y] == move )
				found = true;
		if( found )
			move.SetDirectionNumber( y - 1 );
		else
		{
			//	add this direction
			directions[directionCount] = 
				Direction( move.GetFileDifference( 0 ), 
				           move.GetRankDifference( 0 ) );
			move.SetDirectionNumber( directionCount++ );
		}
	}

	//	check this piece's promotion type (if any)
	//	and make sure that we have it too
	if( pieceType.GetPromotion().GetPromotionCapability() == PromoteToSpecificType )
	{
		PieceType &promoteType = pieceType.GetPromotion().GetTypeToPromoteTo();

		//	check to see if we have this type already
		bool found = false;
		for( int i = 0; i < nPieceTypes; i++ )
			if( promoteType == *(pieceTypes[i]) )
				found = true;
		if( !found )
			AddPieceType( promoteType );
	}
}

bool Board::RemovePieceType
	( PieceType &pieceType )
{
	bool found = false;
	int x;

	//	remove from pieceTypes array
	for( x = 0; !found && x < nPieceTypes; x++ )
	{
		if( pieceTypes[x] == &pieceType )
		{
			found = true;
			if( x == nPieceTypes - 1 )
				nPieceTypes--;
			else
				pieceTypes[x] = pieceTypes[--nPieceTypes];
		}
	}
	if( !found )
		return false;

	//	remove from playerPieceTypes
	int nPlayer;
	for( nPlayer = 0; nPlayer < 2; nPlayer++ )
	{
		found = false;
		for( x = 0; !found && x < nPlayerPieceTypes[nPlayer]; x++ )
		{
			if( playerPieceTypes[nPlayer][x] == &pieceType )
			{
				found = true;
				if( x == nPlayerPieceTypes[nPlayer] - 1 )
					nPlayerPieceTypes[nPlayer]--;
				else
					playerPieceTypes[nPlayer][x] = playerPieceTypes[nPlayer][--nPlayerPieceTypes[nPlayer]];
			}
		}
	}
	return true;
}

void Board::AddPieceTypeIfNecessary( PieceType &pieceType )
{
	//	see if we've already got this piece type,
	//	and add it if not, using helper function AddPieceType()
	bool found = false;
	for( int x = 0; x < nPieceTypes && !found; x++ )
	{
		if( (*pieceTypes[x]) == pieceType )
			found = true;
	}
	if( !found )
		AddPieceType( pieceType );
}

void Board::AddPlayerPieceType
	( PieceType &pieceType, int player )
{
	bool found;
	int x;
	for( x = 0, found = false; x < nPlayerPieceTypes[player]; x++ )
		if( &pieceType == playerPieceTypes[player][x] )
			found = true;
	if( !found )
		playerPieceTypes[player][nPlayerPieceTypes[player]++] = &pieceType;
	AddPieceTypeIfNecessary( pieceType );
}

void Board::AddPlayerPieceTypeBothPlayers
	( PieceType &pieceType )
{
	AddPlayerPieceType( pieceType, 0 );
	AddPlayerPieceType( pieceType, 1 );
	AddPieceTypeIfNecessary( pieceType );
}

Board::~Board()
{
	delete[] squareContents;
	for( int x = 0; x < nPieces[0]; x++ )
		delete pieces[0][x];
	for( int y = 0; y < nPieces[1]; y++ )
		delete pieces[1][y];
	delete[] pieces[0];
	delete[] pieces[1];

	if( pEvalHashtable != NULL )
		delete pEvalHashtable;
	
	if( openingBookFilename != NULL )
		delete[] openingBookFilename;

	delete[] pieceTypes;
	delete[] directions;

	for( int i = 0; i < directionCount; i++ )
		delete[] movementMatrix[i];
	delete[] movementMatrix;

	if( pBook != NULL )
		delete pBook;
}

void Board::RecalculateHashes()
{
	primaryHash = 0;
	secondaryHash = 0;
	primaryPawnHash = 0;
	secondaryPawnHash = 0;

	for( int player = 0; player < 2; player++ )
	{
		for( int i = 0; i < nPieces[player]; i++ )
		{
			Piece &piece = *(pieces[player][i]);

			if( !piece.IsCaptured() )
			{
				//	xor with hash values
				primaryHash = primaryHash ^ piece.GetPrimaryHash();
				secondaryHash = secondaryHash ^ piece.GetSecondaryHash();
				if( piece.IsPawn() )
				{
					primaryPawnHash = primaryPawnHash ^ piece.GetPrimaryHash();
					secondaryPawnHash = secondaryPawnHash ^ piece.GetSecondaryHash();
				}
			}
		}
	}
}

void Board::Initialize
	( Game *game, 
	  int bitboardType )
{
	pGame = game;
	historicalMoves.SetBoard( this );
	legalUserMoves.SetBoard( this );
	legalUserMoves.CheckMovesForLegality();
	movementLists[1].CheckMovesForLegality();


	// *********************************
	// ***                           ***
	// ***  BitBoard initialization  ***
	// ***                           ***
	// *********************************

	bitBoardType = bitboardType;

	switch( bitBoardType )
	{
	  case BITBOARD_64:
		BitBoard64::InitializeData( nRanks, nFiles );
		{
			int file;
			int tosq;
			for( file = 0; file < nFiles; file++ )
			{
				bb64_files[file].Clear();
				for( tosq = 0; tosq < nSquares; tosq++ )
				{
					int tofile = tosq % nFiles;
					if( file == tofile )
						bb64_files[file].SetBit( tosq );
				}
			}
		}
		break;

	  case BITBOARD_96:
		BitBoard96::InitializeData( nRanks, nFiles );
		break;

	  case BITBOARD_128:
		BitBoard128::InitializeData( nRanks, nFiles );
		break;
	}

	//	zero rotation
	int x;
	for( x = 0; x < nSquares; x++ )
	{
		int nFile = this->files[0][x];
		int nRank = this->ranks[0][x];
		shift00[x] = nRank * nFiles;
		mask00[x] = (1 << nFiles) - 1;
	}

	//	90-degree rotation
	int position = 0;
	for( int file = 0; file < nFiles; file++ )
	{
		for( int rank = 0; rank < nRanks; rank++ )
		{
			int nSquare = rank * nFiles + file;
			rotate90[nSquare] = position++;
			shift90[nSquare] = file * nRanks;
			mask90[nSquare] = (1 << nRanks) - 1;
		}
	}

	//	45-degree rotation
	position = 0;
	int shift = 0;
	for( int rank = nRanks - 1; rank >= 0; rank-- )
	{
		int currentRank = rank;
		int currentFile = 0;
		int nSquare = currentRank * nFiles + currentFile;
		while( currentRank < nRanks && currentFile < nFiles )
		{
			rotate45[nSquare] = position;
			shift45[nSquare] = shift;
			mask45[nSquare] = (1 << MIN(nRanks - rank, nFiles)) - 1;
			position++;
			currentRank++;
			currentFile++;
			nSquare = currentRank * nFiles + currentFile;
		}
		shift += MIN(nRanks - rank, nFiles);
	}
	for( int file = 1; file < nFiles; file++ )
	{
		int currentRank = 0;
		int currentFile = file;
		int nSquare = currentRank * nFiles + currentFile;
		while( currentRank < nRanks && currentFile < nFiles )
		{
			rotate45[nSquare] = position;
			shift45[nSquare] = shift;
			mask45[nSquare] = (1 << MIN(nFiles - file, nRanks)) - 1;
			position++;
			currentRank++;
			currentFile++;
			nSquare = currentRank * nFiles + currentFile;
		}
		shift += MIN(nFiles - file, nRanks);
	}

	//	135-degree rotation
	position = 0;
	shift = 0;
	for( int rank = nRanks - 1; rank >= 0; rank-- )
	{
		int currentRank = rank;
		int currentFile = nFiles - 1;
		int nSquare = currentRank * nFiles + currentFile;
		while( currentRank < nRanks && currentFile >= 0 )
		{
			rotate135[nSquare] = position;
			shift135[nSquare] = shift;
			mask135[nSquare] = (1 << MIN(nRanks - rank, nFiles)) - 1;
			position++;
			currentRank++;
			currentFile--;
			nSquare = currentRank * nFiles + currentFile;
		}
		shift += MIN(nRanks - rank, nFiles);
	}
	for( int file = nFiles - 2; file >= 0; file-- )
	{
		int currentRank = 0;
		int currentFile = file;
		int nSquare = currentRank * nFiles + currentFile;
		while( currentRank < nRanks && currentFile >= 0 )
		{
			rotate135[nSquare] = position;
			shift135[nSquare] = shift;
			mask135[nSquare] = (1 << MIN(file + 1, nRanks)) - 1;
			position++;
			currentRank++;
			currentFile--;
			nSquare = currentRank * nFiles + currentFile;
		}
		shift += MIN(file + 1, nRanks);
	}
	if( bitBoardType == BITBOARD_64 )
		InitializeBitBoard64Data();
	if( bitBoardType == BITBOARD_96 )
		InitializeBitBoard96Data();
	if( bitBoardType == BITBOARD_128 )
		InitializeBitBoard128Data();
}

void Board::PostInitialize()
{
	//	initialize direction opposites
	int x;
	for( x = 0; x < directionCount; x++ )
	{
		Direction &direction = directions[x];
		int oppositeRank = -direction.GetRankDifference();
		int oppositeFile = -direction.GetFileDifference();
		bool found = false;
		for( int y = 0; y < directionCount && !found; y++ )
		{
			Direction &testDir = directions[y];
			if( testDir.GetRankDifference() == oppositeRank &&
				testDir.GetFileDifference() == oppositeFile )
			{
				direction.SetOpposite( testDir );
				direction.SetOppositeDirectionNumber( y );
				found = true;
			}
		}
		if( !found )
		{
			directions[directionCount] = Direction( oppositeFile, oppositeRank );
			directions[directionCount].SetOppositeDirectionNumber( x );
			directions[directionCount].SetOpposite( direction );
			direction.SetOpposite( directions[directionCount] );
			direction.SetOppositeDirectionNumber( directionCount );
			directionCount++;
		}
	}

	//	initialize movement matricies 
	for( x = 0; x < directionCount; x++ )
	{
		Direction &direction = directions[x];
		ASSERT(direction.GetOppositeDirectionNumber() >= 0 && direction.GetOppositeDirectionNumber() < directionCount);
		movementMatrix[x] = new int[GetNumberOfSquares()];
		for( int rank = 0; rank < nRanks; rank++ )
		{
			for( int file = 0; file < nFiles; file++ )
			{
				int squareNumber = rank * GetNumberOfFiles() + file;
				int newRank = rank + direction.GetRankDifference();
				int newFile = file + direction.GetFileDifference();
				bool stillOnBoard = newRank >= 0 && newRank < nRanks &&
					                newFile >= 0 && newFile < nFiles;
				if( stillOnBoard )
				{
					int nextSquareNumber = newRank * GetNumberOfFiles() + newFile;
					movementMatrix[x][squareNumber] = nextSquareNumber;
				}
				else
					movementMatrix[x][squareNumber] = -1;
			}
		}
	}

	//	initialize direction look-up matrix
	for( int from = 0; from < nSquares; from++ )
		for( int to = 0; to < nSquares; to++ )
		{
			directionLookup[from][to][0] = -1;
			directionLookup[from][to][1] = -1;
			directionLookup[from][to][2] = -1;
			directionLookup[from][to][3] = -1;
			int directionsMatched = 0;

			for( int x = 0; x < directionCount; x++ )
			{
				Direction &dir = directions[x];
				int *matrix = movementMatrix[x];
				int square = matrix[from];
				while( square != -1 && square != from )
				{
					if( square == to )
					{
						ASSERT(directionsMatched < 4);
						directionLookup[from][to][directionsMatched++] = x;
						square = -1;
					}
					else
						square = matrix[square];
				}
			}
		}

	// *** INITIALIZE PIECE TYPES *** //
	for( x = 0; x < nPieceTypes; x++ )
	{
		pieceTypes[x]->SetBoard( this );
		switch( bitBoardType )
		{
			case BITBOARD_64:
			{
				PieceType64 *pPT = dynamic_cast<PieceType64 *>(pieceTypes[x]);
				if( pPT != NULL )
					pPT->BB64_Initialize();
				break;
			}

			case BITBOARD_96:
			{
				PieceType96 *pPT = dynamic_cast<PieceType96 *>(pieceTypes[x]);
				if( pPT != NULL )
					pPT->BB96_Initialize();
				break;
			}

			case BITBOARD_128:
			{
				PieceType128 *pPT = dynamic_cast<PieceType128 *>(pieceTypes[x]);
				if( pPT != NULL )
					pPT->BB128_Initialize();
				break;
			}
		}
	}

	//	set max attack range for player0
	for( x = 0; x < nPieceTypes; x++ )
	{
		PieceType *pType = pieceTypes[x];
		for( int y = 0; y < pType->GetMoveCapabilityCount(); y++ )
		{
			MovementCapability &move = pType->GetMoveCapability( y );
			if( (move.CanCapture() || move.CanIguiCapture()) && 
				 move.GetMaxSteps() > attackRange[0][move.GetDirectionNumber()] )
				 attackRange[0][move.GetDirectionNumber()] = move.GetMaxSteps();
		}
	}
	
	//	set max attack range for player1
	for( x = 0; x < nPieceTypes; x++ )
	{
		PieceType *pType = pieceTypes[x];
		for( int y = 0; y < pType->GetMoveCapabilityCount(); y++ )
		{
			MovementCapability &move = pType->GetMoveCapability( y );
			int oppositeDir = directions[move.GetDirectionNumber()].GetOppositeDirectionNumber();
			if( (move.CanCapture() || move.CanIguiCapture()) && 
				 move.GetMaxSteps() > attackRange[1][oppositeDir] )
				 attackRange[1][oppositeDir] = move.GetMaxSteps();
		}
	}
	
	//	set the initial positional hashes
	RecalculateHashes();
}

void Board::InitializeBitBoard64Data()
{
	// *** RAYS *** //

	unsigned nSquares = (unsigned) (nRanks * nFiles);
	bb64_rays.SetSize( nSquares * nSquares );
	for( unsigned fromSquare = 0; fromSquare < nSquares; fromSquare++ )
	{
		int fromFile = fromSquare % nFiles;
		int fromRank = fromSquare / nFiles;
		for( unsigned toSquare = 0; toSquare < nSquares; toSquare++ )
		{
			int toFile = toSquare % nFiles;
			int toRank = toSquare / nFiles;
			int ray_index = (fromSquare * nSquares) + toSquare;

			if( fromFile == toFile )
			{
				if( fromRank < toRank )
					for( int x = fromRank + 1; x <= toRank; x++ )
						bb64_rays[ray_index].SetBit( x * nFiles + fromFile );
				else if( fromRank > toRank )
					for( int x = fromRank - 1; x >= toRank; x-- )
						bb64_rays[ray_index].SetBit( x * nFiles + fromFile );
			}
			else if( fromRank == toRank )
			{
				if( fromFile < toFile )
					for( int x = fromFile + 1; x <= toFile; x++ )
						bb64_rays[ray_index].SetBit( fromRank * nFiles + x );
				else if( fromFile > toFile )
					for( int x = fromFile - 1; x >= toFile; x-- )
						bb64_rays[ray_index].SetBit( fromRank * nFiles + x );
			}
			else if( fromRank > toRank && fromRank - toRank == fromFile - toFile )
			{
				for( int delta = 1; delta <= fromRank - toRank; delta++ )
					bb64_rays[ray_index].SetBit( (fromRank - delta) * nFiles + (fromFile - delta) );
			}
			else if( fromRank < toRank && toRank - fromRank == toFile - fromFile )
			{
				for( int delta = 1; delta <= toRank - fromRank; delta++ )
					bb64_rays[ray_index].SetBit( (fromRank + delta) * nFiles + (fromFile + delta) );
			}
			else if( fromRank > toRank && fromRank - toRank == toFile - fromFile )
			{
				for( int delta = 1; delta <= fromRank - toRank; delta++ )
					bb64_rays[ray_index].SetBit( (fromRank - delta) * nFiles + (fromFile + delta) );
			}
			else if( fromRank < toRank && toRank - fromRank == fromFile - toFile )
			{
				for( int delta = 1; delta <= toRank - fromRank; delta++ )
					bb64_rays[ray_index].SetBit( (fromRank + delta) * nFiles + (fromFile - delta) );
			}
		}
	}

	// *** ROOK ATTACK 00 *** //
	int blockerElementCount = 1 << nFiles;
	rook00attackWidth = blockerElementCount;
	bb64_rook00attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = (1 << nFiles) - 1;

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard64 attack;
			int file = fromFile + 1;
			while( file < nFiles )
			{
				attack.SetBit( fromRank * nFiles + file );
				if( blockers & (1 << file) )
					//	we can slide no further in this direction
					file = 9999;
				file++;
			}
			file = fromFile - 1;
			while( file >= 0 )
			{
				attack.SetBit( fromRank * nFiles + file );
				if( blockers & (1 << file) )
					//	we can slide no further in this direction
					file = -1;
				file--;
			}
			bb64_rook00attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}

	// *** ROOK ATTACK 90 *** //
	blockerElementCount = 1 << nRanks;
	rook90attackWidth = blockerElementCount;
	bb64_rook90attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = (1 << nRanks) - 1;

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard64 attack;
			int rank = fromRank + 1;
			while( rank < nRanks )
			{
				attack.SetBit( rank * nFiles + fromFile );
				if( blockers & (1 << rank) )
					//	we can slide no further in this direction
					rank = 9999;
				rank++;
			}
			rank = fromRank - 1;
			while( rank >= 0 )
			{
				attack.SetBit( rank * nFiles + fromFile );
				if( blockers & (1 << rank) )
					//	we can slide no further in this direction
					rank = -1;
				rank--;
			}
			bb64_rook90attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}

	// *** BISHOP ATTACK 45 *** //
	blockerElementCount = 1 << MIN(nRanks, nFiles);
	bishop45attackWidth = blockerElementCount;
	bb64_bishop45attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = mask45[originSquare];

		//	determine bit number of this square
		unsigned bitNumber = 0;
		int file = fromFile;
		int rank = fromRank;
		while( file > 0 && rank > 0 )
		{
			bitNumber++;
			file--;
			rank--;
		}

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard64 attack;
			file = fromFile + 1;
			rank = fromRank + 1;
			int bit = bitNumber + 1;
			while( rank < nRanks && file < nFiles )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = 9999;
				file++;
				rank++;
				bit++;
			}
			file = fromFile - 1;
			rank = fromRank - 1;
			bit = bitNumber - 1;
			while( rank >= 0 && file >= 0 )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = -1;
				file--;
				rank--;
				bit--;
			}
			bb64_bishop45attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}	

	// *** BISHOP ATTACK 135 *** //
	blockerElementCount = 1 << MIN(nRanks, nFiles);
	bishop135attackWidth = blockerElementCount;
	bb64_bishop135attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = mask135[originSquare];

		//	determine bit number of this square
		unsigned bitNumber = 0;
		int file = fromFile;
		int rank = fromRank;
		while( file < nFiles - 1 && rank > 0 )
		{
			bitNumber++;
			file++;
			rank--;
		}

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard64 attack;
			file = fromFile - 1;
			rank = fromRank + 1;
			int bit = bitNumber + 1;
			while( rank < nRanks && file >= 0 )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = 9999;
				file--;
				rank++;
				bit++;
			}
			file = fromFile + 1;
			rank = fromRank - 1;
			bit = bitNumber - 1;
			while( rank >= 0 && file < nFiles  )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					file = 9999;
				file++;
				rank--;
				bit--;
			}
			bb64_bishop135attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}	
}

void Board::InitializeBitBoard96Data()
{
	// *** RAYS *** //

	unsigned nSquares = (unsigned) (nRanks * nFiles);
	bb96_rays.SetSize( nSquares * nSquares );
	for( unsigned fromSquare = 0; fromSquare < nSquares; fromSquare++ )
	{
		int fromFile = fromSquare % nFiles;
		int fromRank = fromSquare / nFiles;
		for( unsigned toSquare = 0; toSquare < nSquares; toSquare++ )
		{
			int toFile = toSquare % nFiles;
			int toRank = toSquare / nFiles;
			int ray_index = (fromSquare * nSquares) + toSquare;

			if( fromFile == toFile )
			{
				if( fromRank < toRank )
					for( int x = fromRank + 1; x <= toRank; x++ )
						bb96_rays[ray_index].SetBit( x * nFiles + fromFile );
				else if( fromRank > toRank )
					for( int x = fromRank - 1; x >= toRank; x-- )
						bb96_rays[ray_index].SetBit( x * nFiles + fromFile );
			}
			else if( fromRank == toRank )
			{
				if( fromFile < toFile )
					for( int x = fromFile + 1; x <= toFile; x++ )
						bb96_rays[ray_index].SetBit( fromRank * nFiles + x );
				else if( fromFile > toFile )
					for( int x = fromFile - 1; x >= toFile; x-- )
						bb96_rays[ray_index].SetBit( fromRank * nFiles + x );
			}
			else if( fromRank > toRank && fromRank - toRank == fromFile - toFile )
			{
				for( int delta = 1; delta <= fromRank - toRank; delta++ )
					bb96_rays[ray_index].SetBit( (fromRank - delta) * nFiles + (fromFile - delta) );
			}
			else if( fromRank < toRank && toRank - fromRank == toFile - fromFile )
			{
				for( int delta = 1; delta <= toRank - fromRank; delta++ )
					bb96_rays[ray_index].SetBit( (fromRank + delta) * nFiles + (fromFile + delta) );
			}
			else if( fromRank > toRank && fromRank - toRank == toFile - fromFile )
			{
				for( int delta = 1; delta <= fromRank - toRank; delta++ )
					bb96_rays[ray_index].SetBit( (fromRank - delta) * nFiles + (fromFile + delta) );
			}
			else if( fromRank < toRank && toRank - fromRank == fromFile - toFile )
			{
				for( int delta = 1; delta <= toRank - fromRank; delta++ )
					bb96_rays[ray_index].SetBit( (fromRank + delta) * nFiles + (fromFile - delta) );
			}
		}
	}

	// *** ROOK ATTACK 00 *** //
	int blockerElementCount = 1 << nFiles;
	rook00attackWidth = blockerElementCount;
	bb96_rook00attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = (1 << nFiles) - 1;

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard96 attack;
			int file = fromFile + 1;
			while( file < nFiles )
			{
				attack.SetBit( fromRank * nFiles + file );
				if( blockers & (1 << file) )
					//	we can slide no further in this direction
					file = 9999;
				file++;
			}
			file = fromFile - 1;
			while( file >= 0 )
			{
				attack.SetBit( fromRank * nFiles + file );
				if( blockers & (1 << file) )
					//	we can slide no further in this direction
					file = -1;
				file--;
			}
			bb96_rook00attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}

	// *** ROOK ATTACK 90 *** //
	blockerElementCount = 1 << nRanks;
	rook90attackWidth = blockerElementCount;
	bb96_rook90attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = (1 << nRanks) - 1;

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard96 attack;
			int rank = fromRank + 1;
			while( rank < nRanks )
			{
				attack.SetBit( rank * nFiles + fromFile );
				if( blockers & (1 << rank) )
					//	we can slide no further in this direction
					rank = 9999;
				rank++;
			}
			rank = fromRank - 1;
			while( rank >= 0 )
			{
				attack.SetBit( rank * nFiles + fromFile );
				if( blockers & (1 << rank) )
					//	we can slide no further in this direction
					rank = -1;
				rank--;
			}
			bb96_rook90attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}

	// *** BISHOP ATTACK 45 *** //
	blockerElementCount = 1 << MIN(nRanks, nFiles);
	bishop45attackWidth = blockerElementCount;
	bb96_bishop45attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = mask45[originSquare];

		//	determine bit number of this square
		unsigned bitNumber = 0;
		int file = fromFile;
		int rank = fromRank;
		while( file > 0 && rank > 0 )
		{
			bitNumber++;
			file--;
			rank--;
		}

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard96 attack;
			file = fromFile + 1;
			rank = fromRank + 1;
			int bit = bitNumber + 1;
			while( rank < nRanks && file < nFiles )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = 9999;
				file++;
				rank++;
				bit++;
			}
			file = fromFile - 1;
			rank = fromRank - 1;
			bit = bitNumber - 1;
			while( rank >= 0 && file >= 0 )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = -1;
				file--;
				rank--;
				bit--;
			}
			bb96_bishop45attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}	

	// *** BISHOP ATTACK 135 *** //
	blockerElementCount = 1 << MIN(nRanks, nFiles);
	bishop135attackWidth = blockerElementCount;
	bb96_bishop135attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = mask135[originSquare];

		//	determine bit number of this square
		unsigned bitNumber = 0;
		int file = fromFile;
		int rank = fromRank;
		while( file < nFiles - 1 && rank > 0 )
		{
			bitNumber++;
			file++;
			rank--;
		}

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard96 attack;
			file = fromFile - 1;
			rank = fromRank + 1;
			int bit = bitNumber + 1;
			while( rank < nRanks && file >= 0 )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = 9999;
				file--;
				rank++;
				bit++;
			}
			file = fromFile + 1;
			rank = fromRank - 1;
			bit = bitNumber - 1;
			while( rank >= 0 && file < nFiles  )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					file = 9999;
				file++;
				rank--;
				bit--;
			}
			bb96_bishop135attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}	
}

void Board::InitializeBitBoard128Data()
{
	// *** RAYS *** //

	unsigned nSquares = (unsigned) (nRanks * nFiles);
	bb128_rays.SetSize( nSquares * nSquares );
	for( unsigned fromSquare = 0; fromSquare < nSquares; fromSquare++ )
	{
		int fromFile = fromSquare % nFiles;
		int fromRank = fromSquare / nFiles;
		for( unsigned toSquare = 0; toSquare < nSquares; toSquare++ )
		{
			int toFile = toSquare % nFiles;
			int toRank = toSquare / nFiles;
			int ray_index = (fromSquare * nSquares) + toSquare;

			if( fromFile == toFile )
			{
				if( fromRank < toRank )
					for( int x = fromRank + 1; x <= toRank; x++ )
						bb128_rays[ray_index].SetBit( x * nFiles + fromFile );
				else if( fromRank > toRank )
					for( int x = fromRank - 1; x >= toRank; x-- )
						bb128_rays[ray_index].SetBit( x * nFiles + fromFile );
			}
			else if( fromRank == toRank )
			{
				if( fromFile < toFile )
					for( int x = fromFile + 1; x <= toFile; x++ )
						bb128_rays[ray_index].SetBit( fromRank * nFiles + x );
				else if( fromFile > toFile )
					for( int x = fromFile - 1; x >= toFile; x-- )
						bb128_rays[ray_index].SetBit( fromRank * nFiles + x );
			}
			else if( fromRank > toRank && fromRank - toRank == fromFile - toFile )
			{
				for( int delta = 1; delta <= fromRank - toRank; delta++ )
					bb128_rays[ray_index].SetBit( (fromRank - delta) * nFiles + (fromFile - delta) );
			}
			else if( fromRank < toRank && toRank - fromRank == toFile - fromFile )
			{
				for( int delta = 1; delta <= toRank - fromRank; delta++ )
					bb128_rays[ray_index].SetBit( (fromRank + delta) * nFiles + (fromFile + delta) );
			}
			else if( fromRank > toRank && fromRank - toRank == toFile - fromFile )
			{
				for( int delta = 1; delta <= fromRank - toRank; delta++ )
					bb128_rays[ray_index].SetBit( (fromRank - delta) * nFiles + (fromFile + delta) );
			}
			else if( fromRank < toRank && toRank - fromRank == fromFile - toFile )
			{
				for( int delta = 1; delta <= toRank - fromRank; delta++ )
					bb128_rays[ray_index].SetBit( (fromRank + delta) * nFiles + (fromFile - delta) );
			}
		}
	}

	// *** ROOK ATTACK 00 *** //
	int blockerElementCount = 1 << nFiles;
	rook00attackWidth = blockerElementCount;
	bb128_rook00attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = (1 << nFiles) - 1;

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard128 attack;
			int file = fromFile + 1;
			while( file < nFiles )
			{
				attack.SetBit( fromRank * nFiles + file );
				if( blockers & (1 << file) )
					//	we can slide no further in this direction
					file = 9999;
				file++;
			}
			file = fromFile - 1;
			while( file >= 0 )
			{
				attack.SetBit( fromRank * nFiles + file );
				if( blockers & (1 << file) )
					//	we can slide no further in this direction
					file = -1;
				file--;
			}
			bb128_rook00attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}

	// *** ROOK ATTACK 90 *** //
	blockerElementCount = 1 << nRanks;
	rook90attackWidth = blockerElementCount;
	bb128_rook90attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = (1 << nRanks) - 1;

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard128 attack;
			int rank = fromRank + 1;
			while( rank < nRanks )
			{
				attack.SetBit( rank * nFiles + fromFile );
				if( blockers & (1 << rank) )
					//	we can slide no further in this direction
					rank = 9999;
				rank++;
			}
			rank = fromRank - 1;
			while( rank >= 0 )
			{
				attack.SetBit( rank * nFiles + fromFile );
				if( blockers & (1 << rank) )
					//	we can slide no further in this direction
					rank = -1;
				rank--;
			}
			bb128_rook90attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}

	// *** BISHOP ATTACK 45 *** //
	blockerElementCount = 1 << MIN(nRanks, nFiles);
	bishop45attackWidth = blockerElementCount;
	bb128_bishop45attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = mask45[originSquare];

		//	determine bit number of this square
		unsigned bitNumber = 0;
		int file = fromFile;
		int rank = fromRank;
		while( file > 0 && rank > 0 )
		{
			bitNumber++;
			file--;
			rank--;
		}

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard128 attack;
			file = fromFile + 1;
			rank = fromRank + 1;
			int bit = bitNumber + 1;
			while( rank < nRanks && file < nFiles )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = 9999;
				file++;
				rank++;
				bit++;
			}
			file = fromFile - 1;
			rank = fromRank - 1;
			bit = bitNumber - 1;
			while( rank >= 0 && file >= 0 )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = -1;
				file--;
				rank--;
				bit--;
			}
			bb128_bishop45attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}	

	// *** BISHOP ATTACK 135 *** //
	blockerElementCount = 1 << MIN(nRanks, nFiles);
	bishop135attackWidth = blockerElementCount;
	bb128_bishop135attack.SetSize( nSquares * blockerElementCount );
	for( unsigned originSquare = 0; originSquare < nSquares; originSquare++ )
	{
		unsigned fromFile = originSquare % nFiles;
		unsigned fromRank = originSquare / nFiles;
		unsigned mask = mask135[originSquare];

		//	determine bit number of this square
		unsigned bitNumber = 0;
		int file = fromFile;
		int rank = fromRank;
		while( file < nFiles - 1 && rank > 0 )
		{
			bitNumber++;
			file++;
			rank--;
		}

		for( unsigned blockers = 0; blockers <= mask; blockers++ )
		{
			BitBoard128 attack;
			file = fromFile - 1;
			rank = fromRank + 1;
			int bit = bitNumber + 1;
			while( rank < nRanks && file >= 0 )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					rank = 9999;
				file--;
				rank++;
				bit++;
			}
			file = fromFile + 1;
			rank = fromRank - 1;
			bit = bitNumber - 1;
			while( rank >= 0 && file < nFiles  )
			{
				attack.SetBit( rank * nFiles + file );
				if( blockers & (1 << bit) )
					//	we can slide no further in this direction
					file = 9999;
				file++;
				rank--;
				bit--;
			}
			bb128_bishop135attack[originSquare * blockerElementCount + blockers] = attack;
		}
	}	
}

void Board::InitializeOutpost( int *squareBonuses )
{
	//	TODO: fix for symmetry type 'none'
	hasOutposts = true;
	outpostSquareBonuses = squareBonuses;

	for( int nSquare = 0; nSquare < nSquares; nSquare++ )
	{
		int player0Bonus = squareBonuses[nSquare];
		int player1Bonus = squareBonuses[flipSquare[1][nSquare]];
		if( player0Bonus > 0 )
		{
			int pieceRank = ranks[0][nSquare];
			int pieceFile = files[0][nSquare];
			if( bitBoardType == BITBOARD_64 )
			{
				//	initialize matrix of bitboards that must be free of pawns if 
				//	piece of player 0 is to be considered 'posted'
				bb64OutpostNoPawnSquares[0][nSquare].Clear();
				for( int rank = pieceRank + 1; rank < nRanks; rank++ )
				{
					int square = GetMovementMatrix( DIRECTION_E )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb64OutpostNoPawnSquares[0][nSquare].SetBit( square );
					square = GetMovementMatrix( DIRECTION_W )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb64OutpostNoPawnSquares[0][nSquare].SetBit( square );
				}
			}
			else if( bitBoardType == BITBOARD_96 )
			{
				//	initialize matrix of bitboards that must be free of pawns if 
				//	piece of player 0 is to be considered 'posted'
				bb96OutpostNoPawnSquares[0][nSquare].Clear();
				for( int rank = pieceRank + 1; rank < nRanks; rank++ )
				{
					int square = GetMovementMatrix( DIRECTION_E )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb96OutpostNoPawnSquares[0][nSquare].SetBit( square );
					square = GetMovementMatrix( DIRECTION_W )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb96OutpostNoPawnSquares[0][nSquare].SetBit( square );
				}
			}
			else if( bitBoardType == BITBOARD_128 )
			{
				//	initialize matrix of bitboards that must be free of pawns if 
				//	piece of player 0 is to be considered 'posted'
				bb128OutpostNoPawnSquares[0][nSquare].Clear();
				for( int rank = pieceRank + 1; rank < nRanks; rank++ )
				{
					int square = GetMovementMatrix( DIRECTION_E )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb128OutpostNoPawnSquares[0][nSquare].SetBit( square );
					square = GetMovementMatrix( DIRECTION_W )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb128OutpostNoPawnSquares[0][nSquare].SetBit( square );
				}
			}
		}
		if( player1Bonus > 0 )
		{
			int pieceRank = ranks[0][nSquare];
			int pieceFile = files[0][nSquare];
			if( bitBoardType == BITBOARD_64 )
			{
				bb64OutpostNoPawnSquares[1][nSquare].Clear();
				for( int rank = pieceRank - 1; rank >= 0; rank-- )
				{
					int square = GetMovementMatrix( DIRECTION_E )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb64OutpostNoPawnSquares[1][nSquare].SetBit( square );
					square = GetMovementMatrix( DIRECTION_W )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb64OutpostNoPawnSquares[1][nSquare].SetBit( square );
				}
			}
			else if( bitBoardType == BITBOARD_96 )
			{
				bb96OutpostNoPawnSquares[1][nSquare].Clear();
				for( int rank = pieceRank - 1; rank >= 0; rank-- )
				{
					int square = GetMovementMatrix( DIRECTION_E )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb96OutpostNoPawnSquares[1][nSquare].SetBit( square );
					square = GetMovementMatrix( DIRECTION_W )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb96OutpostNoPawnSquares[1][nSquare].SetBit( square );
				}
			}
			else if( bitBoardType == BITBOARD_128 )
			{
				bb128OutpostNoPawnSquares[1][nSquare].Clear();
				for( int rank = pieceRank - 1; rank >= 0; rank-- )
				{
					int square = GetMovementMatrix( DIRECTION_E )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb128OutpostNoPawnSquares[1][nSquare].SetBit( square );
					square = GetMovementMatrix( DIRECTION_W )[rank * nFiles + pieceFile];
					if( square >= 0 )
						bb128OutpostNoPawnSquares[1][nSquare].SetBit( square );
				}
			}
		}
	}
}

void Board::PlacePiecesByFEN
	( char *notation )
{
	int cursor = 0;
	int rank = nRanks - 1;
	int file = 0;
	char buffer[4];
	int length = (int) strlen( notation );
	
	while( cursor < length )
	{
		buffer[0] = notation[cursor];
		buffer[1] = '\0';

		if( buffer[0] == '/' )
		{
			file = 0;
			rank--;
			cursor++;
		}
		else if( buffer[0] >= '0' && buffer[0] <= '9' )
		{
			cursor++;
			buffer[1] = '\0';
			if( notation[cursor] >= '0' && notation[cursor] <= '9' )
			{
				cursor++;
				buffer[1] = notation[cursor];
				buffer[2] = '\0';
			}
			file += atoi( buffer );
		}
		else if( buffer[0] == '_' )
		{
			cursor++;
			buffer[1] = notation[cursor++];
			buffer[2] = toupper(notation[cursor++]);
			buffer[3] = '\0';
			int player = 0;
			if( buffer[1] >= 'a' && buffer[1] <= 'z' )
			{
				player = 1;
				buffer[1] = toupper(buffer[1]);
			}
			PieceType *type = NULL;
			for( int x = 0; x < nPlayerPieceTypes[player] && type == NULL; x++ )
				if( !strcmp( playerPieceTypes[player][x]->GetNotation(), buffer ) )
					type = playerPieceTypes[player][x];
			if( type != NULL )
			{
				pGame->AddPiece( *type, player, rank, file );
				file++;
			}
		}
		else
		{
			buffer[1] = '\0';
			int player = 0;
			if( buffer[0] >= 'a' && buffer[0] <= 'z' )
			{
				player = 1;
				buffer[0] = toupper(buffer[0]);
			}
			PieceType *type = NULL;
			for( int x = 0; x < nPlayerPieceTypes[player] && type == NULL; x++ )
				if( !strcmp( playerPieceTypes[player][x]->GetNotation(), buffer ) )
					type = playerPieceTypes[player][x];
			if( type != NULL )
			{
				pGame->AddPiece( *type, player, rank, file );
				cursor++;
				file++;
			}
			else if( cursor + 1 < length )
			{
				//	try two-character notation
				buffer[1] = toupper(notation[cursor+1]);
				buffer[2] = '\0';
				for( int x = 0; x < nPlayerPieceTypes[player] && type == NULL; x++ )
					if( !strcmp( playerPieceTypes[player][x]->GetNotation(), buffer ) )
						type = playerPieceTypes[player][x];
				if( type != NULL )
				{
					pGame->AddPiece( *type, player, rank, file );
					cursor += 2;
					file++;
				}
			}
			else
			{
				ASSERT(FALSE);
			}
		}
	}
}

void Board::PrepareGame()
{
	//	load piece set
	int x;
	int nTypes = 0;
	PieceType **types = pGame->GetPieceTypesRequired( nTypes );
	for( x = 0; x < nTypes; x++ )
		types[x]->LoadPieceSet( selectedPieceSet );

	//	call CompleteSetup for all pieces
	for( int player = 0; player < 2; player++ )
		for( int y = 0; y < nPieces[player]; y++ )
			pieces[player][y]->CompleteSetup();

	//	determine 'generic' square values
	for( int nPlayer = 0; nPlayer < 1; nPlayer++ )
		for( int nPiece = 0; nPiece < nPieces[nPlayer]; nPiece++ )
			//	add the piece's mobility matrix to the default 
			//	value bonuses for occupying each square
			if( !pieces[nPlayer][nPiece]->IsCaptured() && 
				!pieces[nPlayer][nPiece]->GetType().IsPawn() )
				squareValues += pieces[nPlayer][nPiece]->GetMobilityMatrix();

	//	scale the square values to an average value of 100
	double total = 0.0;
	for( x = 0; x < nSquares; x++ )
		total += squareValues[x];
	double scale = pGame->GetAverageSquareValue() / (total / nSquares);
	for( x = 0; x < nSquares; x++ )
		squareValues[x] = (int) ((squareValues[x] * scale) + 0.5);

	squareWidth = pieceSetSizes[selectedPieceSet];
	squareHeight = pieceSetSizes[selectedPieceSet];
}

void Board::StartGame()
{
	currentPlayer = 0;
	gamePly = 1;
	gameRecords[0].pieceMoved = NULL;
	gameRecords[0].lastCaptureOrPawn = 0;
	gameRecords[0].primaryHash = 0UL;
	gameRecords[0].gameInt1 = 0;
	gameRecords[0].gameInt2 = 0;
	gameRecords[0].gameFlags = 0UL;

	//	load opening book
	FILE *file = NULL;
	char buffer[500];
	if( openingBookFilename != NULL )
		file = fopen( openingBookFilename, "r" );
	if( file != NULL )
	{
		while( true )
		{
			char *rtn = fgets( buffer, 500, file );
			if( rtn == NULL )
			{
				break;
			}
			int cursor = 0;
			int len = (int) strlen( buffer );
			//	skip whitespace
			while( cursor < len &&
				(buffer[cursor] == ' ' || 
				 buffer[cursor] == '\t') )
				cursor++;
			if( buffer[cursor] == ';' )
				//	skip comment
				continue;
			ParseOpeningLine( buffer, cursor );
		}
	}

	if( pGame->GetPlayer( 0 ).IsComputerControlled() )
	{
		::BringWindowToTop( theWindow );
		::InvalidateRect( theWindow, NULL, true );
		::UpdateWindow( theWindow );
		Think();
	}
	else
	{
		inCheck[currentDepth] = IsInCheck();
		legalUserMoves.GenerateMoves( Movement(), Movement(), PV );
	}

	::InvalidateRect( theWindow, NULL, true );
	::UpdateWindow( theWindow );
}

void Board::UpdatePieceRealSquareNumbers()
{
	rootCurrentPlayer = currentPlayer;
	rootPrimaryHash = primaryHash;
	rootSecondaryHash = secondaryHash;

	for( int player = 0; player < 2; player++ )
	{
		for( int x = 0; x < nPieces[player]; x++ )
		{
			if( pieces[player][x]->IsCaptured() )
				pieces[player][x]->SetReallyCapturedStatus( true );
			else
			{
				pieces[player][x]->SetReallyCapturedStatus( false );
				pieces[player][x]->SetRealSquare( pieces[player][x]->GetSquareNumber() );
			}
			pieces[player][x]->SetRealPieceType();
		}
	}
}

void Board::TestPosition()
{
	//	clear the pv before a new search
//	for( int i = 0; i < PV_BUFF; i++ )
//		for( int j = 0; j < PV_BUFF; j++ )
//			pvTable[i][j] = Movement();

	//	clear the history huersitic
	for( int x = 0; x < MAX_PLY; x++ )
		history[x].ResetContents();

	//	clear the killer moves
	for( int i = 0; i < MAX_PLY; i++ )
	{
		killer1[i] = Movement();
		killer2[i] = Movement();
	}

	//	clear statistics
	Statistics::ResetStats();

	int oldFutilityMargin = theGame->GetFutilityMargin();
	int oldExtendedFutilityMargin = theGame->GetExtendedFutilityMargin();
	int oldRazorMargin = theGame->GetRazorMargin();

	theGame->SetFutilityMargin( 8000 );
	theGame->SetExtendedFutilityMargin( 10000 );
	theGame->SetRazorMargin( 12500 );

	movementLists[1].Clear();
	theGame->AboutToStartThinking( currentPlayer );
	int alpha = -INFINITY;
	int beta = INFINITY;
	currentDepth = 1;
	positionTestList.GenerateMoves( Movement(), Movement(), PV );
	positionTestList.ResetCurrentMove();
	bool movesRemaining = true;
	bool firstMove = true;
	int score; 
	int firstScore;
	int movesTested = 0;
	Movement bestMoveNextLevel;
	while( movesRemaining )
	{
		//	make the move
		if( !positionTestList.MakeNextMove() )
		{
			movesRemaining = false;
			continue;
		}
		if( movesTested < testingMaxMoves )
		{
			//	go deeper (recursively)
			if( firstMove )
			{
				for( iDepth = 1; iDepth <= testingDepth; iDepth++ )
				{
					currentDepth++;
					firstScore = -Search( -400, 400, iDepth, false, PV, bestMoveNextLevel );
					currentDepth--;
				}
				firstMove = false;
				MoveInfo &mi = positionTestList.GetCurrentMove();
				mi.evaluation = firstScore;
			}
			else
			{
				for( iDepth = 1; iDepth <= testingDepth; iDepth++ )
				{
					currentDepth++;
					score = -Search( -400, 400, iDepth, false, PV, bestMoveNextLevel );
					currentDepth--;
				}
				positionTestList.GetCurrentMove().evaluation = score;
			}
		}
		else
			positionTestList.GetCurrentMove().evaluation = -100000;
		//	undo the move
		positionTestList.UnmakeMove();
		movesTested++;
	}
	positionTestList.ResetMoveOrdering();
	positionTestList.OrderMoves( Movement(), PV );
	theGame->SetFutilityMargin( oldFutilityMargin );
	theGame->SetExtendedFutilityMargin( oldExtendedFutilityMargin );
	theGame->SetRazorMargin( oldRazorMargin );
}

MovementList &Board::GetCurrentMovementList()
{
	int x = currentDepth;
	MovementList &l = movementLists[x];
	return l;
}

word32 Board::GetPrimaryHash()
{
	return pGame->AdjustPrimaryHash( primaryHash ^ (0x00010000UL - currentPlayer) );
}

int Board::GetRankByName
	( char *rankName )
{
	for( int r = 0; r < nRanks; r++ )
		if( !strcmp( rankName, namesOfRanks[r] ) )
			return r;
	return -1;
}

int Board::GetRankByChar
	( char rankName )
{
	for( int r = 0; r < nRanks; r++ )
		if( rankName == namesOfRanks[r][0] &&
			namesOfRanks[r][1] == '\0' )
			return r;
	return -1;
}

int Board::GetRankByCharPair
	( char rankName1, 
	  char rankName2 )
{
	for( int r = 0; r < nRanks; r++ )
		if( rankName1 == namesOfRanks[r][0] &&
			rankName2 == namesOfRanks[r][1]  )
			return r;
	return -1;
}

int Board::GetFileByName
	( char *fileName )
{
	for( int f = 0; f < nFiles; f++ )
		if( !strcmp( fileName, namesOfFiles[f] ) )
			return f;
	return -1;
}

int Board::GetFileByChar
	( char fileName )
{
	for( int f = 0; f < nFiles; f++ )
		if( fileName == namesOfFiles[f][0] &&
			namesOfFiles[f][1] == '\0' )
			return f;
	return -1;
}

int Board::GetFileByCharPair
	( char fileName1, 
	  char fileName2 )
{
	for( int f = 0; f < nFiles; f++ )
		if( fileName1 == namesOfFiles[f][0] &&
			fileName2 == namesOfFiles[f][1]  )
			return f;
	return -1;
}

void Board::TakeBackMove()
{
	if( gamePly <= 1 )
		return;
	historicalMoves.UnmakeMove( gamePly - 2 );
	historicalMoves.Pop();
	::SendMessage( hMoveList, LB_DELETESTRING, gamePly - 1, (LPARAM) 0 );
	theGame->SetMoveHistoryCursor( theGame->GetMoveHistoryCursor() - 1 );
	UpdatePieceRealSquareNumbers();
	::InvalidateRect( theWindow, NULL, false );
	::UpdateWindow( theWindow );
	legalUserMoves.GenerateMoves( Movement(), Movement(), PV );
	positionTestList.GenerateMoves( Movement(), Movement(), PV );
	pGame->GameOver( false );
}

void Board::TakeBackAllMoves()
{
	if( pGame->IsGameOver() )
	{
		while( gamePly > 1 )
			TakeBackMove();
		
		currentPlayer = 0;
		gamePly = 1;
		gameRecords[0].pieceMoved = NULL;
		gameRecords[0].lastCaptureOrPawn = 0;
		gameRecords[0].primaryHash = 0UL;
		gameRecords[0].gameInt1 = 0;
		gameRecords[0].gameInt2 = 0;
		gameRecords[0].gameFlags = 0UL;
		historicalMoves.Clear();
		UpdatePieceRealSquareNumbers();
		RecalculateHashes();
		currentLine[0] = '\0';
	}
	else
	{
		while( gamePly > 1 )
			TakeBackMove();
	}
	hashTable.ClearAll();
}

void Board::DropPiece
	( int squareNumber )
{
	AssertValid();
	int fromSquare = pieceLifted->GetSquareNumber();
	MoveInfo *pMoveInfos[20];
	int moveInfoIndexes[20];
	int nMoves = 0;
	for( int i = 0; i < legalUserMoves.GetCount(); i++ )
	{
		MoveInfo &move = legalUserMoves.GetMove( i );
		if( move.fromSquare == fromSquare &&
			move.toSquare == squareNumber )
		{
			//	the historicalMoves movement stack performs the actual move
			pMoveInfos[nMoves] = &move;
			moveInfoIndexes[nMoves] = i;
			nMoves++;

#ifdef _WINBOARD_VERSION
			if( promoChar )
			{	
				// [HGM] try to decide promotion piece from WinBoard move
			    char c = 0;
			    if( move.promotion )
				{
					c = move.promotion->GetFullName()[0];
					if( !strcmp( move.promotion->GetFullName(), "Knight" ) ) 
						c = 'N';
			    }
			    if( c >= 'A' && c <= 'Z' )
					c += 'a' - 'A';
			    if( c != promoChar )
				{
					nMoves--; // this was not the promotion we are looking for
//					if( arg )
//						printf( "# rejected promotion to %s\n", move.promotion->GetFullName() );
			    }
			}
#endif
		}
	}

	MoveInfo *pSelectedMove = NULL;
	int selectedMoveIndex;
	if( nMoves == 1 )
	{
		pSelectedMove = pMoveInfos[0];
		selectedMoveIndex = moveInfoIndexes[0];
	}
	else if( nMoves > 1 )
	{
		pieceLifted = NULL;
		::InvalidateRect( theWindow, NULL, false );
		allowableMoves = pMoveInfos;
		allowableMoveCount = nMoves;
		allowableMoveIndexes = moveInfoIndexes;

#ifdef _WINBOARD_VERSION
		printf( "# ambiguous move %d-%d: %d possibilities\n", fromSquare, squareNumber, nMoves );
		int sel = -1;
#else
		int sel = (int) ::DialogBox( theInstance, MAKEINTRESOURCE(IDD_MOVE_SELECTION_DIALOG), NULL, 
			reinterpret_cast<DLGPROC>(MoveSelectDlgProc) );
#endif

		if( sel != -1 )
		{
			pSelectedMove = &(legalUserMoves.GetMove( sel ));
			selectedMoveIndex = sel;
		}
	}

	pieceLifted = NULL;

	if( pSelectedMove != NULL )
	{
		PerformMove( selectedMoveIndex );
	}
#ifdef _WINBOARD_VERSION
	else
		printf( "# move %d-%d rejected\n", fromSquare, squareNumber );
#endif
}

bool Board::IsMoveLegal
	( int fromSquareNumber, 
	  int toSquareNumber )
{
	for( int i = 0; i < legalUserMoves.GetCount(); i++ )
		if( legalUserMoves.GetMove( i ).fromSquare == fromSquareNumber &&
			legalUserMoves.GetMove( i ).toSquare == toSquareNumber )
			return true;
	return false;
}

bool Board::IsDraw()
{
	//	IsDraw() is used to check for draw-by-repetition  

	//	Some notes:
    //	- the 2 repetition trick is attempted first: if we have already seen a
    //	  position in the search history (not game history), we haven't found
    //	  anything better to do than repeat, and searching the position again would
    //	  be a waste of time
    //	- if there is no match on the 2 repetition check, we look for an actual
    //	  3 fold repetition
    //	- we can't check for the 50 move rule here, since the 50 move rule must be
    //	  checked at the end of a search node due to mates  on the 50th move, yet
    //	  we want to check for a draw by repetition before we waste any time
	//	  searching the position or generating moves
    //	- is_draw () can be used in both search () and search_root () as the
    //	  for loop for the 2 repetition trick will exit immediately at the root

	int i, repeats = 0, end, start;
	int relevantHistory = gamePly - gameRecords[gamePly].lastCaptureOrPawn;

	//	Check on the 2 repetition trick.  
	//	Some notes:
    //	- a position can't possibly have occurred once before if fifty isn't
    //	  at least 4
    //	- the end point is picked to look at the least number of positions, ie
    //	  if going to the last capture is shorter than looking at the whole search
    //	  history, we will do only that much
	if( relevantHistory >= 4 )
	{
		if( gamePly - currentDepth + 1 < gamePly - relevantHistory )
			end = gamePly - relevantHistory;
		else
			end = gamePly - currentDepth + 1;
		for( i = gamePly - 2; i >= 0 && i >= end; i -= 2 )
		{
			if( GetPrimaryHash() == gameRecords[i].primaryHash )
				return true;
		}
	}

	//	Check for actual 3 repetition match.  
	//	Some notes:
    //	- a position can't possibly have occurred twice before if fifty isn't
    //	  at least 6
    //	- there is no need for us to consider positions encountered during search,
    //	  as if there was a match on any of them, it would have been found above
    //	- we need to adjust the starting point here based on who's turn it is:
    //	  if it's the same as at the root, we need to subtract one extra 
	if( relevantHistory >= 6 )
	{
		start = gamePly - currentDepth - (currentDepth % 2);
		end = gamePly - relevantHistory;
		for( i = start; i >= 0 && i >= end; i -= 2 )
		{
			if( GetPrimaryHash() == gameRecords[i].primaryHash )
				repeats++;
			if( repeats >= 2 )
				return true;
		}
	}
	return false;
}

bool Board::IsSquareAttacked
	( int squareNumber, 
	  int playerNumber )
{
	for( int x = 0; x < directionCount; x++ )
	{
		int attack_range = attackRange[playerNumber][x];

		if( attack_range > 0 )
		{
			Direction &direction = directions[x];
			int oppositeDirection = direction.GetOppositeDirectionNumber();
			int steps = 1;
			int square = movementMatrix[oppositeDirection][squareNumber];

			//	unrolled ...
			if( square >= 0 )
			{
				Piece *piece = GetSquareContents( square );
				square = movementMatrix[oppositeDirection][square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == playerNumber &&
						piece->GetType().GetAttackRange( piece->GetPlayerNumber(), x ) >= steps &&
						piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), x ) <= steps &&
						!IsImmobilized( piece->GetSquareNumber() ) )
						return true;
				}
				steps++;
			}

			while( square >= 0 && steps <= attack_range )
			{
				Piece *piece = GetSquareContents( square );
				square = movementMatrix[oppositeDirection][square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == playerNumber &&
						piece->GetType().GetAttackRange( piece->GetPlayerNumber(), x ) >= steps &&
						piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), x ) <= steps &&
						!IsImmobilized( piece->GetSquareNumber() ) )
						return true;
				}
				steps++;
			}
		}
	}
	return false;
}

bool Board::IsSquareAttacked
	( int squareNumber, 
	  int playerNumber,
	  int dirSquare1, 
	  int dirSquare2 )
{
	int dirs[8];
	int nDirs = 0;
	if( directionLookup[dirSquare1][squareNumber][0] != -1 )
	{
		dirs[nDirs++] = directionLookup[dirSquare1][squareNumber][0];
		if( directionLookup[dirSquare1][squareNumber][1] != -1 )
		{
			dirs[nDirs++] = directionLookup[dirSquare1][squareNumber][1];
			if( directionLookup[dirSquare1][squareNumber][2] != -1 )
			{
				dirs[nDirs++] = directionLookup[dirSquare1][squareNumber][2];
				if( directionLookup[dirSquare1][squareNumber][3] != -1 )
				{
					dirs[nDirs++] = directionLookup[dirSquare1][squareNumber][3];
				}
			}
		}
	}
	if( dirSquare2 != -1 && directionLookup[dirSquare2][squareNumber][0] != -1 )
	{
		dirs[nDirs++] = directionLookup[dirSquare2][squareNumber][0];
		if( directionLookup[squareNumber][dirSquare2][1] != -1 )
		{
			dirs[nDirs++] = directionLookup[dirSquare2][squareNumber][1];
			if( directionLookup[dirSquare2][squareNumber][2] != -1 )
			{
				dirs[nDirs++] = directionLookup[dirSquare2][squareNumber][2];
				if( directionLookup[dirSquare2][squareNumber][3] != -1 )
				{
					dirs[nDirs++] = directionLookup[dirSquare2][squareNumber][3];
				}
			}
		}
	}

	for( int x = 0; x < nDirs; x++ )
	{
		int attack_range = attackRange[playerNumber][dirs[x]];

		if( attack_range > 0 )
		{
			Direction &direction = directions[dirs[x]];
			int oppositeDirection = direction.GetOppositeDirectionNumber();
			int steps = 1;
			int square = movementMatrix[oppositeDirection][squareNumber];

			//	unrolled ...
			if( square >= 0 )
			{
				Piece *piece = GetSquareContents( square );
				square = movementMatrix[oppositeDirection][square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == playerNumber &&
						piece->GetType().GetAttackRange( piece->GetPlayerNumber(), dirs[x] ) >= steps &&
						piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), dirs[x] ) <= steps &&
						!IsImmobilized( piece->GetSquareNumber() ) )
						return true;
				}
				steps++;
			}

			while( square >= 0 && steps <= attack_range )
			{
				Piece *piece = GetSquareContents( square );
				square = movementMatrix[oppositeDirection][square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == playerNumber &&
						piece->GetType().GetAttackRange( piece->GetPlayerNumber(), dirs[x] ) >= steps &&
						piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), dirs[x] ) <= steps &&
						!IsImmobilized( piece->GetSquareNumber() ) )
						return true;
				}
				steps++;
			}
		}
	}
	return false;
}

void Board::BuildAttackerList
	( int squareNumber,
	  AttackVector *playerAttackers[2], 
	  int *playerAttackerCount,
	  Piece *pieceToIgnore )
{
	playerAttackerCount[0] = 0;
	playerAttackerCount[1] = 0;
	for( int x = 0; x < directionCount; x++ )
	{
		Direction &direction = directions[x];
		int oppositeDirection = direction.GetOppositeDirectionNumber();
		int *matrix = movementMatrix[x];
		int steps = 1;
		int square = matrix[squareNumber];
		while( square >= 0 && 
			(steps <= attackRange[0][x] || steps <= attackRange[1][x]) )
		{
			Piece *piece = squareContents[square];
			if( piece != NULL && piece != pieceToIgnore )
			{
				if( piece->GetType().GetAttackRange( piece->GetPlayerNumber(), oppositeDirection ) >= steps && 
					piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), oppositeDirection ) <= steps && 
					!IsImmobilized( piece->GetSquareNumber() ) )
				{
					//	before adding this attacker, we might need to ensure that 
					//	we haven't already seen this piece when looking in another direction
					bool addThisPiece = true;
					int nPlayer = piece->GetPlayerNumber();
					if( boardWrapsAround || direction.IsAuxilaryDirection() )
					{
						for( int y = 0; y < playerAttackerCount[nPlayer] && addThisPiece; y++ )
							if( playerAttackers[nPlayer][y].piece == piece )
								//	we already have this piece, so don't add it
								addThisPiece = false;
					}
					if( addThisPiece )
					{
						playerAttackers[nPlayer][playerAttackerCount[nPlayer]].piece = piece;
						playerAttackers[nPlayer][playerAttackerCount[nPlayer]].baseValue = piece->GetType().IsRoyal() ? 100000 : piece->GetBaseValue();
						playerAttackers[nPlayer][playerAttackerCount[nPlayer]].directionNumber = x;
						playerAttackers[nPlayer][playerAttackerCount[nPlayer]].distanceToTarget = steps;
						playerAttackerCount[nPlayer]++;
					}
				}
				square = -1;
			}
			else
			{
				square = matrix[square];
				steps++;
			}
		}
	}
}

bool Board::IsInCheck()
{
	if( !theGame->IsGoalToCheckmate() )
		return false;

	Piece *king = kings[currentPlayer];
	int otherPlayer = FLIP(currentPlayer);

	if( theGame->HasSpecialCheckTesting() )
		return theGame->IsInCheck( king );

	return theGame->IsSquareAttacked( king->GetSquareNumber(), otherPlayer );
}

bool Board::IsInCheck
	( int fromSquare, 
	  int toSquare )
{
	Piece *king = kings[currentPlayer];

	if( theGame->HasSpecialCheckTesting() )
		return theGame->IsInCheck( king );

	int dirs[8];
	int nDirs = 0;
	if( directionLookup[fromSquare][king->GetSquareNumber()][0] != -1 )
	{
		dirs[nDirs++] = directionLookup[fromSquare][king->GetSquareNumber()][0];
		if( directionLookup[fromSquare][king->GetSquareNumber()][1] != -1 )
		{
			dirs[nDirs++] = directionLookup[fromSquare][king->GetSquareNumber()][1];
			if( directionLookup[fromSquare][king->GetSquareNumber()][2] != -1 )
			{
				dirs[nDirs++] = directionLookup[fromSquare][king->GetSquareNumber()][2];
				if( directionLookup[fromSquare][king->GetSquareNumber()][3] != -1 )
				{
					dirs[nDirs++] = directionLookup[fromSquare][king->GetSquareNumber()][3];
				}
			}
		}
	}
	if( toSquare != -1 && directionLookup[toSquare][king->GetSquareNumber()][0] != -1 )
	{
		dirs[nDirs++] = directionLookup[toSquare][king->GetSquareNumber()][0];
		if( directionLookup[king->GetSquareNumber()][toSquare][1] != -1 )
		{
			dirs[nDirs++] = directionLookup[toSquare][king->GetSquareNumber()][1];
			if( directionLookup[toSquare][king->GetSquareNumber()][2] != -1 )
			{
				dirs[nDirs++] = directionLookup[toSquare][king->GetSquareNumber()][2];
				if( directionLookup[toSquare][king->GetSquareNumber()][3] != -1 )
				{
					dirs[nDirs++] = directionLookup[toSquare][king->GetSquareNumber()][3];
				}
			}
		}
	}
	for( int x = 0; x < nDirs; x++ )
	{
		int attack_range = attackRange[FLIP(king->GetPlayerNumber())][dirs[x]];

		if( attack_range > 0 )
		{
			Direction &direction = GetDirection( dirs[x] );
			int oppositeDirection = direction.GetOppositeDirectionNumber();
			int steps = 1;
			int square = movementMatrix[oppositeDirection][king->GetSquareNumber()];

			//	unrolled ...
			if( square >= 0 )
			{
				Piece *piece = GetSquareContents( square );
				square = movementMatrix[oppositeDirection][square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == FLIP(king->GetPlayerNumber()) &&
						piece->GetType().GetAttackRange( piece->GetPlayerNumber(), dirs[x] ) >= steps &&
						piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), dirs[x] ) <= steps &&
						!IsImmobilized( piece->GetSquareNumber() ) )
						return true;
				}
				steps++;
			}

			while( square >= 0 && steps <= attack_range )
			{
				Piece *piece = GetSquareContents( square );
				square = movementMatrix[oppositeDirection][square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == FLIP(king->GetPlayerNumber()) &&
						piece->GetType().GetAttackRange( piece->GetPlayerNumber(), dirs[x] ) >= steps &&
						piece->GetType().GetMinAttackRange( piece->GetPlayerNumber(), dirs[x] ) <= steps &&
						!IsImmobilized( piece->GetSquareNumber() ) )
						return true;
				}
				steps++;
			}
		}
	}
	return false;
}

bool Board::IsOtherPlayerInCheck()
{
	if( !theGame->IsGoalToCheckmate() )
		return false;

	Piece *king = kings[FLIP(currentPlayer)];

	if( theGame->HasSpecialCheckTesting() )
		return theGame->IsInCheck( king );

	return theGame->IsSquareAttacked( king->GetSquareNumber(), currentPlayer );
}

void Board::PlayLine( char *line )
{
	int cursor = 0;
	currentDepth = 1;

	//	skip whitespace
	while( line[cursor] != ' ' && line[cursor] != ';' && 
			line[cursor] != '\0' && line[cursor] != '\t' && 
			line[cursor] != '\r' && line[cursor] != '\n' )
		cursor++;
	while( line[cursor] == ' ' || line[cursor] == '\t' )
		cursor++;

	while( line[cursor] != '\0' && 
		   line[cursor] != '}' )
	{
		//	generate moves
		legalUserMoves.GenerateMoves( Movement(), Movement(), PV );
		positionTestList.GenerateMoves( Movement(), Movement(), PV );

		//	search for the move
		int moveNumber = pGame->TranslateMove( legalUserMoves, line + cursor );

		if( moveNumber == -1 )
		{
			::MessageBox( theWindow, "Illegal move found in save game file!", "ERROR!", MB_ICONHAND | MB_OK );
			break;
		}
		else
		{
			MoveInfo &moveInfo = legalUserMoves.GetMove( moveNumber );
			legalUserMoves.CopyMoveToList( moveNumber, historicalMoves );
			historicalMoves.MakeMove( historicalMoves.GetCount() - 1 );
			UpdatePieceRealSquareNumbers();

			if( pGame->IsPositionForbidden() )
			{
				::PostQuitMessage( 0 );
				return;
			}

			if( currentLine != NULL )
			{
				//	add this move info to the current move line
				int length =  (int) strlen( currentLine );
				if( length > 0 )
					currentLine[length++] = ' ';
				pGame->DescribeMove( moveInfo, NULL, NULL, currentLine + length );
			}
			//	add move to game history (so it shows up in the move list)
			theGame->AddHistoricalMove( moveInfo );
		}
		inCheck[currentDepth] = IsInCheck();

		//	skip whitespace
		while( line[cursor] != ' ' && line[cursor] != ';' && 
			   line[cursor] != '\0' && line[cursor] != '\t' && 
			   line[cursor] != '\r' && line[cursor] != '\n' )
			cursor++;
		while( line[cursor] == ' ' || line[cursor] == '\t' )
			cursor++;
	}

	//	load piece set
	int nTypes = 0;
	PieceType **types = pGame->GetPieceTypesRequired( nTypes );
	for( int x = 0; x < nTypes; x++ )
		types[x]->LoadPieceSet( selectedPieceSet );

	for( int player = 0; player < 2; player++ )
		for( int y = 0; y < nPieces[player]; y++ )
			pieces[player][y]->CompleteSetup();

	squareWidth = pieceSetSizes[selectedPieceSet];
	squareHeight = pieceSetSizes[selectedPieceSet];
}

void Board::Bookkeeping()
{
	static RECT updateRect;

	#ifdef _DEBUG
	AssertValid();
	#endif

	if( testingPosition )
	{
		timeLeft = true;
		if( STAT_LOOKUP(nodeCount) % 32768LL == 0 )
		{
			spinner = (spinner + 1) % 4;
			updateRect.top = 0;
			updateRect.left = 0;
			updateRect.right = 680;
			updateRect.bottom = 75;
			showStats = true;
			::InvalidateRect( theWindow, &updateRect, false );
			::UpdateWindow( theWindow );
		}
		return;
	}
	if( abortSearch )
	{
		abortSearch = false;
		timeLeft = false;
		::InvalidateRect( theWindow, NULL, false );
		::UpdateWindow( theWindow );
		return;
	}
	if( STAT_LOOKUP(nodeCount) % 8192LL == 0 )
	{
		MSG msg;
		while( ::PeekMessage( &msg, theWindow, 0, 0, PM_REMOVE ) != 0 )
			::DispatchMessage( &msg );
		if( iDepth > 1 && STAT_LOOKUP(nodeCount) % 32768LL == 0 )
		{
			__time64_t currentTime;
			_time64( &currentTime );
			int secondsElapsed = (int) (currentTime - startOfSearchTime);
			timeLeft = iDepth <= minIDepth || secondsElapsed < maxThinkTime;
			updateRect.top = 0;
			updateRect.left = 0;
			updateRect.right = 680;
			updateRect.bottom = 75;
			::InvalidateRect( theWindow, &updateRect, false );
			refreshCausedByBookkeeping = true;
			showStats = true;
			::UpdateWindow( theWindow );
			refreshCausedByBookkeeping = false;
		}
	}
}

void Board::ParseOpeningLine
	( char *buffer,
	  int cursor )
{
	currentDepth = 1;
	bool done = false;
	bool moveIsQuestionable;
	int moves[50];

	//	skip whitespace
	while( buffer[cursor] == ' ' || buffer[cursor] == '\t' )
		cursor++;
	if( buffer[cursor] == ';' || 
		buffer[cursor] == '\r' || 
		buffer[cursor] == '\n' || 
		buffer[cursor] == '\0' )
		//	end-of-line
		done = true;
	while( !done )
	{
        //	generate moves
		movementLists[currentDepth].GenerateMoves( PV );

		if( buffer[cursor] == '?' )
		{
			moveIsQuestionable = true;
			cursor++;
		}
		else
			moveIsQuestionable = false;

		//	search for the move
		int moveNumber = pGame->TranslateMove( movementLists[currentDepth], buffer + cursor );

		if( moveNumber == -1 )
		{
			::MessageBox( theWindow, "Illegal move found in opening book!", "ERROR!", MB_ICONHAND | MB_OK );
			break;
		}
		else
		{
			//	add info to opening book and make the move
			MoveInfo &move = movementLists[currentDepth].GetMove( moveNumber );
			pBook->AddMove( (Movement) move, moveIsQuestionable );
			movementLists[currentDepth].MakeMove( moveNumber );
			moves[currentDepth] = moveNumber;
			currentDepth++;
			inCheck[currentDepth] = IsInCheck();
			//	advance the cursor
			while( buffer[cursor] != ' ' && 
				   buffer[cursor] != '\t' && 
				   buffer[cursor] != '\r' && 
				   buffer[cursor] != '\n' && 
				   buffer[cursor] != '\0' && 
				   buffer[cursor] != ';' )
				cursor++;
		}
		if( buffer[cursor] == ';' || 
			buffer[cursor] == '\r' || 
			buffer[cursor] == '\n' || 
			buffer[cursor] == '\0' )
			//	end-of-line
			done = true;
		else
		{
			//	skip whitespace
			while( buffer[cursor] == ' ' || buffer[cursor] == '\t' )
				cursor++;
			if( buffer[cursor] == '\r' || buffer[cursor] == '\n' || 
				buffer[cursor] == '\0' || buffer[cursor] == ';' )
				//	done with this line
				done = true;
		}
	}
	while( --currentDepth >= 1 )
	{
		movementLists[currentDepth].UnmakeMove( moves[currentDepth] );
	}
}

void Board::SetOpeningBook
	( Book *openingBook, 
	  const char *filename )
{
	pBook = openingBook;
	openingBookFilename = new char[strlen(filename)+1];
	strcpy( openingBookFilename, filename );
}

void Board::Think()
{
	bool computerControlled = true;

	if( pGame->IsPositionForbidden() )
	{
		::PostQuitMessage( 0 );
		return;
	}

	while( computerControlled && !theGame->IsGameOver() )
	{
		//	check for draw by repetition
		if( IsDraw() )
		{
#ifdef _WINBOARD_VERSION
			printf( "1/2-1/2\n" ), fflush( stdout );
#endif
			::MessageBox( theWindow, "Draw by repetition", "Stalemate", MB_ICONHAND | MB_OK );
			return;
		}

		//	clear counters for new move
		rootScore = 0;				//	evaluation of the current position
		rootCurrentPlayer = currentPlayer;
		rootPrimaryHash = primaryHash;
		rootSecondaryHash = secondaryHash;

		//	check opening book
		bool foundOpeningMove = false;
		Movement openingMove;
		if( pBook != NULL && currentLine != NULL )
		{
			foundOpeningMove = pBook->Lookup( openingMove );
		}

		if( !foundOpeningMove )
			thinking = true;
		RECT updateRect;
		updateRect.top = 0;
		updateRect.left = 0;
		updateRect.right = 600;
		updateRect.bottom = 75;

		//	clear the history huersitic
//		history[0].ResetContents();
//		history[1].ResetContents();

		//	clear the killer moves
		int i;
		for( i = 0; i < MAX_PLY; i++ )
		{
			killer1[i] = Movement();
			killer2[i] = Movement();
		}

		//	clear statistics
		Statistics::ResetStats();

		movementLists[1].Clear();
		theGame->AboutToStartThinking( currentPlayer );

		//	record current time, and end-of-search time
		_time64( &startOfSearchTime );
		endOfSearchTime = startOfSearchTime + maxThinkTime;
		timeLeft = true;

		currentDepth = 1;

		//	clear thinking list
		for( LRESULT result = 0; result != LB_ERR; )
			result = ::SendMessage( hThinkingList, LB_DELETESTRING, 0, 0 );

		//	*** MOVE GENERATION ***  //	
		MovementList &stack = movementLists[1];
		inCheck[0] = false;
		inCheck[currentDepth] = IsInCheck();
		stack.GenerateMoves( PV );

		//	*** Check for END OF GAME ***  //
		bool performNegamaxSearch = true;
		if( stack.GetCount() == 0 )
		{
#ifdef _WINBOARD_VERSION
			printf( "# no moves\n" ), fflush( stdout );
#endif

			//	since there are no legal moves, the game is over.  the 
			//	question is whether we have won, lost, or drawn.  first, we 
			//	give the game class a chance to decide the outcome by means 
			//	of the TestForWinLossDraw function.  
			int eval;
			//	no moves; we've lost or been stalemated
			if( theGame->IsGoalToCheckmate() && inCheck[currentDepth] )
			{
				char message[80];
#ifdef _WINBOARD_VERSION
				printf( "%s {checkmate}\n", currentPlayer ? "1-0" : "0-1" ), fflush( stdout );
#else
				sprintf( message, "%s loses!", pGame->GetPlayer( currentPlayer ).GetName() );
				::MessageBox( theWindow, message, "Checkmate!", MB_ICONHAND | MB_OK );
#endif
				theGame->GameOver( true );
			}
			//	next, if that didn't decide it, we give the game class a chance 
			//	to decide the outcome by means of the TestForWinLossDraw function.  
			else if( theGame->TestForWinLossDraw( eval ) )
			{
				if( eval == INFINITY )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "%s {checkmate}\n", currentPlayer ? "1-0" : "0-1" ), fflush( stdout );
					sprintf( message, "%s loses!", pGame->GetPlayer( currentPlayer ).GetName() );
#else
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else if( eval == -INFINITY )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( !currentPlayer ? "1-0\n" : "0-1\n" ), fflush( stdout );
#else
					sprintf( message, "%s wins!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "1/2-1/2\n" ), fflush( stdout );
#else
					sprintf( message, "It's a draw!" );
					::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
			}
			//	if we still haven't decided, then neither side has won by checkmate, or 
			//	special victory condition, and therefore the game is a stalemate... 
			//	however, stalemate may still be considered a win or a loss in some variants
			else
			{
				char message[80];
				if( theGame->GetStalemateScore() == 0 )
				{
#ifdef _WINBOARD_VERSION
					printf( "1/2-1/2\n" ), fflush( stdout );
#endif
					sprintf( message, "%s is stalemated!", pGame->GetPlayer( currentPlayer ).GetName() );
				}
				else if( theGame->GetStalemateScore() > 0 )
				{
#ifdef _WINBOARD_VERSION
					printf( currentPlayer ? "1-0\n" : "0-1\n" ), fflush( stdout );
#endif
					sprintf( message, "%s wins!", pGame->GetPlayer( currentPlayer ).GetName() );
				}
				else
				{
#ifdef _WINBOARD_VERSION
					printf( !currentPlayer ? "1-0\n" : "0-1\n" ), fflush( stdout );
#endif
					sprintf( message, "%s loses!", pGame->GetPlayer( currentPlayer ).GetName() );
				}
#ifndef _WINBOARD_VERSION
				::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
#endif
				theGame->GameOver( true );
			}
			currentPlayer = FLIP(currentPlayer);
			performNegamaxSearch = false;
		}
		else if( stack.GetCount() == 1 )
		{
#ifdef _WINBOARD_VERSION
			printf( "# only move\n" ), fflush( stdout );
#endif

			//	only 1 move!
			performNegamaxSearch = false;
			Move( 0 );
		}
		else if( foundOpeningMove )
		{
#ifdef _WINBOARD_VERSION
			printf( "# opening\n" ), fflush( stdout );
#endif
			//	search for this move
			MoveInfo *moveInfo = NULL;
			bool found = false;
			for( int i = 0; i < stack.GetCount() && !found; i++ )
			{
				if( stack.GetMove( i ) == openingMove )
				{
					found = true;
					moveInfo = &(stack.GetMove( i ));
					int index = stack.CopyMoveToList( i, historicalMoves );
					historicalMoves.MakeMove( index );
					UpdatePieceRealSquareNumbers();
					theGame->AddHistoricalMove( *moveInfo );
#ifdef _WINBOARD_VERSION
					if( nRanks <= 9 )
						printf( "move %c%c%c%c\n", moveInfo->fromSquare % nFiles + 'a', moveInfo->fromSquare / nFiles + '1',
							moveInfo->toSquare % nFiles + 'a', moveInfo->toSquare / nFiles + '1' );
					else
						printf( "move %c%c%c%c\n", moveInfo->fromSquare % nFiles + 'a', moveInfo->fromSquare / nFiles + '0',
							moveInfo->toSquare % nFiles + 'a', moveInfo->toSquare / nFiles + '0' );
					fflush( stdout );
#endif
				}
			}
			if( found )
			{
				performNegamaxSearch = false;
			}
			else
				::MessageBox( theWindow, "Illegal move found in opening library", "ERROR!", MB_OK | MB_ICONHAND );
		}
		//  *** MOVE SEARCH ***  //
		if( performNegamaxSearch )
		{
			int move;
			int eval;

			#ifdef TRACE_ON
			traceFile = fopen( "C:\\trace.txt", "w" );
			fprintf( traceFile, "Current position:\n\n" );
			Output( traceFile );
			fprintf( traceFile, "\n" );
			#endif

			//	*** ITERATIVE DEEPENING LOOP ***  //
			bestRootMove = 0;
			int iDepthCompleted = 0;
			int currentAlpha = -INFINITY;
			int currentBeta = INFINITY;
			for( iDepth = 1; iDepth <= maxIDepth && timeLeft; )
			{
				TRACE("\n*** Beginning iDepth %d ***\n", iDepth);

				theGame->AboutToDeepenSearch( currentPlayer );

				// *** PERFORM MOVE SEARCH *** //
				move = SearchRoot( currentAlpha, currentBeta, iDepth, eval );

				// *** ASPIRATION WINDOW *** //
				if( eval <= currentAlpha && currentAlpha > -INFINITY )
				{
					//	evaluation fell outside of aspiration window;
					//	we need to research with a wider window
					currentAlpha = -INFINITY;
					continue;
				}
				else if( eval >= currentBeta && currentBeta < INFINITY )
				{
					//	evaluation fell outside of aspiration window;
					//	we need to research with a wider window
					currentBeta = INFINITY;
					continue;
				}
				//	set aspiration window for next iteration
				currentAlpha = eval - 500;
				currentBeta = eval + 500;

				if( timeLeft )
				{
					iDepthCompleted = iDepth;

					//	update the best move
					bestRootMove = move;
					MoveInfo &moveInfo = movementLists[1].GetMove( bestRootMove );
					hashTable.Store( iDepth, rootAlpha, Exact, (Movement) moveInfo );
					movementLists[1].ReorderRoot( iDepth );
 
					//	update the "thinking information" part of the window
					refreshCausedByBookkeeping = true;
					::InvalidateRect( theWindow, &updateRect, false );
					::UpdateWindow( theWindow );
					refreshCausedByBookkeeping = false;

					//	add entry to thinking list
					char buffer[120];
					if( iDepth >= 3 )
					{
						int score = theBoard->GetRootScore();
						char pvBuffer[100];
						GetPV( pvBuffer );
#ifdef _WINBOARD_VERSION
						if( post ) 
						{ 
							// [HGM] WinBoard Thinking Output
							printf( "%2d %6d %6d %9d %s\n", iDepth, (score+5)/10, 2, 2, pvBuffer ); 
							fflush( stdout );
						}
#endif
						if( score > 0 )
							sprintf( buffer, "I-Depth %d completed\t+%d\tPV: %s", iDepth, score, pvBuffer );
						else
							sprintf( buffer, "I-Depth %d completed\t%d\tPV: %s", iDepth, score, pvBuffer );
						::SendMessage( hThinkingList, LB_INSERTSTRING, 0, (LPARAM) buffer );
					}

					//	perform a time check; if we have used 2/3rds of our
					//	time already, we won't start another interation, because
					//	we wouldn't have time for it to complete
					__time64_t currentTime;
					_time64( &currentTime );
					if( currentTime >= endOfSearchTime )
						timeLeft = false;
					else
					{
						__time64_t timeUsed = currentTime - startOfSearchTime;
						if( timeUsed > (maxThinkTime * 2 / 3) )
							timeLeft = false;
					}
				}
				iDepth++;
			}
			iDepth = iDepthCompleted;

			#ifdef TRACE_ON
			fclose( traceFile );
			#endif

			//	execute the best move found
			Move( bestRootMove );
		}
		::InvalidateRect( theWindow, NULL, false );
		::UpdateWindow( theWindow );
		computerControlled = pGame->GetPlayer( currentPlayer ).IsComputerControlled();

		//	generate valid response moves
		legalUserMoves.GenerateMoves( PV );
		positionTestList.GenerateMoves( PV );

		//	look for checkmate / stalemate
		if( legalUserMoves.GetCount() == 0 )
		{
			//	no moves; we've stalemated or beaten our opponent
			int eval;
			if( theGame->TestForWinLossDraw( eval ) )
			{
				if( eval > 0 )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "1-0\n" ), fflush( stdout );
#else
					sprintf( message, "%s has won!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else if( eval < 0 )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "0-1\n" ), fflush( stdout );
#else
					sprintf( message, "%s has lost!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else
				{
#ifdef _WINBOARD_VERSION
					printf( "1/2-1/2\n" ), fflush( stdout );
#else
					::MessageBox( theWindow, "The game is a Draw!", "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
			}
			else if( theGame->IsGoalToCheckmate() )
			{
				if( IsInCheck() )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "%s {checkmate}\n", currentPlayer ? "1-0" : "0-1" ), fflush( stdout );
#else
					sprintf( message, "%s has won!", pGame->GetPlayer( FLIP(currentPlayer) ).GetName() );
					::MessageBox( theWindow, message, "Checkmate!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else
				{
					char message[80];
					if( theGame->GetStalemateScore() == 0 )
					{
#ifdef _WINBOARD_VERSION
						printf( "1/2-1/2 {stalemate}\n" ), fflush( stdout );
#else
						sprintf( message, "%s is stalemated!", pGame->GetPlayer( currentPlayer ).GetName() );
						::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
#endif
					}
					else
					{
						if( theGame->GetStalemateScore() < 0 )
						{
#ifdef _WINBOARD_VERSION
							printf( "%s {stalemate}\n", currentPlayer ? "0-1" : "1-0" ), fflush( stdout );
#else
							sprintf( message, "%s has won!", pGame->GetPlayer( currentPlayer ).GetName() );
							::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
#endif
						}
						else
						{
#ifdef _WINBOARD_VERSION
							printf( "%s {stalemate}\n", !currentPlayer ? "0-1" : "1-0" ), fflush( stdout );
#else
							sprintf( message, "%s has lost!", pGame->GetPlayer( currentPlayer ).GetName() );
							::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
#endif
						}
					}
					theGame->GameOver( true );
				}
			}
			else
			{
				char message[80];
				if( theGame->GetStalemateScore() == 0 )
				{
#ifdef _WINBOARD_VERSION
					printf( "1/2-1/2 {stalemate}\n" ), fflush( stdout );
#else
					sprintf( message, "The game is a draw!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
				}
				else if( theGame->GetStalemateScore() < 0 )
				{
#ifdef _WINBOARD_VERSION
					printf( "%s {stalemate}\n", currentPlayer ? "0-1" : "1-0" ), fflush( stdout );
#else
					sprintf( message, "%s has won!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
				}
				else
				{
#ifdef _WINBOARD_VERSION
					printf( "%s {stalemate}\n", !currentPlayer ? "0-1" : "1-0" ), fflush( stdout );
#else
					sprintf( message, "%s has lost!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
				}
				theGame->GameOver( true );
			}
		}
		else
		{
			//	check with Game-derived class for win-loss-draw
			int eval;
			bool gameOver = theGame->TestForWinLossDraw( eval );
			if( gameOver )
			{
				if( eval > 0 )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "%s\n", currentPlayer ? "0-1" : "1-0" ), fflush( stdout );
#else
					sprintf( message, "%s has won!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else if( eval < 0 )
				{
					char message[80];
#ifdef _WINBOARD_VERSION
					printf( "%s\n", !currentPlayer ? "0-1" : "1-0" ), fflush( stdout );
#else
					sprintf( message, "%s has lost!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
				else
				{
#ifdef _WINBOARD_VERSION
					printf( "1/2-1/2\n" ), fflush( stdout );
#else
					::MessageBox( theWindow, "The game is a Draw!", "Game Over!", MB_ICONHAND | MB_OK );
#endif
					theGame->GameOver( true );
				}
			}
		}
	}
}

void Board::GetPV( char *line )
{
	//	the only move we know off-hand is the root move.  
	//	to determine the other moves, we look them up in 
	//	the hashtable.  this saves us the time and effort 
	//	associated with constantly 'backing up' the PV 
	//	moves from the leaves back to the root.
	Movement rootMove = movementLists[1].GetMove( bestRootMove );
	pGame->DescribeMove( rootMove, line );
/*	movementLists[1].MakeMove( bestRootMove );
	int score;
	Movement nextMove;
	hashTable.Lookup( 0, 0, score, nextMove );
	if( iDepth > 1 )
	{
		MovementList moveList;
		moveList.SetBoard( this );
		moveList.GenerateMoves( PV );
		if( moveList.MakeMove( nextMove ) )
		{
			int len = (int) strlen( line );
			line[len++] = ' ';
			line[len++] = ' ';
			pGame->DescribeMove( nextMove, line + len );
			int score;
			Movement nextMove;
			hashTable.Lookup( 0, 0, score, nextMove );
			if( iDepth > 2 )
			{
				MovementList moveList;
				moveList.SetBoard( this );
				moveList.GenerateMoves( PV );
				if( moveList.MakeMove( nextMove ) )
				{
					int len = (int) strlen( line );
					line[len++] = ' ';
					line[len++] = ' ';
					pGame->DescribeMove( nextMove, line + len );
					int score;
					Movement nextMove;
					hashTable.Lookup( 0, 0, score, nextMove );
					if( iDepth > 3 )
					{
						MovementList moveList;
						moveList.SetBoard( this );
						moveList.GenerateMoves( PV );
						if( moveList.MakeMove( nextMove ) )
						{
							int len = (int) strlen( line );
							line[len++] = ' ';
							line[len++] = ' ';
							pGame->DescribeMove( nextMove, line + len );
							int score;
							Movement nextMove;
							hashTable.Lookup( 0, 0, score, nextMove );
							if( iDepth > 4 )
							{
								MovementList moveList;
								moveList.SetBoard( this );
								moveList.GenerateMoves( PV );
								if( moveList.MakeMove( nextMove ) )
								{
									int len = (int) strlen( line );
									line[len++] = ' ';
									line[len++] = ' ';
									pGame->DescribeMove( nextMove, line + len );
									moveList.UnmakeMove();
								}
							}
							moveList.UnmakeMove();
						}
					}
					moveList.UnmakeMove();
				}
			}
			moveList.UnmakeMove();
		}
	}
	movementLists[1].UnmakeMove();*/
}

void Board::RecalculateMaterial()
{
	material[0] = 0;
	pawnMaterial[0] = 0;
	for( int x = 0; x < nPieces[0]; x++ )
		if( !pieces[0][x]->IsCaptured() )
		{
			material[0] += pieces[0][x]->GetBaseValue();
			if( pieces[0][x]->IsPawn() )
				pawnMaterial[0] += pieces[0][x]->GetBaseValue();
		}
	material[1] = 0;
	pawnMaterial[1] = 0;
	for( int y = 0; y < nPieces[1]; y++ )
		if( !pieces[1][y]->IsCaptured() )
		{
			material[1] += pieces[1][y]->GetBaseValue();
			if( pieces[1][y]->IsPawn() )
				pawnMaterial[1] += pieces[1][y]->GetBaseValue();
		}
}

int Board::GetPiecePostedBonus
	( int playerNumber,
	  int squareNumber,
	  PieceType &pawnType )
{
	int bonus = outpostSquareBonuses[flipSquare[playerNumber][squareNumber]];
	if( bonus > 0 )
	{
		switch( bitBoardType )
		{
		  case BITBOARD_64:
			if( !(bool) (static_cast<PieceType64 &>(pawnType).GetPieces( FLIP(playerNumber) ) & 
				bb64OutpostNoPawnSquares[playerNumber][squareNumber]) )
				return bonus;
			break;

		  case BITBOARD_96:
			if( !(bool) (static_cast<PieceType96 &>(pawnType).GetPieces( FLIP(playerNumber) ) & 
				bb96OutpostNoPawnSquares[playerNumber][squareNumber]) )
				return bonus;
			break;

		  case BITBOARD_128:
			if( !(bool) (static_cast<PieceType128 &>(pawnType).GetPieces( FLIP(playerNumber) ) & 
				bb128OutpostNoPawnSquares[playerNumber][squareNumber]) )
				return bonus;
			break;
		}
	}
	return 0;
}

void Board::Output( FILE *fi )
{
	fprintf( fi, "\n" );
	for( int r = GetNumberOfRanks() - 1; r >= 0; r-- )
	{
		for( int f = 0; f < GetNumberOfFiles(); f++ )
		{
			int nSquare = r * GetNumberOfFiles() + f;
			char ch = ' ';
			if( squareContents[nSquare] != NULL )
				if( squareContents[nSquare]->GetPlayerNumber() == 0 )
					ch = squareContents[nSquare]->GetType().GetNotation()[0];
				else
					ch = tolower( squareContents[nSquare]->GetType().GetNotation()[0] );
			fprintf( fi, "%c ", ch );
		}
		fprintf( fi, "\n" );
	}
}

void Board::AssertValid()
{
	word32 priHash = primaryHash;
	word32 secHash = secondaryHash;
	word32 priPawnHash = primaryPawnHash;
	word32 secPawnHash = secondaryPawnHash;
	RecalculateHashes();
	ASSERT(primaryHash == priHash);
	ASSERT(secondaryHash == secHash);
	ASSERT(primaryPawnHash == priPawnHash);
	ASSERT(secondaryPawnHash == secPawnHash);

	int materialCount[2] = { 0, 0 };
	int pawnMaterialCount[2] = { 0, 0 };
	int captureCount = 0;
	int piecesFound = 0;
	for( int plr = 0; plr < 2; plr++ )
	{
		for( int currentPiece = 0; currentPiece < nPieces[plr]; currentPiece++ )
		{
			//	loop through all pieces owned by this player
			if( !( pieces[plr][currentPiece]->IsCaptured() ) )
			{
				Piece *piece = pieces[plr][currentPiece];
				ASSERT(piece != NULL);
				int sqr = piece->GetSquareNumber();
				ASSERT(sqr >= 0 && sqr <= nSquares);
				ASSERT(squareContents[sqr] == piece);
				materialCount[plr] += piece->GetBaseValue();
				if( piece->IsPawn() )
					pawnMaterialCount[plr] += piece->GetBaseValue();
			}
			else
				captureCount++;
		}
	}
	for( int x = 0; x < GetNumberOfSquares(); x++ )
	{
		Piece *piece = squareContents[x];
		if( piece != NULL )
		{
			piecesFound++;
			ASSERT(!piece->IsCaptured());
			ASSERT(piece->GetSquareNumber() == x);
		}
	}
	if( bitBoardType == BITBOARD_64 )
	{
		ASSERT(piecesFound == bb64_blocker.GetBitCount());
		ASSERT(piecesFound == bb64_blocker90.GetBitCount());
		ASSERT(piecesFound == bb64_blocker45.GetBitCount());
		ASSERT(piecesFound == bb64_blocker135.GetBitCount());
	}
	if( bitBoardType == BITBOARD_96 )
	{
		ASSERT(piecesFound == bb96_blocker.GetBitCount());
		ASSERT(piecesFound == bb96_blocker90.GetBitCount());
		ASSERT(piecesFound == bb96_blocker45.GetBitCount());
		ASSERT(piecesFound == bb96_blocker135.GetBitCount());
	}
	if( bitBoardType == BITBOARD_128 )
	{
		ASSERT(piecesFound == bb128_blocker.GetBitCount());
		ASSERT(piecesFound == bb128_blocker90.GetBitCount());
		ASSERT(piecesFound == bb128_blocker45.GetBitCount());
		ASSERT(piecesFound == bb128_blocker135.GetBitCount());
	}
	ASSERT(captureCount == nCapturedPieces);
	ASSERT(materialCount[0] == material[0]);
	ASSERT(materialCount[1] == material[1]);
	ASSERT(pawnMaterialCount[0] == pawnMaterial[0]);
	ASSERT(pawnMaterialCount[1] == pawnMaterial[1]);
}

void Board::AssertValidSetup()
{
	for( int x = 0; x < nPieceTypes; x++ )
	{
		PieceType &pieceType = *pieceTypes[x];
		for( int y = 0; y < pieceType.GetMoveCapabilityCount(); y++ )
		{
			MovementCapability &move = pieceType.GetMoveCapability( y );
			ASSERT(move.GetDirectionNumber() >= 0 && move.GetDirectionNumber() < directionCount);
		}
	}
}

void Board::ClearAnyExtraCaptures()
{
	if( extraCaptures > 0 )
	{
		for( int x = 0; x < extraCaptures; x++ )
			InvalidateSquare( extraCaptureSquares[x] );
	}
	extraCaptures = 0;
}

int Board::CanMoveToSquare
	( int nSquare,
	  bool updateUI )
{
	if( pieceLifted == NULL )
		return 0;
	int fromSquare = pieceLifted->GetSquareNumber();
	for( int x = 0; x < legalUserMoves.GetCount(); x++ )
	{
		int captures;
		MoveInfo &move = legalUserMoves.GetMove( x, captures );
		if( move.fromSquare == fromSquare &&
			move.toSquare == nSquare )
		{
			if( move.type == StandardMove )
			{
				if( updateUI )
					ClearAnyExtraCaptures();
				return 1;
			}
			else if( move.type == StandardCapture )
			{
				if( updateUI )
					ClearAnyExtraCaptures();
				return 2;
			}
			else
			{
				if( updateUI )
				{
					ClearAnyExtraCaptures();
					legalUserMoves.AdjustUIElements( x );
				}
				if( captures > 0 )
					return 2;
				return 1;
			}
		}
	}
	return 0;
}

void Board::DrawBoard
	( HWND hwnd, 
	  HDC hdc,
	  LPPAINTSTRUCT ps )
{
	Brush lightSquareBrush( squareColor1 );
	Brush darkSquareBrush( squareColor2 );
	Brush thirdSquareBrush( squareColor3 );
	Brush borderBrush( borderColor );
	Brush hilightedSquareBrush( RGB(31, 254, 31) );
	Brush captureSquareBrush( RGB(240, 0, 0) );
	RECT rect;

	int topMargin    = 75;
	int leftMargin   = 20;
	if( theBoard->GetNumberOfFiles() < 8 )
		leftMargin += 54;


	//	DRAW CHESS BOARD
	::SelectObject( hdc, borderBrush );
	rect.left = leftMargin;
	rect.top = topMargin;
	rect.right = rect.left + 2 * borderWidth + nFiles * squareWidth;
	rect.bottom = rect.top + 2 * borderWidth + nRanks * squareHeight;
	::Rectangle( hdc, rect.left, rect.top, rect.left + borderWidth + 1, rect.bottom + 1 );
	::Rectangle( hdc, rect.left, rect.top, rect.right + 1, rect.top + borderWidth + 1 );
	::Rectangle( hdc, rect.left, rect.bottom - borderWidth, rect.right + 1, rect.bottom + 1 );
	::Rectangle( hdc, rect.right - borderWidth, rect.top, rect.right + 1, rect.bottom + 1 );

	Pen blackPen( RGB(0, 0, 0), 1 );
	if( boardDisplayType == BOARD_IS_UNCHECKERED )
	{
		::SelectObject( hdc, blackPen );
	}

	int totalHeightOfSquares = theBoard->GetNumberOfRanks() * squareHeight;
	Piece *liftedPiece = theBoard->GetPieceBeingLifted();

	//	draw rank & file labels
	if( borderColor == RGB(192, 192, 192) )
		::SetTextColor( hdc, RGB(0, 0, 0) );
	else
		::SetTextColor( hdc, RGB(255, 255, 0) );
	if( ps->rcPaint.left < leftMargin + borderWidth )
	{
		for( int x = 0; x < nRanks; x++ )
		{
			RECT r;
			r.left = leftMargin;
			r.right = leftMargin + borderWidth;
			r.top = topMargin + borderWidth + ((nRanks - x - 1) * squareHeight);
			r.bottom = r.top + squareHeight;
			char *str;
			str = GetNameOfRank( rotateBoard ? nRanks - x - 1 : x );
			::DrawText( hdc, str, (int) strlen( str ), &r, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
		}
	}
	if( ps->rcPaint.top < topMargin + borderWidth + squareWidth * nRanks )
	{
		for( int y = 0; y < nFiles; y++ )
		{
			RECT r;
			r.left = leftMargin + borderWidth + y * squareWidth;
			r.right = r.left + squareWidth;
			r.top = topMargin + borderWidth + squareWidth * nRanks;
			r.bottom = r.top + borderWidth;
			char *str;
			str = GetNameOfFile( rotateBoard ? nFiles - y - 1 : y );
			::DrawText( hdc, str, (int) strlen( str ), &r, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
		}
	}
	::SetTextColor( hdc, RGB(255, 255, 255) );

	//	draw squares
	HBITMAP *squareTexture = NULL;
	for( int rank = 0; rank < nRanks; rank++ )
	{
		for( int file = 0; file < nFiles; file++ )
		{
			if( (ps->rcPaint.right >= leftMargin + borderWidth + file * squareWidth &&  
				 ps->rcPaint.left <= leftMargin + borderWidth + file * squareWidth + squareWidth + 1) && 
				(ps->rcPaint.bottom >= topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight) && 
				 ps->rcPaint.top <= topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight) + squareHeight + 1) )
			{
				bool onDestinationSquare = false;
				Pen destinationOutlinePen;
				int squareNumber = rank * theBoard->GetNumberOfFiles() + file;
				if( rotateBoard )
					squareNumber = flipSquare[1][squareNumber];

				//	select appropriate brush and/or texture based on the square 
				//	and the board display type selected
				if( boardDisplayType == BOARD_IS_UNCHECKERED )
				{
					::SelectObject( hdc, lightSquareBrush );
					squareTexture = squareTextureBitmaps[0];
				}
				else if( boardDisplayType == BOARD_IS_CHECKERED )
				{
					if( (rank + file) % 2 == 1 )
					{
						::SelectObject( hdc, lightSquareBrush );
						squareTexture = squareTextureBitmaps[0];
					}
					else
					{
						::SelectObject( hdc, darkSquareBrush );
						squareTexture = squareTextureBitmaps[1];
					}
				}
				else if( boardDisplayType == BOARD_IS_THREE_COLORED )
				{
					if( (rank + file) % 2 == 1 )
					{
						::SelectObject( hdc, lightSquareBrush );
						squareTexture = squareTextureBitmaps[0];
					}
					else
					{
						if( rank % 2 == 0 )
						{
							::SelectObject( hdc, darkSquareBrush );
							squareTexture = squareTextureBitmaps[1];
						}
						else
						{
							::SelectObject( hdc, thirdSquareBrush );
							squareTexture = squareTextureBitmaps[2];
						}
					}
				}
				//	draw the square
				rect.left = leftMargin + borderWidth + file * squareWidth;
				rect.top = topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight);
				rect.right = rect.left + squareWidth + 1;
				rect.bottom = rect.top + squareHeight + 1;
				if( liftedPiece != NULL && squareNumber == destinationSquare )
				{
					onDestinationSquare = true;
					destinationOutlinePen.CreateSolidPen( RGB(0, 0, 0), 4 );
					::SelectObject( hdc, destinationOutlinePen );
					rect.left += 2;
					rect.top += 2;
					rect.right -= 2;
					rect.bottom -= 2;
				}
				if( squareTexture == NULL || onDestinationSquare )
				{
					::Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
				}
				if( squareTexture != NULL )
				{
					int bitmapNumber = squareNumber % 19;
					HDC hMemDC = ::CreateCompatibleDC( hdc );
					HBITMAP hbmOld = (HBITMAP) ::SelectObject( hMemDC, squareTexture[bitmapNumber] );
					if( onDestinationSquare )
						::BitBlt( hdc, rect.left + 2, rect.top + 2, squareWidth - 8, squareHeight - 8, hMemDC, 5, 5, SRCCOPY );
					else
						::BitBlt( hdc, rect.left, rect.top, squareWidth, squareHeight, hMemDC, 2, 2, SRCCOPY );
					::SelectObject( hMemDC, hbmOld );
					::DeleteDC( hMemDC );
				}
				if( onDestinationSquare )
				{
					::SelectObject( hdc, ::GetStockObject( NULL_PEN ) );
					rect.left -= 2;
					rect.top -= 2;
					rect.right += 2;
					rect.bottom += 2;
				}
				if( liftedPiece != NULL && theBoard->CanMoveToSquare( squareNumber ) != 0 )
				{
					Piece *target = theBoard->GetSquareContents( squareNumber );
					if( theBoard->CanMoveToSquare( squareNumber ) == 1 )
						::SelectObject( hdc, hilightedSquareBrush );
					else
						::SelectObject( hdc, captureSquareBrush );
					if( selectedPieceSet == PIECE_SET_EURASIAN )
					{
						rect.right -= 1;
						rect.bottom -= 1;
						::SelectObject( hdc, blackPen );
						::RoundRect( hdc, rect.left, rect.top, rect.right, rect.bottom, 56, 56 );
						::SelectObject( hdc, ::GetStockObject( NULL_PEN ) );
					}
					else
					{
						rect.left += 4;
						rect.top += 4;
						rect.right -= 4;
						rect.bottom -= 4;
						::SelectObject( hdc, blackPen );
						::RoundRect( hdc, rect.left, rect.top, rect.right, rect.bottom, 49, 49 );
						::SelectObject( hdc, ::GetStockObject( NULL_PEN ) );
					}
					if( boardDisplayType == BOARD_IS_UNCHECKERED )
						::SelectObject( hdc, blackPen );
				}
			}
		}
	}
}

void Board::DrawPiecesOnBoard
	( HWND hwnd, 
	  HDC hdc, 
	  LPPAINTSTRUCT ps )
{
	RECT rect;
	int topMargin    = 75;
	int leftMargin   = 20;
	if( theBoard->GetNumberOfFiles() < 8 )
		leftMargin += 54;
	Piece *liftedPiece = theBoard->GetPieceBeingLifted();
	int totalHeightOfSquares = theBoard->GetNumberOfRanks() * squareHeight;

	//	loop through all pieces, and display those that have not been captured
	for( int playerNumber = 0; playerNumber < 2; playerNumber++ )
	{
		for( int pieceNumber = 0; pieceNumber < nPieces[playerNumber]; pieceNumber++ )
		{
			Piece *piece = pieces[playerNumber][pieceNumber];
			int squareNumber = piece->GetRealSquareNumber();
			if( !piece->IsReallyCaptured() )
			{
				if( rotateBoard )
					squareNumber = flipSquare[1][squareNumber];

				int rank = GetSquareRank( squareNumber );
				int file = GetSquareFile( squareNumber );

				if( (ps->rcPaint.right >= leftMargin + borderWidth + file * squareWidth &&  
					ps->rcPaint.left <= leftMargin + borderWidth + file * squareWidth + squareWidth + 1) && 
					(ps->rcPaint.bottom >= topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight) && 
					ps->rcPaint.top <= topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight) + squareHeight + 1) )
				{
					rect.left = leftMargin + borderWidth + file * squareWidth;
					rect.top = topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight);
					rect.right = rect.left + squareWidth + 1;
					rect.bottom = rect.top + squareHeight + 1;

					//	DRAW PIECE
					if( piece != liftedPiece )
					{
						HBITMAP hBitmap = piece->GetBitmap();
						if( hBitmap != NULL )
						{
							DrawTransparentBitmap( hdc, hBitmap, (short) rect.left /*- (liftedPiece != NULL && theBoard->CanMoveToSquare( squareNumber ) ? 5 : 0)*/, 
								(short) rect.top /* - (liftedPiece != NULL && theBoard->CanMoveToSquare( squareNumber ) ? 5 : 0)*/, RGB(0, 255, 0), true,
								playerNumber == 0 ? RGB(255, 255, 255) : RGB(255, 0, 0), 
								playerNumber == 0 ? pieceColor1 : pieceColor2 );
						}
					}
				}
			}
		}
	}
	//	loop through all the squares and draw a red 'X' over 
	//	all those which are in the extraCaptures array
	for( int rank = 0; rank < nRanks; rank++ )
	{
		for( int file = 0; file < nFiles; file++ )
		{
			int squareNumber = rank * theBoard->GetNumberOfFiles() + file;
			if( rotateBoard )
				squareNumber = flipSquare[1][squareNumber];

			rect.left = leftMargin + borderWidth + file * squareWidth;
			rect.top = topMargin + borderWidth + totalHeightOfSquares - ((rank + 1) * squareHeight);
			rect.right = rect.left + squareWidth + 1;
			rect.bottom = rect.top + squareHeight + 1;

			if( extraCaptures > 0 )
			{
				for( int x = 0; x < extraCaptures; x++ )
					if( extraCaptureSquares[x] == squareNumber )
					{
						Pen redPen( RGB(255, 0, 0), 8 );
						Pen oldPen( (HPEN) ::SelectObject( hdc, redPen ), false );
						::MoveToEx( hdc, rect.left + 15, rect.top + 15, NULL );
						::LineTo( hdc, rect.right - 15, rect.bottom - 15 );
						::MoveToEx( hdc, rect.right - 15, rect.top + 15, NULL );
						::LineTo( hdc, rect.left + 15, rect.bottom - 15 );
						::SelectObject( hdc, oldPen );
					}
			}
		}
	}
	if( liftedPiece != NULL )
	{
		HBITMAP hBitmap = liftedPiece->GetBitmap();
		DrawTransparentBitmap( hdc, hBitmap, mouseX, mouseY, RGB(0, 255, 0), false,
			liftedPiece->GetPlayerNumber() == 0 ? RGB(255, 255, 255) : RGB(255, 0, 0), 
			liftedPiece->GetPlayerNumber() == 0 ? pieceColor1 : pieceColor2, false, true );
	}
}

int Board::GetBoardRightEdge()
{
	int topMargin    = 75;
	int leftMargin   = 20;
	if( theBoard->GetNumberOfFiles() < 8 )
		leftMargin += 54;

	return 2 * borderWidth + nFiles * squareWidth + leftMargin;
}

int Board::GetBoardBottomEdge()
{
	int topMargin    = 75;
	int leftMargin   = 20;
	if( theBoard->GetNumberOfFiles() < 8 )
		leftMargin += 54;

	return 2 * borderWidth + nRanks * squareHeight + topMargin;
}

void Board::InvalidateSquare( int nSquare )
{
	//	various dimensions
	int topMargin    = 75;
	int leftMargin   = 20;
	if( theBoard->GetNumberOfFiles() < 8 )
		leftMargin += 54;

	if( rotateBoard )
		nSquare = flipSquare[1][nSquare];

	RECT rect;
	rect.left = leftMargin + borderWidth + (nSquare % nFiles) * squareWidth - 3;
	rect.top = topMargin + borderWidth + (nRanks - (nSquare / nFiles) - 1) * squareHeight - 3;
	rect.right = rect.left + squareWidth + 6;
	rect.bottom = rect.top + squareHeight + 6;
	::InvalidateRect( theWindow, &rect, false );
}

int Board::SquareHitTest( int x, int y )
{
	//	various dimensions
	int topMargin    = 75;
	int leftMargin   = 20;
	if( theBoard->GetNumberOfFiles() < 8 )
		leftMargin += 54;

	//	adjust and find square clicked on
	x -= leftMargin;
	x -= borderWidth;
	y -= topMargin;
	y -= borderWidth;

	int rank = theBoard->GetNumberOfRanks() - (y / squareHeight) - 1;
	int file = x / squareWidth;

	if( rank < 0 || rank >= nRanks || file < 0 || file >= nFiles )
		//	not on a square
		return -1;

	int nSquare = rank * nFiles + file;

	if( rotateBoard )
		return flipSquare[1][nSquare];

	return nSquare;
}

void Board::PerformMove
	( int selectedMoveIndex )
{
	int moveNumber = legalUserMoves.CopyMoveToList( selectedMoveIndex, historicalMoves );
	MoveInfo &selectedMove = legalUserMoves.GetMove( selectedMoveIndex );
	historicalMoves.MakeMove( moveNumber );
	UpdatePieceRealSquareNumbers();

	if( pGame->IsPositionForbidden() )
	{
		::PostQuitMessage( 0 );
		return;
	}

	//	add move to game history (so it shows up in the move list)
	theGame->AddHistoricalMove( selectedMove );

	::InvalidateRect( theWindow, NULL, false );
	::UpdateWindow( theWindow );
	AssertValid();
	bool computerControlled = pGame->GetPlayer( currentPlayer ).IsComputerControlled();
	if( computerControlled )
	{
		showStats = true;
		Think();
	}
	else
	{
		showStats = false;
		legalUserMoves.Clear();
		inCheck[0] = false;
		inCheck[1] = IsInCheck();
		currentDepth = 1;
		int eval = 0;

		//	generate legal response moves
		legalUserMoves.GenerateMoves( PV );
		positionTestList.GenerateMoves( PV );

		//	look for checkmate / stalemate
		if( legalUserMoves.GetCount() == 0 )
		{
			//	no moves; we've stalemated or beaten our opponent
			if( theGame->IsGoalToCheckmate() && IsInCheck() )
			{
				char message[80];
				sprintf( message, "Player wins as %s!", pGame->GetPlayer( FLIP(currentPlayer) ).GetName() );
				::MessageBox( theWindow, message, "Checkmate!", MB_ICONHAND | MB_OK );
				theGame->GameOver( true );
			}
			else if( theGame->TestForWinLossDraw( eval ) )
			{
				if( eval == INFINITY )
				{
					char message[80];
					sprintf( message, "%s loses!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
					theGame->GameOver( true );
				}
				else if( eval == -INFINITY )
				{
					char message[80];
					sprintf( message, "%s wins!", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
					theGame->GameOver( true );
				}
				else
				{
					char message[80];
					sprintf( message, "It's a draw!" );
					::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
					theGame->GameOver( true );
				}
			}
			else
			{
				char message[80];
				if( theGame->GetStalemateScore() == 0 )
					sprintf( message, "%s is stalemated!", pGame->GetPlayer( currentPlayer ).GetName() );
				else if( theGame->GetStalemateScore() < 0 )
					sprintf( message, "%s wins!", pGame->GetPlayer( currentPlayer ).GetName() );
				else
					sprintf( message, "%s loses!", pGame->GetPlayer( currentPlayer ).GetName() );
				::MessageBox( theWindow, message, "Stalemate!", MB_ICONHAND | MB_OK );
				theGame->GameOver( true );
			}
		}
		else
		{
			//	check with Game-derived class for win-loss-draw
			int eval;
			bool gameOver = theGame->TestForWinLossDraw( eval );
			if( gameOver )
			{
				if( eval > 0 )
				{
					char message[80];
					sprintf( message, "%s wins", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
					theGame->GameOver( true );
				}
				else if( eval < 0 )
				{
					char message[80];
					sprintf( message, "%s loses", pGame->GetPlayer( currentPlayer ).GetName() );
					::MessageBox( theWindow, message, "Game Over!", MB_ICONHAND | MB_OK );
					theGame->GameOver( true );
				}
				else
				{
					::MessageBox( theWindow, "It's a Draw!", "Game Over!", MB_ICONHAND | MB_OK );
					theGame->GameOver( true );
				}
			}
		}
	}
}

/************************************************************************\ 
**************************************************************************
**                                                                      **
**                         helper functions                             **
**                                                                      **
**************************************************************************
\************************************************************************/

// ***********************************************************************
// *
// *	Move
// *
// *	this function is called once the search process has completed, 
// *	and a move has been selected.  this function makes the actual 
// *	move by selecting the specified move number from movementStacks[1]
// *	and executing it by copying it to the historicalMoves movement stack
// *	and that stack performs the actual execution.

void Board::Move( int moveNumber )
{
	MoveInfo &moveInfo = movementLists[1].GetMove( moveNumber );
	int fromSquareNumber = moveInfo.fromSquare;
	int toSquareNumber = moveInfo.toSquare;

#ifdef _WINBOARD_VERSION
	char promo = '\n';
	if( moveInfo.promotion )
	{
	    char *name = moveInfo.promotion->GetFullName();
	    printf( "# promotion to %s\n", name );
	    promo = name[0];
	    if( !strcmp( name, "Knight" ) )
			promo = 'n';
	    if( promo >= 'A' && promo <= 'Z' ) 
			promo += 'a' - 'A';
	}
	if( nRanks <= 9 )
	{
		printf( "move %c%c%c%c%c\n", fromSquareNumber % nFiles + 'a', fromSquareNumber / nFiles + '1',
			toSquareNumber % nFiles + 'a', toSquareNumber / nFiles + '1', promo );
	}
	else
	{
		printf( "move %c%c%c%c%c\n", fromSquareNumber % nFiles + 'a', fromSquareNumber / nFiles + '0',
			toSquareNumber % nFiles + 'a', toSquareNumber / nFiles + '0', promo );
	}
	fflush( stdout );
#endif

	//	the historicalMoves movement stack performs the actual move
	int move = movementLists[1].CopyMoveToList( moveNumber, historicalMoves );
	historicalMoves.MakeMove( move );
	UpdatePieceRealSquareNumbers();

	if( pGame->IsPositionForbidden() )
	{
		::PostQuitMessage( 0 );
		return;
	}

	if( currentLine != NULL )
	{
		//	add this move info to the current move line
		int length =  (int) strlen( currentLine );
		if( length > 0 )
			currentLine[length++] = ' ';
		pGame->DescribeMove( moveInfo, NULL, NULL, currentLine + length );
	}

	//	add move to game history
	theGame->AddHistoricalMove( moveInfo );
}

