
/**
 *  class Evaluation
 *
 *  This is class conataining static evaluation methods
 *  We have this in an own class to easily switch evaluation if needed
 *  
 *  @author Jonatan Pettersson (mediocrechess@gmail.com)
 */
public class Evaluation implements Definitions
{

	public static int white_king_index;
	public static int black_king_index;
	public static int gamePhase;

	public static int evaluate(Board board)
	{
		white_king_index = 0; // Initialize the white king's poisition set to 0 so no crash if for some reason the king is not found, shouldn't happen though
		black_king_index = 0; // Initialize the black king's position
		gamePhase = PHASE_OPENING; // Initialize the game phase to opening, will be updated in the material search

		int finalEval = 0;

		finalEval += material(board); // Start with the evaluations

		
		// Only calculate development if not in ending
		if(gamePhase == PHASE_OPENING || gamePhase == PHASE_MIDDLE)
			finalEval += development(board);
		
		// Only calculate endings if we're at the ending
		if(gamePhase == PHASE_ENDING || gamePhase == PHASE_PAWN_ENDING)
			finalEval += ending(board);

		return finalEval*board.toMove; // Flip the eval if black to move
		
	}
	
	/**
	 *  This method returns the game phase, used if we need the game phase
	 *  without evaluating the board (like for contempt factor)
	 *  
	 *  This is integrated in the material evaluation for ordinary evaluation
	 * 
	 * @param board The board to decide game phase on
	 * @return gamePhase The phase the position on the board is in
	 */	
	public static int getGamePhase(Board board)
	{
		int phase = PHASE_OPENING; // Initialize to opening
		int gamePhaseCheck = 0; // Initialize the count
		
		for(int i = 0; i < 120; i++)
		{
			if((i & 0x88) == 0)
			{
				switch(board.boardArray[i])
				{
					case W_QUEEN: gamePhaseCheck += 4; break;
					case B_QUEEN: gamePhaseCheck += 4; break;
					case W_ROOK: gamePhaseCheck += 2; break;
					case B_ROOK: gamePhaseCheck += 2; break;
					case W_KNIGHT: gamePhaseCheck += 1; break;
					case B_KNIGHT: gamePhaseCheck += 1; break;
					case W_BISHOP: gamePhaseCheck += 1; break;
					case B_BISHOP: gamePhaseCheck += 1; break;
				}

			}
			else i += 7;
		}
		
		if(gamePhaseCheck == 0) phase = PHASE_PAWN_ENDING;
		else if(gamePhaseCheck <= 8) phase = PHASE_ENDING;
		else if(gamePhaseCheck >20) phase = PHASE_OPENING;
		else phase = PHASE_MIDDLE;
		
		return phase;		
	}
	// END getGamePhase()
	
