// Copyright 1994, 1995 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "scoring.h"
#include "bearing.h"
#include "constant.h"
#include "util.h"

static int RookCenterTable[] =
    {0, 0, 0, 27, 28, 0, 0, 0,
     0, 0, 0, 27, 28, 0, 0, 0,
     0, 0, 0, 27, 28, 0, 0, 0,
     27, 27, 27, 27, 28, 28, 28, 28,
     35, 35, 35, 35, 36, 36, 36, 36,
     0, 0, 0, 35, 36, 0, 0, 0,
     0, 0, 0, 35, 36, 0, 0, 0,
     0, 0, 0, 35, 36, 0, 0, 0};

static int RookCenterDirs[] =
    {0, 0, 0, 8, 8, 0, 0, 0,
     0, 0, 0, 8, 8, 0, 0, 0,
     0, 0, 0, 8, 8, 0, 0, 0,
     1, 1, 1, 0, 0, -1, -1, -1,
     1, 1, 1, 0, 0, -1, -1, -1,
     0, 0, 0, -8, -8, 0, 0, 0,
     0, 0, 0, -8, -8, 0, 0, 0,
     0, 0, 0, -8, -8, 0, 0, 0};

static int KnightCenterScores[] =
    {0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 2, 2, 2, 2, 0, 0,
     0, 2, 3, 2, 2, 3, 2, 0,
     0, 2, 2, 5, 5, 2, 2, 0,
     0, 2, 2, 5, 5, 2, 2, 0,
     0, 2, 3, 2, 2, 3, 2, 0,
     0, 0, 2, 2, 2, 2, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0};

static int PawnCenterScores[] =
    {0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 5, 5, 0, 0, 0,
     0, 0, 4, 6, 6, 4, 0, 0,
     0, 0, 3, 4, 4, 3, 0, 0,
     0, 0, -1, -2, -2, -1, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0};

static int KingCenterScores[] =
    {0, 0, 1, 1, 1, 1, 0, 0,
     0, 1, 2, 3, 3, 2, 1, 0,
     1, 2, 6, 7, 7, 6, 2, 1,
     1, 3, 7, 9, 9, 7, 3, 1,
     1, 3, 7, 9, 9, 7, 3, 1,
     1, 2, 6, 7, 7, 6, 2, 1,
     0, 1, 2, 3, 3, 2, 1, 0,
     0, 0, 1, 1, 1, 1, 0, 0};

const int KBNKScores[] =
    {-12, -8, -2, 3, 7, 10, 15, 20,
     -8, -6, 1, 0, 5, 9, 12, 15,
     -2, 1, 0, 0, 5, 8, 9, 10,
     3, 2, 0, 0, 0, 5, 5, 7,
     7, 5, 5, 0, 0, 0, 0, 3,
     10, 9, 8, 5, 0, 0, 1, -2,
     15, 12, 9, 5, 2, 1, -6, -8,
     20, 15, 10, 7, 3, -2, -8, -12};

int Scoring::en_prise = 0;
int Scoring::trapped = 0;

static BOOL endgame;

static int distance( const Square sq1, const Square sq2 )
{
    int file_dist = Util::Abs(sq1.File() - sq2.File());
    int rank_dist = Util::Abs(sq1.Rank(White) - sq2.Rank(White));
    return file_dist+rank_dist;
}

static BOOL uncatchable(const Board &board,
                        const Square pawnpos,
                        const Square oppkp,
                        const ColorType side)
{
   // Use the "square of the king" rule to see whether a passed pawn can
   // be captured before it promotes (or immediately after).

   int rankdist = pawnpos.Rank(side) - oppkp.Rank(side);

   if (board.Side() == side)
   {
       if (rankdist > 0)
       {
           // If the king is behind us and we are on move we can't be caught.
           return TRUE;
       }
       else
       {
         return
            8 - pawnpos.Rank(side) <
            Util::Abs(pawnpos.File() - oppkp.File()) - rankdist;
       }
   }
   else
   {
       if (rankdist > 1)
       {
           // pawn > 1 square ahead
           return TRUE;
       }
       else
       {
           return
            9 - pawnpos.Rank(side) <
            Util::Abs(pawnpos.File() - oppkp.File()) - rankdist;
       }
   }
}   

// Weights for positional scoring components.  A plus value means that
// the item being scored is valuable to the player; a minus value means
// that a penalty is extracted.  In most cases, scoring components are
// simply summed together to get a total score, but some are folded
// in, in a more complex way.

