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

                                 ChessV

                   COPYRIGHT (C) 2005 BY GREGORY STRONG

This file is part of ChessV.  ChessV is free software; you can redistribute
it and/or modify it under the terms of the GNU General Public License as 
published by the Free Software Foundation; either version 2 of the License, 
or (at your option) any later version.

ChessV is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
more details; the file 'COPYING' contains the License text, but if for
some reason you need a copy, please write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

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


#include "StdAfx.h"
#include "../../ChessV.h"
#include "../../PieceType.h"
#include "../../Piece.h"
#include "../../Direction.h"
#include "../../Rand.h"
#include "../../GameParameters.h"
#include "ChessWithUltimaPiecesGame.h"
#include "Decimal_Types.h"

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


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


void StartCWUPGame()
{
	if( !justLoaded )
		int rtn = (int) ::DialogBox( theInstance, MAKEINTRESOURCE(IDD_CWUP_SELECT_DIALOG), NULL, 
			reinterpret_cast<DLGPROC>(CWUP_SelectDlgProc) );

	theBoard = new Board( 10, 10 );
	char *player0Name = "White";
	char *player1Name = "Black";
	LookupStringParameter( "player1", player0Name );
	LookupStringParameter( "player2", player1Name );
	CreatePlayers( player0Name, player1Name, whiteComp, blackComp );

	strcpy( gameSelection, "Chess with Ultima Pieces" );
}

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

ChessWUPGame::ChessWUPGame( Board &board, Player &whitePlayer, Player &blackPlayer ):
	DecimalChessGame(board, whitePlayer, blackPlayer)
{
	//	all rules and evaluation values set in the base class
	//	(decimalChessGame) are fine, with the exception of two 
	//	things which we will now change...

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

	//	turn off static exchange evaluation, since the 
	//	the static exchange evaluator cannot handle the 
	//	Ultima pieces, with their ability of Baroque capture
	useStaticExchangeEvaluation = false;
}

ChessWUPGame::~ChessWUPGame()
{
}