	public static int material(Board board)
	{
		// Go through all the pieces on the board
		//
		// Add up the values, and see if there is enough material left to force a mate
		boolean whiteHasBishop = false; // Used by draw by material detection
		boolean whiteHasKnight = false; // we need to keep track of the number of
		boolean blackHasBishop = false; // knights and bishops on the board since 1
		boolean blackHasKnight = false; // is draw but 2 is not
		
		// Count pieces below to see if we are at the ending
		// queen 4 points, rook 2 and minor 1
		//
        // if total <=8 we are at the ending
		// E.g. one queen on each side, or one rook and two minor pieces on each side
		//
		// if total >20 we are at opening
		// E.g. two minor pieces exchanged, or all pieces still on board
		// 
		// if total >8 && <=20 we are at the middle game
		// E.g. one queen, two rooks and a minor piece on each side
		int gamePhaseCheck = 0;
							    
		                       

		int evaluation = 0; // Initialize the evaluation
		boolean drawn_by_material = true; // Initialize the draw check

		// Since looping through the board costs time, we try to get all the information
		// we can from this one loop. Even things that don't count material.
		// Like finding the kings and checking for draw by material
		for(int i = 0; i < 120; i++)
		{
			if((i & 0x88) == 0)
			{
				// Add the value of the piece to the evaluation
				evaluation += PIECE_VALUE_ARRAY[board.boardArray[i] + 7];

				// Special calculations for particular pieces
				switch(board.boardArray[i])
				{
					case W_KING:
					{
						white_king_index = i; break;
					}
					case B_KING:
					{
						black_king_index = i; break;
					}
					case W_QUEEN:
					{
						gamePhaseCheck += 4;
						break;
					}
					case B_QUEEN:
					{
						gamePhaseCheck += 4;
						break;
						
					}
					case W_ROOK:
					{
						gamePhaseCheck += 2;
						break;
						
					}
					case B_ROOK:
					{
						gamePhaseCheck += 2;
						break;
						
					}
					case W_KNIGHT:
					{
						gamePhaseCheck += 1;
						evaluation += W_KNIGHT_POS[i]; break;
					}
					case B_KNIGHT:
					{
						gamePhaseCheck += 1;
						evaluation -= B_KNIGHT_POS[i]; break;
					}
					case W_BISHOP:
					{
						gamePhaseCheck += 1;
						evaluation += W_BISHOP_POS[i]; break;
					}
					case B_BISHOP:
					{
						gamePhaseCheck += 1;
						evaluation -= B_BISHOP_POS[i]; break;
					}
					case W_PAWN:
					{
						evaluation += W_PAWN_POS[i]; break;
					}
					case B_PAWN:
					{
						evaluation -= B_PAWN_POS[i]; break;
					}
				}
				// Do the checks for drawn by material
				if(drawn_by_material && board.boardArray[i] != EMPTY_SQUARE)
				{
					// If either side has queen, rook or pawn it is not drawn
					if(board.boardArray[i] == W_QUEEN || board.boardArray[i] == W_ROOK  ||
							board.boardArray[i] == W_PAWN  || board.boardArray[i] == B_QUEEN ||
							board.boardArray[i] == B_ROOK  || board.boardArray[i] == B_PAWN    )
						drawn_by_material = false;

					else if((whiteHasBishop || whiteHasKnight) && board.boardArray[i] == W_BISHOP)
						drawn_by_material = false;

					else if(whiteHasBishop && board.boardArray[i] == W_KNIGHT)
						drawn_by_material = false;

					else if(board.boardArray[i] == W_BISHOP)
						whiteHasBishop = true;

					else if(board.boardArray[i] == W_KNIGHT)
						whiteHasKnight = true;

					else if((blackHasBishop || blackHasKnight) && board.boardArray[i] == B_BISHOP)
						drawn_by_material = false;

					else if(blackHasBishop && board.boardArray[i] == B_KNIGHT)
						drawn_by_material = false;

					else if(board.boardArray[i] == B_BISHOP)
						blackHasBishop = true;

					else if(board.boardArray[i] == B_KNIGHT) {
						blackHasKnight = true;
					}

				}

			}
			else i += 7;
		}

		if(gamePhaseCheck == 0) gamePhase = PHASE_PAWN_ENDING;
		else if(gamePhaseCheck <= 8) gamePhase = PHASE_ENDING;
		else if(gamePhaseCheck >20) gamePhase = PHASE_OPENING;
		else gamePhase = PHASE_MIDDLE;

		return ( drawn_by_material ) ? DRAW_VALUE : evaluation; 
	}
	// END evaluatePosition()
	
	// Used if the game is in the ending
	public static int ending(Board board)
	{
		int ending = 0;
		
		ending += KING_POSITION_ENDING[white_king_index];
		ending -= KING_POSITION_ENDING[black_king_index];
		
		
		return ending;		
	}
	// END ending()
	
	// Development (will not be used in endgames)
	public static int development(Board board)
	{
		int development = 0;
		
		// King placement, better in the corners
		development += WHITE_KING_POSITION[white_king_index];
		development -= BLACK_KING_POSITION[black_king_index];
		
		// Pawn shield 
		if(((white_king_index+15) & 0x88) == 0 && board.boardArray[white_king_index+15] == W_PAWN)
			development += 5;
		if(((white_king_index+16) & 0x88) == 0 && board.boardArray[white_king_index+16] == W_PAWN)
			development += 5;
		if(((white_king_index+17) & 0x88) == 0 && board.boardArray[white_king_index+17] == W_PAWN)
			development += 5;
		
		if(((black_king_index-15) & 0x88) == 0 && board.boardArray[black_king_index-15] == B_PAWN)
			development -= 5;
		if(((black_king_index-16) & 0x88) == 0 && board.boardArray[black_king_index-16] == B_PAWN)
			development -= 5;
		if(((black_king_index-17) & 0x88) == 0 && board.boardArray[black_king_index-17] == B_PAWN)
			development -= 5;
		
		// Penalize center pawns on the original squares		
		if(board.boardArray[19] == W_PAWN) development -= 2;
		if(board.boardArray[20] == W_PAWN) development -= 2;
		if(board.boardArray[99] == B_PAWN) development += 2;
		if(board.boardArray[100] == B_PAWN) development += 2;
		
		// Penalize knights, bishops and queens on first rank
		for(int i = 0; i < 8; i++)
		{
			if(board.boardArray[i] == W_KNIGHT) development -= 10;
			if(board.boardArray[i] == W_BISHOP) development -= 10;
			if(board.boardArray[i] == W_QUEEN) development -= 10;
		}
		for(int i = 112; i < 120; i++)
		{
			if(board.boardArray[i] == B_KNIGHT) development += 10;
			if(board.boardArray[i] == B_BISHOP) development += 10;
			if(board.boardArray[i] == B_QUEEN) development += 10;
		}
		
		
		return development;
	}
	// END development()
}