// material terms
const int TWO_BISHOPS = 8;
// development terms
const int CENTER1 = 1; // center control by sliding piece
const int NO_CENTER_ATTACK = -1; // center square unoccupied and not attacked
const int BLOCKED_BISHOP = -4;
const int LOW_BISHOP_MOBILITY = -2;
const int BISHOP_BACK = -7;
const int BAD_BISHOP = -5;
const int CENTER_PAWN_BLOCK = -6;  // e or d pawn blocked by piece
const int LONG_DIAG = 3; // bishop on long diagonal
const int KNIGHT_BACK = -7;
const int KNIGHT_ON_RIM = -2;
const int KNIGHT_IN_HOLE = 6;
const int KNIGHT_MOBILITY = 1;
const int ROOK_ON_7TH_RANK = 5;
const int QUEEN_DEVELOP = 2;
const int QUEEN_OUT = -6; // premature development of queen
const int DOUBLED_ROOKS = 6; // on open file
const int ROOK_ON_HALF_OPEN_FILE = 2;
const int ROOK_ON_OPEN_FILE = 4;
// castling terms
const int KCASTLE = 9;
const int QCASTLE = 6;
const int CAN_CASTLE = -2;
const int CANT_CASTLE = -12;
const int CANT_CASTLE_K_SIDE = -6;
const int CANT_CASTLE_Q_SIDE = -4;
const int ATTACK_PREVENTS_CASTLING = -3;
// pawn structure terms
const int PAWN_ON_7TH_RANK = 4;
const int BACKWARD_PAWN = -11;  // on open file
const int BACKWARD_PAWN_BLOCK = -4; // square in front of backward pawn
                                    // attacked by enemy pawn
const int CENTRAL_ISOLANI = -8;
const int ISOLANI = -6;
const int ISOLATED_PAWN_BLOCK = -4; // pawn blocked or advance square 
				    // attacked
const int DOUBLED_PAWNS = -22;
const int TRIPLED_PAWNS = -30;
const int PASSED_PAWN[8] = { 0, 12, 13, 17, 25, 41, 73, 0}; // score by rank
const int CONNECTED_PASSERS = 32;
const int RIM_PASSED_PAWN = -3; // pawn on h-file or a-file
const int PAWN_DUO = 5; // a la Kmoch
const int ROOK_BEHIND_PP = 3; // rook or queen behind passed pawn
const int UNSUPPORTED_PAWN = -1; // an advanced but unsupported pawn

const int OPP_KING_DISTANCE_FROM_PP = 2; // opposing king distance from passed
					 // pawn
const int OPP_KING_AHEAD_OF_PP = -7;
// king safety terms
const int CHECK = -5;  // being in check
const int ATTACK_NEAR_KING = -2; // square near king attacked
const int KING_OFF_BACK_RANK = -7;
const int KING_PAWN_COVER_MISSING = -6;
const int KING_FILE_OPEN = -4;
const int K_SIDE_OPEN = -2;
const int KING_NEAR_OPEN_FILE = -1;
const int DANGER_ON_FILE = -2; // rook or queen on file near king
// king position terms - valid only in endgame
const int ADVANCED_KING = 4;
const int KING_NEAR_PAWN = 1;
const int OPPOSITION = 12;
const int KING_DISTANCE = 4; // bringing our king closer to bare king
const int KING_CAN_MOVE = -3; // for each square opposing bare king can move
const int KING_ON_EDGE = 4;   // opposing king on edge
const int KING_IN_CORNER = 4; // opposing king in corner
// piece safety terms
const int EN_PRISE = -6; // piece apparently hung
const int PIECE_TRAPPED = -6; // piece apparently trapped
const int TWO_EN_PRISE = -10; // >1 piece apparently hung
const int BISHOP_TRAPPED = -280; // bishop trapped by pawn
// general endgame terms
const int KING_PAWN_DISTANCE = -10; // distance from opposing king
const int OPP_KING_AHEAD_OF_PAWN = 8;
// KP vs. K endgame
const int UNCATCHABLE_PAWN = 450;
const int KING_AHEAD_OF_PAWN = 32;
const int KING_SAME_FILE = 32;
const int DISTANCE_FROM_PAWN = -8;

static Bitmap passedW[64];
static Bitmap passedB[64];
static Bitmap backwardW[64];
static Bitmap backwardB[64];
static int bitmaps_init = 0;

void Scoring::init()
{
}