// *** CHANGE RULES FOR PARTICULAR (BUILT-IN) VARIANT *** //
void ChessWUPGame::ChangeRulesByVariant
	( char *gameName,
	  char *&array,
	  char *&book )
{
	//	assume, for now, standard castling and promotion rules
	pawnPromotionType = PAWN_PROMOTION_TYPE_STANDARD;
	castlingType = CASTLING_TYPE_STANDARD;
	pawnPromotionRank = 9;
	wackyPawnRestriction = false;
	castleOutOfCheck = false;
	kingsLeap = false;

	// *** CHESS WITH ULTIMA PIECES *** //
	if( !strcmp( selectedVariant->name, "Chess with Ultima Pieces" ) )
	{
		//	add correct pawn type
		pawnPieceTypes[0] = pawnPieceTypes[1] = &Decimal3StepPawn::decimal3StepPawn;
		board.AddPlayerPieceTypeBothPlayers( Decimal3StepPawn::decimal3StepPawn );
		//	lookup variant (which Ultima piece we are using)
		char *parameterLookupValue;
		if( LookupStringParameter( "ultima-piece", parameterLookupValue ) )
		{
			if( stringCompNoCase( parameterLookupValue, "Withdrawer" ) )
			{
				variant = CWUP_VARIANT_WITHDRAWER;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalWithdrawer::decimalWithdrawer;
				board.AddPlayerPieceTypeBothPlayers( DecimalWithdrawer::decimalWithdrawer );
				//	place pieces
				array = "rn_wdbqkb_wdnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_WDBQKB_WDNR";
				//	set name of opening book
				book = "openings\\WithdrawerChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Coordinator" ) )
			{
				variant = CWUP_VARIANT_COORDINATOR;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalCoordinator::decimalCoordinator;
				board.AddPlayerPieceTypeBothPlayers( DecimalCoordinator::decimalCoordinator );
				//	place pieces
				array = "rn_cobqkb_conr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_COBQKB_CONR";
				//	set name of opening book
				book = "openings\\CoordinatorChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Long Leaper" ) )
			{
				variant = CWUP_VARIANT_LONG_LEAPER;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalLongLeaper::decimalLongLeaper;
				board.AddPlayerPieceTypeBothPlayers( DecimalLongLeaper::decimalLongLeaper );
				//	place pieces
				array = "rn_llbqkb_llnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_LLBQKB_LLNR";
				//	set name of opening book
				book = "openings\\LongLeaperChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Immobilizer" ) )
			{
				variant = CWUP_VARIANT_IMMOBILIZER;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalImmobilizer::decimalImmobilizer;
				board.AddPlayerPieceTypeBothPlayers( DecimalImmobilizer::decimalImmobilizer );
				//	place pieces
				array = "rn_imbqkb_imnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_IMBQKB_IMNR";
				//	set name of opening book
				book = "openings\\ImmoblizerChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Advancer" ) )
			{
				variant = CWUP_VARIANT_ADVANCER;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalAdvancer::decimalAdvancer;
				board.AddPlayerPieceTypeBothPlayers( DecimalAdvancer::decimalAdvancer );
				//	place pieces
				array = "rn_wdbqkb_wdnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_ADBQKB_ADNR";
				//	set name of opening book
				book = "openings\\AdvancerChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Swapper" ) )
			{
				variant = CWUP_VARIANT_SWAPPER;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalSwapper::decimalSwapper;
				board.AddPlayerPieceTypeBothPlayers( DecimalSwapper::decimalSwapper );
				//	place pieces
				array = "rn_swbqkb_swnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_SWBQKB_SWNR";
				//	set name of opening book
				book = "openings\\SwapperChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Pushme-Pullyu" ) )
			{
				variant = CWUP_VARIANT_PUSHME_PULLYU;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalPushmePullyu::decimalPushmePullyu;
				board.AddPlayerPieceTypeBothPlayers( DecimalPushmePullyu::decimalPushmePullyu );
				//	place pieces
				array = "rn_ppbqkb_ppnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_PPBQKB_PPNR";
				//	set name of opening book
				book = "openings\\PushmePullyuChess.txt";
			}
			else if( stringCompNoCase( parameterLookupValue, "Mimotaur" ) )
			{
				variant = CWUP_VARIANT_MIMOTAUR;
				majorPieceTypes[0][1] = majorPieceTypes[1][1] = &DecimalMimotaur::decimalMimotaur;
				board.AddPlayerPieceTypeBothPlayers( DecimalMimotaur::decimalMimotaur );
				//	place pieces
				array = "rn_mtbqkb_mtnr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RN_MTBQKB_MTNR";
				//	set name of opening book
				book = "openings\\MimotaurChess.txt";
			}
		}
		else
			ASSERT(FALSE);
	}
}

void ChessWUPGame::AddSpecialMoves
	( int currentPlayer, 
	  MovementList &stack, 
	  GameRec &gameRecord, 
	  bool quiescentSearch )
{
	BitBoard128 bb128;

	// *** COORDINATOR *** //
	bb128 = DecimalCoordinator::decimalCoordinator.GetPieces( currentPlayer );
	while( bb128 )
	{
		int square = bb128.GetFirstBit();
		bb128.ToggleBit( square );
		Piece *piece = board.GetSquareContents( square );
		Piece *king = board.GetKing( currentPlayer );
		GenerateMovesForCoordinator( piece, king, stack, gameRecord, quiescentSearch );
	}
	// *** LONG LEAPER *** //
	bb128 = DecimalLongLeaper::decimalLongLeaper.GetPieces( currentPlayer );
	while( bb128 )
	{
		int square = bb128.GetFirstBit();
		bb128.ToggleBit( square );
		Piece *piece = board.GetSquareContents( square );
		GenerateMovesForLongLeaper( piece, stack, gameRecord, quiescentSearch );
	}
	// *** MIMOTAUR *** //
	bb128 = DecimalMimotaur::decimalMimotaur.GetPieces( currentPlayer );
	while( bb128 )
	{
		int square = bb128.GetFirstBit();
		bb128.ToggleBit( square );
		Piece *piece = board.GetSquareContents( square );
		GenerateMovesForMimotaur( piece, stack, gameRecord, quiescentSearch );
	}
	// *** SWAPPER *** //
	bb128 = DecimalSwapper::decimalSwapper.GetPieces( currentPlayer );
	while( bb128 )
	{
		int square = bb128.GetFirstBit();
		bb128.ToggleBit( square );
		Piece *piece = board.GetSquareContents( square );
		GenerateMovesForSwapper( piece, stack, gameRecord, quiescentSearch );
	}

	DecimalChessGame::AddSpecialMoves( currentPlayer, stack, gameRecord, quiescentSearch );
}