static void init_bitmaps()
{
   for (int i = 0; i < 64; i++)
   {
       Square sq(i);
       int rank = sq.Rank(White);
       int file = sq.File();
       for (int r = rank; r > 1; r--)
       {
           Square sq2(file,r,White);
	   //if (r != rank)
	   //    backwardW[i].set((int)sq2);
	   if (file != 8)
	   {
	       sq2 = Square(file+1,r,White);
	       backwardW[i].set((int)sq2);
	   }
	   if (file != 1)
	   {
	       sq2 = Square(file-1,r,White);
	       backwardW[i].set((int)sq2);
	   }
       }
       for (r = rank + 1; r < 8; r++)
       {
           Square sq2(file,r,White);
	   passedW[i].set((int)sq2);
	   if (file != 8)
	   {
	      sq2 = Square(file+1,r,White);
	      passedW[i].set((int)sq2);
	   }
	   if (file != 1)
	   {
	      sq2 = Square(file-1,r,White);
	      passedW[i].set((int)sq2);
	   }
       }
       rank = sq.Rank(Black);
       for (r = rank; r > 1; r--)
       {
           Square sq2(file,r,Black);
	   //if (r != rank)
	   //    backwardB[i].set((int)sq2);
	   if (file != 8)
	   {
	      sq2 = Square(file+1,r,Black);
	      backwardB[i].set((int)sq2);
	   }
	   if (file != 1)
	   {
	      sq2 = Square(file-1,r,Black);
	      backwardB[i].set((int)sq2);
	   }
       }
       for (r = rank + 1; r < 8; r++)
       {
           Square sq2(file,r,Black);
	   passedB[i].set((int)sq2);
	   if (file != 8)
	   {
	      sq2 = Square(file+1,r,Black);
	      passedB[i].set((int)sq2);
	   }
	   if (file != 1)
	   {
	      sq2 = Square(file-1,r,Black);
	      passedB[i].set((int)sq2);
	   }
       }
   }
}

static int MaterialScore( const Board &board, const ColorType side )
{
  // Computes material score, from the perspective of 'side'.
  // Algorithm is based on that used by Chess 4.5.
  long ourmat, oppmat, totmat;
  int mdiff;
  
  ourmat = board.getMaterial(side).value();
  oppmat = board.getMaterial(OppositeColor(side)).value();
  mdiff =  (int)(ourmat - oppmat);
  if (Util::Abs(mdiff) > 2*Constants::PawnValue)
  {
     totmat = ourmat + oppmat;
     return mdiff + (int)(Util::Sign(mdiff)*( (10240L - totmat) / 
     	Constants::PawnValue ));
  }
  else
     return mdiff;
}

BOOL Scoring::in_endgame( const Board &board, const ColorType side )
{
    int my_pcount = board.getMaterial(side).count(Piece::Pawn);
    int opp_pcount = board.getMaterial(OppositeColor(side)).count(Piece::Pawn);
    return BOOL((board.getMaterial(side).value() - my_pcount*64
    	<= Piece::Value(Piece::King) + Piece::Value(Piece::Queen)) &&
	   (board.getMaterial(OppositeColor(side)).value() - opp_pcount*64 <= 
	    Piece::Value(Piece::King) + Piece::Value(Piece::Queen)));
}

static BOOL mopping_up( const Board &board, const ColorType side )
{
    // Returns "true" if "side" has great material advantage (probably
    // enough to mate by force)
    
    const Material &my_mat = board.getMaterial(side);
    const Material &opp_mat = board.getMaterial(OppositeColor(side));

    if (opp_mat.king_only() && !endgame)
        return TRUE;
    else if (my_mat.value() - opp_mat.value() < 570)
        return FALSE;
    else
        return (my_mat.value() + opp_mat.value() < 5000);
}

int Scoring::positional_score( const Board &board, const ColorType side)
{
   int score = 0;
   int i, j;
   const ColorType oside = OppositeColor(side);
   const Material &my_mat = board.getMaterial(side);
   if (my_mat.count(Piece::Bishop) >= 2)
      score += TWO_BISHOPS;
   if ((side == board.Side()) && board.CheckStatus() == Board::InCheck)
       score += CHECK;

   Square kp(board.KingPos(side));
   Square oppkp(board.KingPos(OppositeColor(side)));

   Square passers[8];
   int passers_count = 0;

   int devel_score = 0;
   int mobl = 0;
   BOOL have_queen = FALSE;
   BOOL queen_back = FALSE;
   BOOL haveWbishop = FALSE;
   BOOL haveBbishop = FALSE;
   int blackPawns = 0, whitePawns = 0;
   const Piece enemyPawn(Piece::Pawn,oside);
   const Piece our_pawn( Piece::Pawn, side );
   const Piece our_bishop( Piece::Bishop, side );

   const int incr = 8*Direction[side];

   /* pawn structure and piece development.  Do this first since we
      use the info gathered here for endgame scoring later */
   for (i = 0; i < 16; i++)
   {
      Square sq(board.PiecePos(side,i));
      if (!sq.IsInvalid())
      {
          Piece piece = board[sq];
	  const int rank = sq.Rank(side);
	  const int file = sq.File();
          switch (piece.Type())
	  {
	  case Piece::Pawn:
             {
             if (!endgame)
             {
  	        if (side == White)
	           score += PawnCenterScores[sq];
                else
	           score += PawnCenterScores[63-(int)sq];
             }
	     if (sq.Color() == White)
	        whitePawns++;
             else
                blackPawns++;
	     BOOL backward = FALSE;
	     BOOL passed = FALSE;
	     BOOL isolated;
	     if (rank == 7)
	        score += PAWN_ON_7TH_RANK;
	     BOOL file_open = (board.PFileCount(file-1,oside)==0);
	     if (file_open)
	     {
	         Bitmap result;
  	         if (side == White)
		     board.PawnBits(side).and(backwardW[sq],result);
                 else
		     board.PawnBits(side).and(backwardB[sq],result);
 	         backward = result.is_clear();
		 result.clear();
		 if (side == White)
		    board.PawnBits(oside).and(passedW[sq],result);
		 else
		   board.PawnBits(oside).and(passedB[sq],result);
		 passed = result.is_clear();
	     }
	     isolated = (board.PFileCount(file-1,side)==0 &&
	                 board.PFileCount(file+1,side)==0);
	     int pr;
	     if ((pr = board.PFileCount(file-1,side)) > 1)
	     {
	        if (pr == 2)
		   score += DOUBLED_PAWNS/2;
		else
		   score += TRIPLED_PAWNS/3;
	     }
	     if (backward && !isolated)
	     {
                score += BACKWARD_PAWN;
		Square sq2(sq + incr);
		score += BACKWARD_PAWN_BLOCK*board.pawn_attacks(sq2,oside);
		if (rank == 2)
		{
		    sq2 += incr;
		    score += BACKWARD_PAWN_BLOCK*board.pawn_attacks(sq2,oside);
		}
		Piece p(board[sq2]);
                Piece::PieceType typ = p.Type();
                if (typ != Piece::Empty && typ != Piece::Pawn && typ != Piece::King)
                {
		   if (p.Color() == oside &&
		       board.pawn_attacks(sq2,oside) > 0)
		   {
                       // piece defended by an enemy pawn is in a pawn "hole",
                       // where we can't attack it
		       score += KNIGHT_IN_HOLE;
		   }
                }
	     }
	     else if (isolated)
	     {
	         if (file>=3 && file<=6)
	            score += CENTRAL_ISOLANI;
	         else
	            score += ISOLANI;
		 Square sq2(sq + incr);
                 Piece p(board[sq2]);
		 if ((!p.IsEmpty() && p.Color() == oside) ||
		     board.pawn_attacks(sq2,oside) > 0)
		     score += ISOLATED_PAWN_BLOCK;
	     }
	     if (!isolated && rank > 2)
	     {
	         Square sq2 = sq + ((Direction[side] >0) ? 
		 		-RankIncr : +RankIncr);
		 if (!(file != 1 && board[sq2 - 1] == our_pawn) &&
		     !(file != 8 && board[sq2 + 1] == our_pawn))
		    score += UNSUPPORTED_PAWN;
	     }
	     if (passed)
	     {
                 passers[passers_count++] = sq;
	         score += PASSED_PAWN[rank-1];
                 int j;
                 for (j=0;j<passers_count-1;j++)
                 {
                    Square psq = passers[j];
                    int psqfile = psq.File();
                    if (psqfile == file-1 || psqfile == file+1)
                    {
                       int psqrank = psq.Rank(side);
                       if (Util::Abs(psqrank-rank)<2)
                          score += CONNECTED_PASSERS;
                    }
                 }
	         if (file ==1 || file == 8)
	            score += RIM_PASSED_PAWN;
	         for (j=rank+1;j<9;j++)
	         {
	       	     Square sq2(file,j,side);
	             Piece piece2 = board[sq2];
	             if (!piece2.IsEmpty())
                     {
                     if (piece2.Color() == side)
                     {
                         if (piece2.Type() == Piece::Pawn)
                         {
                             // This pawn is passed but another
                             // pawn is ahead of it, i.e. it is
                             // doubled
                             score -= PASSED_PAWN[rank-1]/3;
                         }
                     }
                     else 
		     {
		         // blocked passed pawn
		         int hit = PASSED_PAWN[rank-1]/3;
		         // double score if we block the passed pawn
		         // by occupying the square in front of it:
		         if (j==rank+1)
		            score -= 2*hit;
		         else
		            score -= hit;
		         break;
		     }
                     }
	         }
	         for (j=rank-1;j>0;j--)
	         {
	       	    Square sq3(file,j,side);
		    Piece piece3 = board[sq3];
		    if (!piece3.IsEmpty())
		    {
		       if (piece3.Color() == side)
		       {
		          if (piece3.Type() == Piece::Rook || piece3.Type() 
		  	   == Piece::Queen)
		          {
			      score += ROOK_BEHIND_PP;
			      break;
		          }
	               }
                       else
		           break;
		    }
	         }
                 if (endgame)
                 {
                     // Subtract points if the opposing king is near
                     // our passed pawn
                        score += OPP_KING_DISTANCE_FROM_PP*
		 	       distance(oppkp,sq);
                     // Subtract more if it's ahead of the passed pawn
  	             if (oppkp.Rank(side) > sq.Rank(side))
 		        score += OPP_KING_AHEAD_OF_PP;
                 }
	     }
	     if (rank >3 && file != 8 && board[sq+1] == piece)
	         score += PAWN_DUO;
	     }
	     break;
          case Piece::Knight:
	     if (endgame)
	         break;
	     score += KnightCenterScores[sq];
             if (rank == 1 && !endgame)
	         devel_score += KNIGHT_BACK;
	     if (file == 1 || file == 8)
		 score += KNIGHT_ON_RIM;
	     if (!endgame)
	     {
		 const byte *data = KnightSquares[(int)sq];
		 j = 0;
		 while (j < 8 && data[j] != 255)
		 {
		     Square dest(data[j]);
		     if (board[dest].IsEmpty() && 
			 board.pawn_attacks(dest,oside) ==0)
			score += KNIGHT_MOBILITY;
		     ++j;
		 }
	     }
	     break;
          case Piece::Bishop:
             if (sq.Color() == White) 
                 haveWbishop = TRUE;
             else 
                 haveBbishop = TRUE;
             if (!endgame)
	     {
		  for (j = 0; j < 4; j++)
		  {
		      const byte *data = BishopSquares[sq] + (j*8);
		      for (;;)
		      {
		          byte b = *data;
			  if (b == 255)
			      break;
			  data++;
			  Piece piece = board[b];
			  if (piece.IsEmpty())
			     mobl++;
			  else 
			     break;
		      }
		  }
		  if (mobl==0)
		     score += BLOCKED_BISHOP;
		  else if (mobl == 1)
		     score += LOW_BISHOP_MOBILITY;
                  if (rank == file || rank == 9-file)
		     score += LONG_DIAG;
		  if (rank == 1 && !endgame)
		     devel_score += BISHOP_BACK;
             }
             // check for bishop trapped at a7 or h7 with
             // a pawn
             if (rank == 7) 
             {
                  if (file==1)
                  {
                     Square psq(2,6,side);
                     if (board[psq] == Piece(Piece::Pawn,
                         oside) &&
                         board.num_attacks(psq,oside)>0)
                     {
                        score += BISHOP_TRAPPED;
                     }
                  }
                  else if (file==8)
                  {
                     Square psq(7,6,side);
                     if (board[psq] == Piece(Piece::Pawn,
                         oside) &&
                         board.num_attacks(psq,oside)>0)
                     {
                        score += BISHOP_TRAPPED;
                     }

                  }
             }
	     break;
          case Piece::Queen:
	     have_queen = TRUE;
	     queen_back = rank == 1;
	     break;
	  case Piece::Rook:
	     if (rank == 7)
		score += ROOK_ON_7TH_RANK;
             if (board.PFileCount(file-1,side)==0)
                score += ROOK_ON_HALF_OPEN_FILE;
             if (board.PFileCount(file-1,oside)==0)
             {
                 int bonus = ROOK_ON_OPEN_FILE;
		 const int rr = board.RFileCount(file-1,side);
                 if (rr>=2)
                    bonus += DOUBLED_ROOKS/2;
                 // Connected rooks and rooks on open files
                 // are good in the endgame, but less good
                 // than in the opening/middlegame.
                 if (endgame) bonus /= 2;
                 score += bonus;
             }
	     break;
	  default:
	     break;
	  }
      }
   }
   score += devel_score;
   if (!endgame && have_queen && !queen_back)
   {
      if (devel_score == 0 && 
	 (board.CastleStatus(side) == Board::CastledKSide ||
	  board.CastleStatus(side) == Board::CastledQSide))
	   score += QUEEN_DEVELOP;
      else // premature development of queen
          score += QUEEN_OUT;
   }
   if (haveWbishop && !haveBbishop)
   {
      if ((whitePawns + blackPawns > 4) && (whitePawns > blackPawns))
         score += BAD_BISHOP;
   }
   if (haveBbishop && !haveWbishop)
   {
      if ((whitePawns + blackPawns > 4) && (blackPawns > whitePawns))
          score += BAD_BISHOP;
   }

   if (endgame)
   {
      BOOL have_opposition = FALSE;
      if (board.Side() != side)
      {
         int file_distance, rank_distance;
         file_distance=Util::Abs(kp.File()-oppkp.File());
         rank_distance=Util::Abs(kp.Rank(side)-oppkp.Rank(side));
         if (rank_distance == 0 && file_distance == 2) 
         {
             have_opposition = TRUE; // direct opposition
             score += OPPOSITION;
         }
         else if (file_distance == 0 && rank_distance % 2 == 0)
             score += OPPOSITION/2;
         else if ((file_distance == 2) && (rank_distance == 2)) 
             score += OPPOSITION/2;
      }
      if (mopping_up(board,OppositeColor(side)))
      {
           // Our opponent has much more material, so try to avoid
           // being mated.
           score += KingCenterScores[board.KingPos(side)];
           const byte *data = KingSquares[(int)kp];
           for (j = 0; j <8 && *data != 255 ;j++)
           {
              if (board.num_attacks(*data++,OppositeColor(side)) >0)
                  score += ATTACK_NEAR_KING;
           }

	   // Keep our king away from opposing king (from the attacking side's
           // point of view, try to bring the kings close).
	   Square oppkp(board.KingPos(oside));
	   const int opprank = oppkp.Rank(White);
	   const int oppfile = oppkp.File();
	   const int ourrank = kp.Rank(White);
	   const int ourfile = kp.File();
	   int dist = Util::Abs(oppfile - ourfile) +
	   	      Util::Abs(ourrank - opprank);
	   score += (dist-5)*KING_DISTANCE;
	   if (board.getMaterial(side).king_only() &&
               (unsigned)board.getMaterial(oside).infobits() == 0x8048)
	   {
	       // KBNK endgame, special case.
	       ColorType bishopColor;
	       for (int i = 0; i < 16; i++)
	       {
	           Square sq(board.PiecePos(board.Side(),i));
	           if ( sq != Square::Invalid())
		      if (board[sq].Type() == Piece::Bishop)
		      {
		         bishopColor = sq.Color();
			 break;
		      }
	       }
	       // keep out of the corners where mate is possible.
	       Square evalsq;
	       if (bishopColor == White)
	       {
	           evalsq = Square(9-opprank, oppfile, White);
	       }
	       else
	       {
	           evalsq = oppkp;
	       }
               score -= KBNKScores[evalsq];
	       return score;
	   }
	   else
	   {
	       // Encourage putting the king on the edge of the board:
	       if (oppkp.OnEdge())
	          score += KING_ON_EDGE;
	       if (oppfile == 1 || oppfile == 8)
	       {
	          if (opprank == 1 || opprank == 8)
	             score += KING_IN_CORNER;
	       }
	       // Encourage restricting opposing king's mobility:
	       const byte *data = KingSquares[(int)oppkp];
	       score -= 8*KING_CAN_MOVE;
	       for (i = 0; i <8 && *data != 255 ;i++)
               {
                  Square sq(*data++);
	          if (board.num_attacks(sq,side) == 0)
	              score += KING_CAN_MOVE;
	       }
           }
       }
       else if ((unsigned)board.getMaterial(side).infobits() == 0x8001 &&
       		(unsigned)board.getMaterial(oside).king_only())
       {
           // KPK endgame
	   Square pawnpos = passers[0];
	   Square oppkp(board.KingPos(oside));
           if (uncatchable(board,pawnpos,oppkp,side))
           {
               // The opposing king can't catch the pawn before
               // it queens
	       score += UNCATCHABLE_PAWN;
           }
	   else
	   {
	       int rank = pawnpos.Rank(side);
               int file = pawnpos.File();
	       int file_dist = Util::Abs(pawnpos.File() - kp.File());
               int rank_dist = kp.Rank(side)-rank;
	       if (rank_dist > 0 && file_dist <= 1)
	       {
		   score += KING_AHEAD_OF_PAWN;
	           // See if we have a winning position
                   if (file == 1 || file == 8)
                   {
                      // rook pawn
                      Square qsq(file,8,side); // queening square                      
                      if (file_dist == 1 && distance(kp,qsq) <
                         distance(oppkp,qsq))
                         score + UNCATCHABLE_PAWN;
                   }
                   else if (file_dist == 0)
                   {
                      if (rank_dist == 2 ||
                          (rank_dist == 1 && (kp.Rank(side)==6
                          || have_opposition)))
                      {
                          score += UNCATCHABLE_PAWN;
                      }
                   }
	       }
	       else // king behind pawn
	           score -= PASSED_PAWN[rank-1]/2;
	       // also encourage staying near pawn
	       score += 10 + DISTANCE_FROM_PAWN*distance(kp,pawnpos);
	   }
       }
/**
       else if (board.getMaterial(side).no_pieces() &&
                board.getMaterial(oside).no_pieces() && passers_count)
       {
           // King and pawn(s) against bare king or king and pawn(s).
           // At least one pawn is passed.
	   for (int i = 0; i < passers_count; i++)
	   {
	       Square pawnpos(passers[i]);
               if (uncatchable(board,pawnpos,board.KingPos(OppositeColor(side)),side))
               {
	            score += UNCATCHABLE_PAWN;
               }
               else
               {
	           // we can't just push the pawn.
  	           int rank = pawnpos.Rank(side);
	           int file_dist = Util::Abs(pawnpos.File() - kp.File());
	           if (kp.Rank(side) > rank && file_dist <= 1)
	           {
		       score += KING_AHEAD_OF_PAWN;
		       if (rank < 6 && file_dist == 0)
		          score += KING_SAME_FILE;
  	           }
	           else
	              score -= PASSED_PAWN[rank-1]/2;
 	           // also encourage staying near pawn
	           score += 10 + DISTANCE_FROM_PAWN*distance(kp,pawnpos);
               }
	   }
       }
**/
       else
       {
       // This code is VERY crude .. it helps (a little) in king and
       // pawn endgames, but is pretty useless otherwise ... Lots more
       // work to do here.
           // Centralize the king
           score += KingCenterScores[kp];
           if (kp.Rank(side) > 4)
              score += ADVANCED_KING;
           // bonus for king near pawns
           const byte *data = KingSquares[(int)kp];
	   const BOOL king_only 
	   	= (unsigned)board.getMaterial(side).king_only();
           for (i = 0; i <8 && *data != 255 ;i++)
           {
               Square sq(*data++);
	       if (board[sq].Type() == Piece::Pawn)
	       {
	          score += KING_NEAR_PAWN;
	          if (king_only)
	          {
	             int file_dist = Util::Abs(kp.File() - sq.File());
	             int rank_dist = Util::Abs(kp.Rank(side) - sq.Rank(side));
	             score += 20 + KING_PAWN_DISTANCE*(file_dist + rank_dist);
		     if (kp.Rank(oside) > 
		     	 sq.Rank(oside))
		       score += OPP_KING_AHEAD_OF_PAWN;
	          }
	       }
           }
       }
   }
   if (!bitmaps_init)
   {
      init_bitmaps();
      bitmaps_init++;
   }

   /*** center control ***/
   if (!endgame)
   {
   static int centers[] = { 27, 28, 35, 36 };
   for (i=0;i<4;i++)
   {
       Square sq(centers[i]);
       const Attack_Entry &atcks = board.get_attacks(sq,side);
       if (!(!board[sq].IsEmpty() && board[sq].Color() == side) && atcks.num_attacks() == 0)
           score += NO_CENTER_ATTACK;
       int n = atcks.num_attacks(Piece::Bishop);
       int count = 0;
       if (n)
       {
           score += CENTER1;
       }
       n = atcks.num_attacks(Piece::Rook);
       if (n)
       {
           score += CENTER1;
       }
       n = atcks.num_attacks(Piece::Queen);
       if (n)
       {
           score += CENTER1;
       }
     
       Piece center_piece(board[sq]);
       if (!center_piece.IsEmpty() && center_piece.Color() == side &&
           center_piece.Type() != Piece::Pawn)
       {
           if (board[sq-incr].Type() == Piece::Pawn ||
	       board[sq-2*incr].Type() == Piece::Pawn)
	       score += CENTER_PAWN_BLOCK;
       }
   }
   }
   if (!endgame)
   {
   /** castling **/
   int atcks = 0;
   switch (board.CastleStatus(side))
   {
       case Board::CanCastleEitherSide:
         score += CAN_CASTLE; 
	 for (i = -1; i >= -2; --i)
	    if (board.num_attacks(kp+i,OppositeColor(side)))
	    {
	       atcks++;
	       break;
	    }
	 for (i = 1; i <= 2; ++i)
	    if (board.num_attacks(kp+i,OppositeColor(side)))
	    {
	       atcks++;
	       break;
	    }
	 score += (ATTACK_PREVENTS_CASTLING)*atcks;
	 break;
       case Board::CanCastleKSide:
         score += CANT_CASTLE_Q_SIDE; 
	 for (i = 1; i <= 2; ++i)
	    if (board.num_attacks(kp+i,OppositeColor(side)))
	    {
	       atcks++;
	       break;
	    }
         if (atcks)
	    score += (ATTACK_PREVENTS_CASTLING)*atcks;
	 break;
       case Board::CanCastleQSide:
         score += CANT_CASTLE_K_SIDE; 
	 for (i = -1; i >= -2; --i)
	    if (board.num_attacks(kp+i,OppositeColor(side)))
	    {
	       atcks++;
	       break;
	    }
	 if (atcks)
	    score += ATTACK_PREVENTS_CASTLING;
         break;
       case Board::CastledKSide:
         score += KCASTLE; break;
       case Board::CastledQSide:
         score += QCASTLE; break;
       case Board::CantCastleEitherSide:
         score += CANT_CASTLE; break;
    }
    }
    if (!endgame)
    {
    /*** king safety ***/
    const int oppMat = board.getMaterial(oside).value();
    const byte *data = KingSquares[(int)kp];
    for (j = 0; j <8 && *data != 255 ;j++)
    {
        if (board.num_attacks(*data++,oside) >0)
             score += ATTACK_NEAR_KING;
    }
    if (kp.Rank(side) != 1)
    {
        int king_off_back = KING_OFF_BACK_RANK;
        if (oppMat < 3500)
           king_off_back += 2;
        else if (oppMat < 4500)
          king_off_back += 1;
        score += king_off_back;
    }
    int dir = (side == White ? -RankIncr : RankIncr);
    // We count pawns in the front of the king as more
    // important if we are castled than not: and more
    // important if the opponent has a lot of material.
    Board::CastleType castt;
    int king_pawn_cover = KING_PAWN_COVER_MISSING;
    if ((castt = board.CastleStatus(side)) !=
        Board::CastledKSide && castt != Board::CastledQSide)
    {
        king_pawn_cover == -1;
    }
    else if (oppMat < 3500)
        king_pawn_cover += 4;
    else if (oppMat < 4500)
        king_pawn_cover += 2;

    if (kp.File() != 1)
    {
           Square sq(kp-1);
	   Square pawnsq(sq+dir);
           if (pawnsq.OnBoard() && !(board[pawnsq] == our_pawn ||
               board[pawnsq] == our_bishop))
                score += king_pawn_cover;
	   else if (board.PFileCount(sq.File()-1,side) == 0 &&
	        board.PFileCount(sq.File()-1,OppositeColor(side)) == 0)
	        score += KING_NEAR_OPEN_FILE;
    }
    Square pawnsq(kp+dir);
    if (pawnsq.OnBoard() &&
        !(board[pawnsq] == our_pawn ||
         board[pawnsq] == our_bishop))
        score += king_pawn_cover;
    else if (board.PFileCount(kp.File()-1,side) == 0)
    {
       score += KING_FILE_OPEN;
       if (board.PFileCount(kp.File()-1,OppositeColor(side)) == 0)
	     score += KING_NEAR_OPEN_FILE;
    }
    if (kp.File() != 8)
    {
           Square sq(kp+1);
	   Square pawnsq(sq+dir);
           if (pawnsq.OnBoard() &&
	        !(board[pawnsq] == our_pawn || board[pawnsq] == our_bishop))
                score += king_pawn_cover;
	    else if (board.PFileCount(sq.File()-1,side) == 0 &&
	        board.PFileCount(sq.File()-1,OppositeColor(side)) == 0)
	        score += KING_NEAR_OPEN_FILE;
    }
    if (kp.File() == 1 || kp.File() == 8)
    {
       if (kp.Rank(side) == 1)
         score -= KING_IN_CORNER;
    }
    Board::CastleType ct = board.CastleStatus(side);
    if (ct != Board::CastledKSide && ct != Board::CastledQSide)
    {
           int kfile = kp.File();
           for (i = 0; i < 3; i++)
           {
               if (kfile+1 <= 8)
               {
                  Square sq(kfile+1,2,side);
                  if (board[sq].IsEmpty())
                      score += K_SIDE_OPEN;
               }
           }
    }
    const int kpfile = kp.File();
    for (j=0;j<16;j++)
    {
       Square sq(board.PiecePos(board.OppositeSide(),j));
       if (!sq.IsInvalid())
       {
	    Piece::PieceType p = board[sq].Type();
	    if (p==Piece::Rook || p == Piece::Queen)
	    {
	       if (Util::Abs(kpfile - sq.File()) <= 1)
	       {
	          score += DANGER_ON_FILE;
		  break;
	       }
	    }
       }
    }
    }
    return score;
}