bool ChessWUPGame::IsInCheck
	( Piece *king )
{
	BitBoard128 bb128;
	int squareNumber = king->GetSquareNumber();
	int playerNumber = FLIP(king->GetPlayerNumber());

	for( int x = 0; x < board.GetDirectionCount(); x++ )
	{
		int attack_range = board.GetAttackRange( playerNumber, x );

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

			if( square >= 0 )
			{
				Piece *piece = board.GetSquareContents( square );
				square = board.GetMovementMatrix( oppositeDirection )[square];
				if( piece != NULL )
				{
					if( piece->GetPlayerNumber() == playerNumber )
					{
						if( piece->GetType().GetAttackRange( piece->GetPlayerNumber(), x ) >= steps )
							//	king is in check
							return true;
						if( &(piece->GetType()) == &DecimalWithdrawer::decimalWithdrawer && x < 8 )
							//	Withdrawer on threatening square; see if it has room to move away
							if( square != -1 && board.GetSquareContents( square ) == NULL )
								//	king is in check
								return true;
						if( (&(piece->GetType()) == &DecimalMimotaur::decimalMimotaur || 
							 &(piece->GetType()) == &DecimalSwapper::decimalSwapper) && x < 8 )
							//	king is in check
							return true;
					}
					square = -1;
				}
				steps++;
			}

			while( square >= 0 && steps <= attack_range )
			{
				Piece *piece = board.GetSquareContents( square );
				square = board.GetMovementMatrix( oppositeDirection )[square];
				if( piece != NULL )
				{
					square = -1;
					if( piece->GetPlayerNumber() == playerNumber &&
						(piece->GetType().GetAttackRange( piece->GetPlayerNumber(), x ) >= steps || 
						 (&piece->GetType()) == &DecimalAdvancer::decimalAdvancer) )
						return true;
				}
				steps++;
			}
		}
	}
	// *** COORDINATOR attacks *** //
	if( DecimalCoordinator::decimalCoordinator.GetPieces( playerNumber ) )
	{
		if( board.ranks[0][board.GetKing( 0 )->GetSquareNumber()] == 
			board.ranks[0][board.GetKing( 1 )->GetSquareNumber()] )
		{
			//	if the kings are on the same rank, then the king is in check if a coordinator 
			//	of the opposite color can move to the same file
			int targetFile = board.files[0][king->GetSquareNumber()];
			for( int x = 0; x < board.GetNumberOfPieces( playerNumber ); x++ )
			{
				if( &(board.GetPiece( playerNumber, x )->GetType()) == &DecimalCoordinator::decimalCoordinator && 
					!board.GetPiece( playerNumber, x )->IsCaptured() )
				{
					//	potential attacker - see if it can get there
					Piece *coordinator = board.GetPiece( playerNumber, x );
					int directions[3];
					int nDirections = 3;
					//	assume move to left, correct later if to right
					directions[0] = DIRECTION_NW;
					directions[1] = DIRECTION_W;
					directions[2] = DIRECTION_SW;
					if( targetFile > board.files[0][coordinator->GetSquareNumber()] )
					{
						//	need to move right to reach target file
						directions[0] = DIRECTION_NE;
						directions[1] = DIRECTION_E;
						directions[2] = DIRECTION_SE;
					}
					else if( targetFile == board.files[0][coordinator->GetSquareNumber()] )
					{
						directions[0] = DIRECTION_N;
						directions[1] = DIRECTION_S;
						nDirections = 2;
					}
					//	now step in each direction until we reach target file, 
					//	or until we hit an obstacle
					for( int y = 0; y < nDirections; y++ )
					{
						int nSquare = board.GetMovementMatrix( directions[y] )[coordinator->GetSquareNumber()];
						while( board.GetSquareContents( nSquare ) == NULL )
						{
							if( board.files[0][nSquare] == targetFile )
								//	yup, we are in check!
								return true;
							nSquare = board.GetMovementMatrix( directions[y] )[nSquare];
						}
					}
				}
			}
		}
		if( board.files[0][board.GetKing( 0 )->GetSquareNumber()] == 
			board.files[0][board.GetKing( 1 )->GetSquareNumber()] )
		{
			//	if the kings are on the same file, then the king is in check if a coordinator 
			//	of the opposite color can move to the same rank
			int targetRank = board.ranks[0][king->GetSquareNumber()];
			for( int x = 0; x < board.GetNumberOfPieces( playerNumber ); x++ )
			{
				if( &(board.GetPiece( playerNumber, x )->GetType()) == &DecimalCoordinator::decimalCoordinator && 
					 !board.GetPiece( playerNumber, x )->IsCaptured() )
				{
					//	potential attacker - see if it can get there
					Piece *coordinator = board.GetPiece( playerNumber, x );
					int directions[3];
					int nDirections = 3;
					//	assume move to north, correct later if to south
					directions[0] = DIRECTION_NE;
					directions[1] = DIRECTION_N;
					directions[2] = DIRECTION_NW;
					if( targetRank < board.ranks[0][coordinator->GetSquareNumber()] )
					{
						//	need to move right to reach target file
						directions[0] = DIRECTION_SE;
						directions[1] = DIRECTION_S;
						directions[2] = DIRECTION_SW;
					}
					else if( targetRank == board.ranks[0][coordinator->GetSquareNumber()] )
					{
						directions[0] = DIRECTION_E;
						directions[1] = DIRECTION_W;
						nDirections = 2;
					}
					//	now step in each direction until we reach target file, 
					//	or until we hit an obstacle
					for( int y = 0; y < nDirections; y++ )
					{
						int nSquare = board.GetMovementMatrix( directions[y] )[coordinator->GetSquareNumber()];
						while( board.GetSquareContents( nSquare ) == NULL )
						{
							if( board.ranks[0][nSquare] == targetRank )
								//	yup, we are in check!
								return true;
							nSquare = board.GetMovementMatrix( directions[y] )[nSquare];
						}
					}
				}
			}
		}
	}
	// *** LONG LEAPER attacks *** //
	if( DecimalLongLeaper::decimalLongLeaper.GetPieces( playerNumber ) )
	{
		for( int x = 0; x < board.GetNumberOfPieces( playerNumber ); x++ )
		{
			if( &(board.GetPiece( playerNumber, x )->GetType()) == &DecimalLongLeaper::decimalLongLeaper && 
				 !board.GetPiece( playerNumber, x )->IsCaptured() )
			{
				Piece *leaper = board.GetPiece( playerNumber, x );
				int direction = -1;
				if( board.directionLookup[leaper->GetSquareNumber()][king->GetSquareNumber()][0] >= 0 && 
					board.directionLookup[leaper->GetSquareNumber()][king->GetSquareNumber()][0] <= 7 )
					direction = board.directionLookup[leaper->GetSquareNumber()][king->GetSquareNumber()][0];
				if( board.directionLookup[leaper->GetSquareNumber()][king->GetSquareNumber()][1] >= 0 && 
					board.directionLookup[leaper->GetSquareNumber()][king->GetSquareNumber()][1] <= 7 )
					direction = board.directionLookup[leaper->GetSquareNumber()][king->GetSquareNumber()][1];
				if( direction >= 0 )
				{
					//	the king is indeed on an attack line of the long leaper's; 
					//	now we need to make sure that there's a landing square, and 
					//	that it doesn't jump any friendly or doubled pieces
					int nSquare = board.GetMovementMatrix( direction )[leaper->GetSquareNumber()];
					while( nSquare != -1 )
					{
						if( nSquare == king->GetSquareNumber() && 
							board.GetSquareContents( board.GetMovementMatrix( direction )[nSquare] ) == NULL )
							//	yup, the king is in check
							return true;
						if( board.GetSquareContents( nSquare ) != NULL && 
							(board.GetSquareContents( nSquare )->GetPlayerNumber() == leaper->GetPlayerNumber() ||
							 board.GetSquareContents( board.GetMovementMatrix( direction )[nSquare] ) != NULL) )
							//	we can't move any further in this direction
							nSquare = -1;
						if( nSquare != -1 )
							nSquare = board.GetMovementMatrix( direction )[nSquare];
					}
				}
			}
		}
	}
	return false;
}