BOOL Scoring::check_en_prise( const Board &board,
  const ColorType attackingSide, const Piece p, const Square sq)
{
       BOOL en_prise = FALSE;
       if (p.Type() == Piece::Pawn ||
	        board.num_attacks(sq,attackingSide) == 0)
       {
           return FALSE;
       }
       if (board.pawn_attacks(sq,attackingSide) > 0)
       {
           // piece under attack by pawn(s)
           en_prise = TRUE;
       }
       else if (board.num_attacks(sq,OppositeColor(attackingSide)) == 0)
       {
           // undefended piece
           en_prise = TRUE;
       }
       else
       {
           const Attack_Entry &entr = 
	         board.get_attacks(sq,attackingSide);
	       if (Piece::Value(entr.min_attacker()) < p.Value())
	         en_prise = TRUE;
       }
       return en_prise;
}

int Scoring::evalu8( const Board &board )
{
    int score = 0;
    endgame = in_endgame(board,board.Side());
    score += MaterialScore(board,board.Side()) + positional_score(board);
    return score;
}

int Scoring::positional_score( const Board &board )
// returns a positional score
{
    int score;
    endgame = in_endgame(board,board.Side());
    score =  positional_score(board,board.Side()) -
             positional_score(board,board.OppositeSide());
   return score;
}
		
int Scoring::material_score( const Board &board )
// returns a material score
{
    return MaterialScore(board,board.Side());
}