void ChessWUPGame::AboutToGenerateMoves
	( int currentPlayer, 
	  int currentDepth )
{
	BitBoard128 bb128 = DecimalImmobilizer::decimalImmobilizer.GetPieces( FLIP(currentPlayer) );
	if( bb128 )
	{
		//	we need to setup the immobilizedSquares Matrix, 
		//	so that  move generation will not occur for 
		//	pieces on immobilized squares

		int (*immobilizedSquares)[MAX_DEPTH][MAX_SQUARES];
		board.GetImmobilizedMatrix( &immobilizedSquares );

		//	first, clear out the immobilizedSquares
		memset( (*immobilizedSquares)[currentDepth], 0, 100 * sizeof(int) );

		//	loop through all immobilizers and
		//	increment the value of adjacent squares
		while( bb128 )
		{
			int square = bb128.GetFirstBit();
			bb128.ToggleBit( square );
			Piece *piece = board.GetSquareContents( square );
			for( int dir = 0; dir < 8; dir++ )
			{
				int square = board.GetMovementMatrix( dir )[piece->GetSquareNumber()];
				if( square >= 0 )
					//	adjacent to enemy immobilizer
					(*immobilizedSquares)[currentDepth][square] += 1;
			}
		}
	}
}

void ChessWUPGame::GenerateMovesForMimotaur
	( Piece *piece,
	  MovementList &stack, 
	  GameRec &gameRecord, 
	  bool quiescentSearch )
{
	int x;
	int player = board.GetCurrentPlayerNumber();
	for( x = 0; x < 8; x++ )
	{
		int *movementMatrix = board.GetMovementMatrix( x );
		int nSquare = movementMatrix[piece->GetSquareNumber()];
		int steps = 1;

		while( nSquare != -1 )
		{
			if( board.GetSquareContents( nSquare ) == NULL )
			{
				if( !quiescentSearch )
					stack.AddMove( piece->GetSquareNumber(), nSquare );
				nSquare = movementMatrix[nSquare];
			}
			else
			{
				Piece *pieceOnSquare = board.GetSquareContents( nSquare );
				if( pieceOnSquare->GetPlayerNumber() != piece->GetPlayerNumber() && 
					pieceOnSquare->GetType().GetAttackRange( 0, x ) >= steps )
				{
					stack.AddCapture( piece->GetSquareNumber(), nSquare );
				}
				nSquare = -1;
			}
			steps++;
		}
	}
	for( ; x < 16; x++ )
	{
		int *movementMatrix = board.GetMovementMatrix( x );
		int nSquare = movementMatrix[piece->GetSquareNumber()];

		if( nSquare != -1 &&
			board.GetSquareContents( nSquare ) != NULL &&
			board.GetSquareContents( nSquare )->GetPlayerNumber() != piece->GetPlayerNumber() &&
			board.GetSquareContents( nSquare )->GetType().GetAttackRange( 0, x ) >= 1 )
		{
			stack.AddCapture( piece->GetSquareNumber(), nSquare );
		}
	}
}

void ChessWUPGame::DescribeMove
	( MoveInfo &move,
	  char *queryDescription,
	  char *description,
	  char *notation )
{
	if( move.type == Swap && queryDescription != NULL )
		sprintf( queryDescription, "Swap Pieces" );
	else if( move.type == MutualDestruction && queryDescription != NULL )
		sprintf( queryDescription, "Mutual Destruction" );
	else
		Game::DescribeMove( move, queryDescription, description, notation );
}

void ChessWUPGame::SetPersonality
	( int personality )
{
	//	call base class implementation of SetPersonality
	Game::SetPersonality( personality );

	if( personality >= PERSONALITY_C )
	{
		piecesHaveChangingValues = false;
		phases[0].SetPawnDeficiencyFactor( 16 );
		phases[1].SetPawnDeficiencyFactor( 20 );
		phases[2].SetPawnDeficiencyFactor( 24 );
	}
	else
	{
		piecesHaveChangingValues = true;
		phases[0].SetPawnDeficiencyFactor( 12 );
		phases[1].SetPawnDeficiencyFactor( 16 );
		phases[2].SetPawnDeficiencyFactor( 20 );
	}
}
