// Copyright 1994-1997 by Jon Dart.  All Rights Reserved.

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


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

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, 4, 4, 0, 0, 0,
     0, 0, 5, 7, 7, 5, 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};

static BOOL endgame;
static ColorType side_to_move;
static CRITICAL_SECTION pawnHashCritSection;

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));
    if (file_dist > rank_dist)
       return (file_dist);
    else
       return (rank_dist);
}

static BOOL uncatchable(const Board &board,
                        const Square pawnpos,
                        const Square kp,
                        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 oppkp_rank = oppkp.Rank(side);
   int rankdist = pawnpos.Rank(side) - oppkp_rank;

   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
       {
           if (
            8 - pawnpos.Rank(side) < distance(oppkp,Square(pawnpos.File(),8,side)))
              return TRUE;
       }
   }
   else
   {
       if (rankdist > 1)
       {
           // pawn > 1 square ahead
           return TRUE;
       }
       else
       {
           if (
            9 - pawnpos.Rank(side) < distance(oppkp,Square(pawnpos.File(),8,side)))
              return TRUE;
       }
   }
   // The opposing king could theoretically catch the pawn.  Now
   // see if our king is in a position to prevent this.
   int kpfile = kp.File();
   int pawnfile = pawnpos.File();
   if (pawnfile == 1)
   {
       // rook pawn.
       if (side == White)
       {
          if (kpfile == 2 && distance(kp,0) < distance(oppkp,0))
             return TRUE;
       }
       else
       {
          if (kpfile == 2 && distance(kp,56) < distance(oppkp,56))
             return TRUE;
       }
   }
   else if (pawnfile == 8)
   {
       if (side == White)
       {
          if (kpfile == 2 && distance(kp,7) < distance(oppkp,7))
             return TRUE;
       }
       else
       {
          if (kpfile == 2 && distance(kp,63) < distance(oppkp,63))
             return TRUE;
       }
   }
   else
   {
       // not a rook pawn
       int kprank = kp.Rank(side);
       int pawnrank = pawnpos.Rank(side);
       if (kprank >= pawnrank)
       {
          // a supported pawn on the 6th rank or past is uncatchable
          if (pawnrank >=6  && kprank >= 6)
          {
             Bitmap ka(Bearing::king_attacks[pawnpos]);
             ka.and(board.king_bits[side]);
             if (!ka.is_clear())
               return TRUE;
          }
          // If the king is directly ahead of the pawn and we
          // have the opposition, it is a win
          if (board.Side() != side && kp == pawnpos+Direction[side]*8)
          {
             if (oppkp.File() == pawnfile &&
                  ((int)oppkp - (int)kp) % 16 == 0)
                 return TRUE;
          }
          // If our king is >=2 squares ahead and the opposing king
          // is not closer, it is a win
          if (distance(kp,pawnpos) <= distance(oppkp,pawnpos))
          {
             if (kprank > pawnrank + 1)
                return TRUE;
          }
          // If we guard the pawn and the opposing king is behind or
          // on the same rank, we win
          if (rankdist >= 0 && (kpfile == pawnfile-1 ||
              kpfile == pawnfile+1) && (kprank == pawnrank ||
              kprank == pawnrank+1))
              return TRUE;
       }
   }
   return FALSE;
}   

// 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
static const int TWO_BISHOPS = 8;
// development terms
static const int BLOCKED_BISHOP = -4;
static const int LOW_BISHOP_MOBILITY = -2;
static const int BISHOP_BACK = -8;
static const int BAD_BISHOP = -5;
static const int CENTER_PAWN_BLOCK = -6;  // e or d pawn blocked by piece
static const int LONG_DIAG = 3; // bishop on long diagonal
static const int KNIGHT_BACK = -7;
static const int LOW_ROOK_MOBILITY = -3; // rook can't move horizontally
static const int ROOK_IN_CORNER = -2; // rook in corner
static const int NO_VERTICAL_MOBILITY = -2; // rook on rank 1, can't advance
static const int KNIGHT_IN_HOLE = 7;
static const int PIECE_IN_HOLE = 5;
static const int KNIGHT_MOBILITY = 1;
static const int ROOK_ON_7TH_RANK = 5;
static const int QUEEN_DEVELOP[10] = {6, 6, 6, 5, 5, 5, 4, 4, 4, 3};
static const int QUEEN_OUT = -6; // premature development of queen
static const int DOUBLED_ROOKS = 8; // on open file
static const int ROOK_ON_HALF_OPEN_FILE = 4;
static const int ROOK_ON_OPEN_FILE = 6;
// castling terms
static const int KCASTLE = 9;
static const int QCASTLE = 6;
static const int CAN_CASTLE = -2;
static const int CANT_CASTLE = -12;
static const int CANT_CASTLE_K_SIDE = -6;
static const int CANT_CASTLE_Q_SIDE = -4;
// pawn structure terms
static const int BACKWARD_PAWN = -11;  // on open file
static const int BACKWARD_PAWN_BLOCK = -4; // square in front of backward pawn
                                    // attacked by enemy pawn
static const int PAWN_CRAMP_2ND = -6; // a pawn ram on 2nd rank 
static const int PAWN_CRAMP_3RD = -4; // a pawn ram on 3rd rank 
static const int CENTRAL_ISOLANI = -12;
static const int ISOLANI = -9;
static const int ISOLATED_PAWN_BLOCK = -4; // pawn blocked or advance square 
				    // attacked
static const int DOUBLED_PAWNS = -18;
static const int TRIPLED_PAWNS = -30;
static const int MULTIPLE_PAWN_DEFECTS[] = { 0, 0, -4, -6, -7, -8, -8,
                                            -8, -9, -10, -10};
static const int PASSED_PAWN[8] = { 0, 15, 17, 20, 25, 41, 77, 0}; // score by rank
static const int PASSED_PAWN_BLOCK[8] =
                           { 2, 2, 4, 5, 6, 7, 7, 0 };
static const int PASSED_PAWN_CANT_ADVANCE = 4;
static const int ROOK_BEHIND_PP[8] = 
                           { 0, 6, 8, 11, 14, 17, 20, 0 };
static const int ENDGAME_PAWN_ADVANCE = 1;
static const int CONNECTED_PASSERS = 30;
static const int CONNECTED_PASSERS_6TH = 16;
static const int RIM_PASSED_PAWN = -3; // pawn on h-file or a-file
static const int PAWN_DUO = 5; // a la Kmoch
static const int UNSUPPORTED_PAWN = -2; // an advanced but unsupported pawn
static const int ADV_UNSUPPORTED_PAWN = -2; // an advanced but unsupported pawn

static const int OPP_KING_DISTANCE_FROM_PP = 2; // opposing king distance from passed
					 // pawn
static const int OPP_KING_AHEAD_OF_PP = -6;
// king safety terms
static const int CHECK = -5;  // being in check
static const int ATTACK_NEAR_KING = -2; // square near king attacked
static const int NO_KING_ESCAPE_SQUARE = -8; // king can't move to unattacked sq
static const int ONE_KING_ESCAPE_SQUARE = -4; // only one escape square
// king off back rank penalty, increases with opponent's material
static const int KING_OFF_BACK_RANK[10] = {-5, -6, -6, -7, -7, -8, -8, -9,
                                   -9, -9 };
// king pawn cover penalty, increases with opponent's material
static const int BP_COVER_MISSING[10] = {-1, -2, -3, -3, 4, -4, -5, -5,
                                   -6, -6 };
static const int NP_COVER_MISSING[10] = {-1, -1, -2, -2, -2, -2, -3, -3,
                                   -4, -4 };
static const int RP_COVER_MISSING[10] = {-1, -1, -2, -2, -2, -2, -3, -3,
                                   -4, -4 };
static const int KING_NEAR_OPEN_FILE[10] =  {-2, -3, -4, -5, -5, -5, -5, -5,
                                   -6, -6 };
static const int DANGER_ON_FILE = -3; // rook or queen on file near king
static const int DANGER_ON_RANK = -3; // rook or queen on rank near king
static const int KING_TRAPPED_IN_CORNER = -5; 
static const int KING_PIECE_PROXIMITY[10] = 
 { 0, -4, -14, -30, -48, 
   -48, -48, -48, -48, -48 };
static const int KING_PAWN_PROXIMITY[10] = 
 { 0, -4, -10, -17, -24, 
   -24, -24, -24, -24, -24 };
static const int KING_QUEEN_PROXIMITY = -5;   
// pawn in king territory defended by another pawn
static const int PAWN_STORM = -3;
// pawn on 5th rank in vicinity of king
static const int PAWN_STORM_5TH[4] = {0, -3, -7, -11};
// king position terms - valid only in endgame
static const int ADVANCED_KING = 4;
static const int KING_NEAR_PAWN = 1;
static const int KING_NEAR_PASSER = 3;
static const int OPPOSITION = 12;
static const int KING_DISTANCE = 4; // bringing our king closer to bare king
static const int KING_CAN_MOVE = -3; // for each square opposing bare king can move
static const int KING_ON_EDGE = -4;
static const int KING_IN_CORNER = -4; 
// piece safety terms
static const int BISHOP_TRAPPED = -280; // bishop trapped by pawn
// general endgame terms
static const int KING_PAWN_DISTANCE = -10; // distance from opposing king
//static const int OPP_KING_AHEAD_OF_PAWN = -8;
// KP vs. K endgame
static const int UNCATCHABLE_PAWN = 250;
static const int UNCATCHABLE_PAWN2 = 48; // a pawn that may become "uncatchable"
static const int KING_AHEAD_OF_PAWN = 32;
static const int KING_SAME_FILE = 32;
static const int DISTANCE_FROM_PAWN = -8;

static Bitmap passedW[64];
static Bitmap passedB[64];
static Bitmap backwardW[64];
static Bitmap backwardB[64];
static Bitmap w_king_attacks[64], b_king_attacks[64];
static Bitmap w_pawn_attacks[8], b_pawn_attacks[8];
static Bitmap king_file_danger[8];
static Bitmap king_rank_danger[8];
static Bitmap center_squares;
static Bitmap white_squares;
static Bitmap black_squares;
static int done_init = 0;
static int material_level[2];
static Pawn_Entry *w_pawn_hash_table = NULL;
static Pawn_Entry *b_pawn_hash_table = NULL;

#define PAWN_HASH_SIZE 50000

static void init_bitmaps()
{
   for (int i = 0; i < 64; i++)
   {
       Square sq(i);
       if (sq.InCenter())
       {
           center_squares.set(i);
       }
       if (sq.Color() == White)
          white_squares.set(i);
       else
          black_squares.set(i);
       int rank = sq.Rank(White);
       int file = sq.File();
       king_file_danger[file-1].set(i);
       if (file != 1)
       {
          king_file_danger[file-2].set(i);
       }
       if (file != 8)
       {
          king_file_danger[file].set(i);
       }
       king_rank_danger[rank-1].set(i);
       if (rank != 1)
       {
          king_rank_danger[rank-2].set(i);
       }
       if (rank != 8)
       {
          king_rank_danger[rank].set(i);
       }
       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);
	   }
       }
       if (rank < 4)
       {
           for (r = rank+1; r < rank+4; r++)
           {
              Square sq2(file,r,White);
              w_king_attacks[i].set((int)sq2);
              if (file!=1) 
                 w_king_attacks[i-1].set((int)sq2);
              else
                 w_king_attacks[i+2].set((int)sq2);
              if (file!=8) 
                 w_king_attacks[i+1].set((int)sq2);
              else
                 w_king_attacks[i-2].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);
	   }
       }
       if (rank < 4)
       {
           for (r = rank+1; r < rank+4; r++)
           {
              Square sq2(file,r,Black);
              b_king_attacks[i].set((int)sq2);
              if (file!=1) 
                 b_king_attacks[i-1].set((int)sq2);
              else
                 b_king_attacks[i+2].set((int)sq2);
              if (file!=8) 
                 b_king_attacks[i+1].set((int)sq2);
              else
                 b_king_attacks[i-2].set((int)sq2);
           }
       }
   }
   for (int file = 1; file < 9; file++)
   {
       if (file < 4)
       {
           for (int r = 2; r <= 4; r++)
           {
             w_pawn_attacks[file-1].set((int)Square(1,r,White));
             w_pawn_attacks[file-1].set((int)Square(2,r,White));
             w_pawn_attacks[file-1].set((int)Square(3,r,White));
             b_pawn_attacks[file-1].set((int)Square(1,r,Black));
             b_pawn_attacks[file-1].set((int)Square(2,r,Black));
             b_pawn_attacks[file-1].set((int)Square(3,r,Black));
           }
       }
       else if (file > 5)
       {
           for (int r = 2; r <= 4; r++)
           {
             w_pawn_attacks[file-1].set((int)Square(6,r,White));
             w_pawn_attacks[file-1].set((int)Square(7,r,White));
             w_pawn_attacks[file-1].set((int)Square(8,r,White));
             b_pawn_attacks[file-1].set((int)Square(6,r,Black));
             b_pawn_attacks[file-1].set((int)Square(7,r,Black));
             b_pawn_attacks[file-1].set((int)Square(8,r,Black));
           }
       }
       else
       {
           for (int r = 2; r <= 4; r++)
           {
             w_pawn_attacks[file-1].set((int)Square(4,r,White));
             w_pawn_attacks[file-1].set((int)Square(5,r,White));
             b_pawn_attacks[file-1].set((int)Square(4,r,Black));
             b_pawn_attacks[file-1].set((int)Square(5,r,Black));
           }       
       }
   }
}

void Scoring::init(const Board &board)
{
    if (!done_init)
    {
       init_bitmaps();
       done_init++;
#ifdef _WINDOWS
       InitializeCriticalSection(&pawnHashCritSection);
#endif   
    }
    if (!w_pawn_hash_table)
    {
       // fixed size for now
       //w_pawn_hash_table = new Pawn_Entry[PAWN_HASH_SIZE];
       //b_pawn_hash_table = new Pawn_Entry[PAWN_HASH_SIZE];
       w_pawn_hash_table = (Pawn_Entry*)(new char[PAWN_HASH_SIZE*sizeof(Pawn_Entry)]);
       b_pawn_hash_table = (Pawn_Entry*)(new char[PAWN_HASH_SIZE*sizeof(Pawn_Entry)]);
       memset(w_pawn_hash_table,0xff,PAWN_HASH_SIZE*sizeof(Pawn_Entry));
       memset(b_pawn_hash_table,0xff,PAWN_HASH_SIZE*sizeof(Pawn_Entry));
    }

    int wpawns = board.getMaterial(White).count(Piece::Pawn);
    int bpawns = board.getMaterial(Black).count(Piece::Pawn);
    int wmat = board.getMaterial(White).value() - 2000 - wpawns*64;
    int bmat = board.getMaterial(Black).value() - 2000 - bpawns*64;
    int wpc = board.getMaterial(White).piece_count();
    int bpc = board.getMaterial(Black).piece_count();
    if (wpc <= 2 || wmat < 780)
        material_level[White] = 0;
    else
        material_level[White] = Util::Min(9,wpc + 2*wmat/300 - 1);
    if (bpc <= 2 || bmat < 780)
        material_level[Black] = 0;
    else 
        material_level[Black] = Util::Min(9,bpc + 2*bmat/300 - 1);
    side_to_move = board.Side();
}      

void Scoring::cleanup()
{
    if (done_init)
    {
#ifdef _WINDOWS
       DeleteCriticalSection(&pawnHashCritSection);
#endif    
       delete [] w_pawn_hash_table;
       delete [] b_pawn_hash_table;
    }
}

BOOL Scoring::in_endgame( const Board &board )
{
    int w_pcount = board.getMaterial(White).pawn_count();
    int b_pcount = board.getMaterial(Black).pawn_count();
    return BOOL(
           (board.getMaterial(White).value() - w_pcount*64 <= 2576 /* queen + king */) &&
           (board.getMaterial(Black).value() - b_pcount*64 <= 
	    2576 /* queen + king */));
}

static int MaterialScore( const Board &board, const ColorType side )
{
    // Computes material score, from the perspective of 'side'.
    long ourmat_val, oppmat_val;
    int mdiff;
  
    const Material &ourmat = board.getMaterial(side);
    const Material &oppmat = board.getMaterial(OppositeColor(side));
    ourmat_val = ourmat.value();
    oppmat_val = oppmat.value();
    mdiff =  (int)(ourmat_val - oppmat_val);
    // If we are ahead in material give a bonus for trading down
    // pieces (but not pawns).
    // But don't do so if we are so low in material that we can't 
    // mate.
    // Also give a lower bonus for pawnless endings.
    //
    if (mdiff >= Constants::PawnValue)
    {
         if (ourmat.no_pawns() && ourmat_val < 2576)
            return (mdiff*8)/10; // not enough material to mate
         else if (mdiff < 4*64 && (ourmat_val + oppmat_val < 5250) &&
             ourmat.no_pawns() && oppmat.no_pawns())
         {
            // pawnless endings are generally hard to win (e.g. KR vs
            // KN).  Give a small bonus unless we are way ahead
            return mdiff + mdiff/10;
         }
         else
         {
            int nonpawns = 
               (int)(ourmat_val - ourmat.pawn_count()*64);
            if (nonpawns < 4000)
            {
               int max = 126 - ((nonpawns-2000)/16);
               return mdiff + Util::Min(max,max*(mdiff+500)/900);
            }
            else
               return mdiff;
         }
    }
    else if (mdiff <= -Constants::PawnValue)
    {
         if (oppmat.no_pawns() && oppmat_val < 2576)
            return (mdiff*8)/10;
         else if (mdiff > -4*64 && (ourmat_val + oppmat_val < 5250) &&
             oppmat.no_pawns() && ourmat.no_pawns())
            return mdiff + mdiff/10;
         else
         {
            int nonpawns = 
               (int)(oppmat_val - oppmat.pawn_count()*64);
            if (nonpawns < 4000)
            {
               int max = 126 - ((nonpawns-2000)/16);
               return mdiff - Util::Min(max,max*(-mdiff+500)/900);
            }
            else
               return mdiff;
         }
    }
    else
       return mdiff;
}

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 pawn_structure_score(const Board &board, ColorType side, 
  Square *passers, int &passers_count,
  Bitmap &holes, Bitmap &isolated_pawns,
  int &center_score)
{
    // This function computes a pawn structure score that depends
    // only on the location of pawns (of both colors)
    //
    int score = 0;
    passers_count = 0;
    int total_defects = 0;
    int incr = (side == White) ? -8 : 8;
    const ColorType oside = OppositeColor(side);
    BitIterator bi(board.pawn_bits[side]);
    int doubled_pawns = 0;
    center_score = 0;
    if (side == White)
    {
           Square sq1(51);
           Piece p(board[sq1]);
           if (p.Type() == Piece::Pawn && p.Color() == side)
           {
              Square sq(51-8);
              Piece p2(board[sq]);
              if (p2.Type() != Piece::Empty && p2.Color() == side)
                 center_score += CENTER_PAWN_BLOCK;
           }
           Square sq2(52);
           if (p.Type() == Piece::Pawn && p.Color() == side)
           {
              Square sq(52-8);
              Piece p2(board[sq]);
              if (p2.Type() != Piece::Empty && p2.Color() == side)
                 center_score += CENTER_PAWN_BLOCK;
           }
    }
    else
    {
           Square sq1(11);
           Piece p(board[sq1]);
           if (p.Type() == Piece::Pawn && p.Color() == side)
           {
              Square sq(11+8);
              Piece p2(board[sq]);
              if (p2.Type() != Piece::Empty && p2.Color() == side)
                 center_score += CENTER_PAWN_BLOCK;
           }
           Square sq2(12);
           if (p.Type() == Piece::Pawn && p.Color() == side)
           {
              Square sq(12+8);
              Piece p2(board[sq]);
              if (p2.Type() != Piece::Empty && p2.Color() == side)
                 center_score += CENTER_PAWN_BLOCK;
           }
    }
    for (;;)
    {
       Square sq(bi.Next());
       if (sq.IsInvalid())
          break;
       int file = sq.File();
       int rank = sq.Rank(side);
       BOOL backward = FALSE;
       BOOL passed = FALSE;
       BOOL isolated;
       BOOL file_open = (board.PFileCount(file-1,oside)==0);
       if (side == White)
       {
           center_score += PawnCenterScores[sq];
       }
       else
       {
           center_score += PawnCenterScores[63-(int)sq];
       }
       if (file_open)
       {
            Bitmap result;
            if (side == White)
                result = Bitmap::and(board.PawnBits(side),backwardW[sq]);
            else
                result = Bitmap::and(board.PawnBits(side),backwardB[sq]);
            backward = result.is_clear();
            if (side == White)
               result = Bitmap::and(board.PawnBits(oside),passedW[sq]);
            else
                result = Bitmap::and(board.PawnBits(oside),passedB[sq]);
            passed = result.is_clear();
       }
       if (file == 1)
          isolated = (board.PFileCount(1,side)==0);
       else if (file == 8)
    	  isolated = (board.PFileCount(6,side)==0);
       else
    	  isolated = (board.PFileCount(file-2,side)==0 &&
                      board.PFileCount(file,side)==0);
       int pr;
       int fileCount = 1;
       if ((pr = board.PFileCount(file-1,side)) > 1)
       {
           fileCount = pr;
           doubled_pawns++;
           if (pr == 2)
              score += DOUBLED_PAWNS/2;
   	   else
              score += TRIPLED_PAWNS/3;
       }
       if (backward && !isolated)
       {
           score += BACKWARD_PAWN;
           total_defects++;
           Square sq2(sq + incr);
           int patcks = board.pawn_attacks(sq2,oside).bit_count();
           if (patcks)
              score += patcks*BACKWARD_PAWN_BLOCK;
           if (rank == 2)
           {
   	       sq2 += incr;
               patcks = board.pawn_attacks(sq2,oside).bit_count();
               if (patcks)
   	          score += patcks*BACKWARD_PAWN_BLOCK;
           }
   	   if (!board.pawn_attacks(sq2,oside).is_clear())
              holes.set(sq2);
        }
        else if (isolated)
        {
           // don't give multiple pawns on the same file the
           // isolated penalty.
           if (!passed)
           {
              ++total_defects;
           }
           if (file>=3 && file<=6)
               score += CENTRAL_ISOLANI/fileCount;
           else
               score += ISOLANI/fileCount;
           isolated_pawns.set(sq);
        }
        if (!isolated && rank > 2)
        {
            if (side == White)
            {
                Bitmap atcks(Bearing::pawn_attacks[sq][White]);
                atcks.and(board.pawn_bits[White]);
                if (atcks.is_clear())
                {
    	           score += UNSUPPORTED_PAWN;
                   if (rank >4) score += ADV_UNSUPPORTED_PAWN;
                }
            }
            else
            {
                Bitmap atcks(Bearing::pawn_attacks[sq][Black]);
                atcks.and(board.pawn_bits[Black]);
                if (atcks.is_clear())
                {
                    score += UNSUPPORTED_PAWN;
                    if (rank >4) score += ADV_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);
                    int rdiff = rank - psqrank;
                    if (rdiff == 0 || rdiff == 1)
                    {
                        score += CONNECTED_PASSERS*rank/7;
                        if (psqrank >= 6)
                             score += CONNECTED_PASSERS_6TH;
                     }
                }
            }
            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;
                        }
                    }
                }
            }
        }
/** LATER
        else if (endgame)
        {
            // All things being equal, it is better to have advanced
            // pawns even if they are not passed.
            score += ENDGAME_PAWN_ADVANCE*rank;
        }
**/        
        if (rank == 2)
        {
            if (side == White)
            {
                if (board[sq-8] == Piece(Piece::Pawn,Black))
                     score += PAWN_CRAMP_2ND;
            }
            else
            {
                if (board[sq+8] == Piece(Piece::Pawn,White))
                    score += PAWN_CRAMP_2ND;
            }
        }
        else if (rank == 3)
        {
            if (side == White)
            {
                if (board[sq-8] == Piece(Piece::Pawn,Black))
                   score += PAWN_CRAMP_3RD;
            }
            else
            {
                if (board[sq+8] == Piece(Piece::Pawn,White))
                   score += PAWN_CRAMP_3RD;
            }
        }
        else if (file != 8 && board[sq+1] == Piece(Piece::Pawn,side))
        {
            score += PAWN_DUO;
        }
    }
    total_defects += (doubled_pawns/2);
    if (total_defects > 10) total_defects = 10;
    score += MULTIPLE_PAWN_DEFECTS[total_defects];
    return score;
}

int Scoring::positional_score( const Board &board, const ColorType side)
{
   int score = 0;
   int 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;
   Bitmap holes,isolated_pawns;

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

   const int incr = 8*Direction[side];

   // Query the pawn hash table to see if we have found a position
   // before that has the same pawn structure.  If so, we can skip
   // some of the scoring calculations.
#ifdef _WINDOWS   
   // acquire a lock because the pawn hash table is static and
   // may be used by >1 thread.
   EnterCriticalSection(&pawnHashCritSection);    
#endif   
   Pawn_Entry *hash_entry = NULL;
   BOOL pawn_entry_found;
   if (side == White)
   {
       hash_entry = &w_pawn_hash_table[quickmod(board.pawn_hash(),PAWN_HASH_SIZE)];
   }
   else
   {
       hash_entry = &b_pawn_hash_table[quickmod(board.pawn_hash(),PAWN_HASH_SIZE)];
   }
#ifdef _WINDOWS   
   LeaveCriticalSection(&pawnHashCritSection);    
#endif   
   pawn_entry_found = (hash_entry->hash_code() == board.pawn_hash());
   int pawn_score, center_score;
   if (pawn_entry_found)
   {
       passers_count = hash_entry->get_passers_count();
       memcpy(passers,hash_entry->get_passers(),passers_count);
       isolated_pawns = hash_entry->get_isolated();
       holes = hash_entry->get_holes();
       pawn_score = hash_entry->get_score();
       center_score = hash_entry->get_center_score();
#ifdef _DEBUG
       Square passers2[8];
       int passers_count2 = 0;
       int center_score2;
       Bitmap holes2,isolated_pawns2;
       int score2 = pawn_structure_score(board,side,passers2,passers_count2,
                  holes2,isolated_pawns2,center_score2);
       ASSERT(pawn_score == score2);
       ASSERT(holes == holes2);
       ASSERT(passers_count == passers_count2);
       ASSERT(isolated_pawns2 == isolated_pawns);
       //ASSERT(center_score2 == center_score);
#endif
   }
   else
   {
       // pawn position not found, calculate the data we need
       pawn_score = pawn_structure_score(board,side,passers,passers_count,
                  holes,isolated_pawns,center_score);
       // save the pawn position for future use. Overwrite any
       // existing position
#ifdef _WINDOWS       
       EnterCriticalSection(&pawnHashCritSection);    
#endif       
       *hash_entry = Pawn_Entry(board.pawn_hash(),
         pawn_score,passers,passers_count,holes,
         isolated_pawns,center_score);
#ifdef _WINDOWS         
       LeaveCriticalSection(&pawnHashCritSection);    
#endif       
   }
   score += pawn_score;   
   if (!endgame)
     score += center_score;

    // Compute "second order" pawn structure terms that involve
    // interaction of pawns and other pieces.
    //
    Bitmap pieces_in_holes(holes);
    Bitmap knights_in_holes(holes);
    knights_in_holes.and(board.knight_bits[oside]);
    score += KNIGHT_IN_HOLE*knights_in_holes.bit_count();
    pieces_in_holes.and(board.occupied[oside]);
    pieces_in_holes.andNot(board.knight_bits[oside]);
    score += PIECE_IN_HOLE*pieces_in_holes.bit_count();

    Bitmap b(isolated_pawns);
    if (side == White)
    {
       b.shr(8);
    }
    else
    {
       b.shl(8);
    }
    Bitmap blockers(board.occupied[oside]);
    blockers.or(board.all_pawn_attacks(oside));
    b.and(blockers);
    score += b.bit_count()*ISOLATED_PAWN_BLOCK;

    // check for a rook behind passed pawns
    for (int i = 0; i < passers_count; i++)
    {
        Square sq(passers[i]);
        int file = sq.File();
        int rank = sq.Rank(Black);
        if (side == White)
        {
            Bitmap occ = board.occupiedR90[White];
            int shift = (8-file)<<3;
            byte atcks = (FAST_SHIFT_R(occ,shift)).byte_value();
            if (!Bitmap::and(Bearing::unrot90[Bearing::next_bit_right[atcks][rank-1]][8-file],board.rooksqueens[White]).is_clear())
                score += ROOK_BEHIND_PP[rank-1];
            Square sq2(sq-8);
            occ = board.occupiedR90[Black];
            atcks = (FAST_SHIFT_R(occ,shift)).byte_value();
            //atcks &= (1<<(8-rank))-1;
            if (Bearing::next_bit_right[atcks][8-rank] != 0)
            {
               int hit = PASSED_PAWN_BLOCK[rank-1];
	       if (!board[sq2].IsEmpty() && board[sq2].Color() == oside)
	          score -= (hit + PASSED_PAWN_CANT_ADVANCE);
               else
	          score -= hit;
            }
        }
        else
        {
            Bitmap occ = board.occupiedR90[Black];
            int shift = (8-file)<<3;
            byte atcks = (FAST_SHIFT_R(occ,shift)).byte_value();
            if (!Bitmap::and(Bearing::unrot90[ Bearing::next_bit_left[atcks][rank-1]][8-file],board.rooksqueens[Black]).is_clear())
               score += ROOK_BEHIND_PP[rank-1];
            Square sq2(sq+8);
            occ = board.occupiedR90[White];
            atcks = (FAST_SHIFT_R(occ,shift)).byte_value();
            //atcks &= (byte)(-1<<rank);
            if (Bearing::next_bit_right[atcks][rank-1] != 0)
            {
               int hit = PASSED_PAWN_BLOCK[rank-1];
	       if (!board[sq2].IsEmpty() && board[sq2].Color() != side)
	          score -= (hit + PASSED_PAWN_CANT_ADVANCE);
               else
	          score -= hit;
            }
        }
        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;
        }
   }

   Bitmap piece_bits(board.occupied[side]);
   piece_bits.andNot(board.pawn_bits[side]);
   /* Calculate development for each piece */
   BitIterator pieces(piece_bits);
   for (;;)
   {
      Square sq(pieces.Next());
      if (sq.IsInvalid())
          break;
      else
      {
          Piece piece = board[sq];
          const int rank = sq.Rank(side);
	  const int file = sq.File();
          switch (piece.Type())
	  {
          case Piece::Knight:
	     if (endgame)
	         break;
	     score += KnightCenterScores[sq];
             if (rank == 1)
	         devel_score += KNIGHT_BACK;
             {
                 // Compute knight mobility.  We award a bonus for
                 // each square to which the knight can move, that
                 // is not occupied or attacked by an enemy pawn.
                 Bitmap ka(Bearing::knight_attacks[sq]);
                 ka.andNot(board.occupied[side]);
                 ka.andNot(board.occupied[oside]);
                 ka.andNot(board.all_pawn_attacks(oside));
                 score += ka.bit_count()*KNIGHT_MOBILITY;
             }
	     break;
          case Piece::Bishop:
             if (sq.Color() == White) 
                 haveWbishop = TRUE;
             else 
                 haveBbishop = TRUE;
             if (!endgame)
	     {
                  // compute bishop mobility (squares to which the
                  // bishop can move, not counting captures)
                 int mobl =board.bishop_mobility(sq);
		  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.pawn_attacks(psq,oside).is_clear())
                     {
                        score += BISHOP_TRAPPED;
                     }
                  }
                  else if (file==8)
                  {
                     Square psq(7,6,side);
                     if (board[psq] == Piece(Piece::Pawn,
                         oside) &&
                         !board.pawn_attacks(psq,oside).is_clear())
                     {
                        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)
             {
                 int bonus;
                 if (board.PFileCount(file-1,oside)==0)
                    bonus = ROOK_ON_OPEN_FILE;
                 else
                    bonus = ROOK_ON_HALF_OPEN_FILE;
                 int newsq = (int)sq-8;
                 while (newsq > 0)
                 {
                    if (!board[sq].IsEmpty())
                    {
                        if (board[sq] == piece)
                           bonus += DOUBLED_ROOKS/2;
                        break;
                    }
                    newsq -= 8;
                 }
                 newsq = (int)sq+8;
                 while (newsq < 64)
                 {
                    if (!board[sq].IsEmpty())
                    {
                        if (board[sq] == piece)
                           bonus += DOUBLED_ROOKS/2;
                        break;
                    }
                    newsq += 8;
                 }
                 // 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;
             }
             // check that we have mobility
             if (!endgame)
             {
             int mobl = board.rook_mobility(sq);
             if (mobl == 0) // rook can't move horizontally
             {
                score += LOW_ROOK_MOBILITY;
             }
             else if (mobl <= 4)
                score += (mobl-1);
             else
                score += 3;
             if (rank == 1)
             {
                 if (!board[sq+Direction[side]].IsEmpty())
                    score += NO_VERTICAL_MOBILITY;
                 if (file == 1 || file == 8)
                    score += ROOK_IN_CORNER;
             }
             }
	     break;
	  default:
	     break;
	  }
      }
   }
   score += devel_score;
   if (!endgame && have_queen && !queen_back)
   {
      if (devel_score == 0 && 
	 (int)board.CastleStatus(side) > 2)
	   score += QUEEN_DEVELOP[material_level[oside]];
      else // premature development of queen
           score += QUEEN_OUT;
   }
   if (haveWbishop && !haveBbishop)
   {
      Bitmap wpawns(white_squares);
      wpawns.and(board.pawn_bits[side]);
      whitePawns = wpawns.bit_count();
      Bitmap bpawns(black_squares);
      bpawns.and(board.pawn_bits[side]);
      blackPawns = bpawns.bit_count();
      if ((whitePawns + blackPawns > 4) && (whitePawns > blackPawns))
         score += BAD_BISHOP;
   }
   else if (haveBbishop && !haveWbishop)
   {
      Bitmap wpawns(white_squares);
      wpawns.and(board.pawn_bits[side]);
      whitePawns = wpawns.bit_count();
      Bitmap bpawns(black_squares);
      bpawns.and(board.pawn_bits[side]);
      blackPawns = bpawns.bit_count();
      if ((whitePawns + blackPawns > 4) && (blackPawns > whitePawns))
          score += BAD_BISHOP;
   }

      if (mopping_up(board,OppositeColor(side)))
      {
           // Our opponent has much more material, so try to avoid
           // being mated.
           score += KingCenterScores[(int)kp];
           const byte *data = KingSquares[(int)kp];
           int safe_square = 0;
           for (j = 0; j <8 && *data != 255 ;j++)
           {
              Square sq(*data++);
              if (board.any_attacks(sq,oside) >0)
                  score += ATTACK_NEAR_KING;
              else if (board[sq].IsEmpty())
                 safe_square++;
           }
           if (!safe_square)
               score += NO_KING_ESCAPE_SQUARE;
           else if (safe_square == 1)
               score += ONE_KING_ESCAPE_SQUARE;
              
	   // Keep our king away from opposing king (from the attacking side's
           // point of view, try to bring the kings close).
	   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;
           // Encourage putting the king on the edge of the board:
	   if (kp.OnEdge())
           {
	       score += KING_ON_EDGE;
               if (kp.InCorner())
                  score += KING_IN_CORNER;
           }
      }
   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 (board.getMaterial(side).king_only() &&
               (unsigned)board.getMaterial(oside).infobits() == 0x8048)
      {
           // KBNK endgame, special case.
	   const int opprank = oppkp.Rank(White);
	   const int oppfile = oppkp.File();
	   ColorType bishopColor;
           Bitmap bishops = board.bishop_bits[side];
           Square sq(bishops.FirstOne());
           bishopColor = sq.Color();
	   // 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 if ((unsigned)board.getMaterial(side).infobits() == 0x8001 &&
       		(unsigned)board.getMaterial(oside).king_only())
       {
           // KPK endgame
	   Square pawnpos = passers[0];
           if (uncatchable(board,pawnpos,kp,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;
	       }
	       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(oside).no_pieces() && passers_count)
       {
           // our opponent has no pieces and we have at least one
           // passed pawn
           int uncatchables = 0;
           int minfile = 9;
           int maxfile = 0;
           Square minfilesq, maxfilesq;
	   for (int i = 0; i < passers_count; i++)
	   {
	       Square passer(passers[i]);
               if (uncatchable(board,passer,kp,oppkp,side))
               {
	            score += UNCATCHABLE_PAWN;
                    ++uncatchables;
               }
               if (passer.File() < minfile)
               {
                  minfile = passer.File(); minfilesq = passer;
               }
               if (passer.File() > maxfile)
               {
                  maxfile = passer.File(); maxfilesq = passer;
               }
               Bitmap king_squares(Bearing::king_attacks[passer]);
               king_squares.and(board.king_bits[side]);
               if (!king_squares.is_clear())
                   score += KING_NEAR_PASSER;
           }
           if (!uncatchables && passers_count > 1)
           {
               int d21 = distance(oppkp,minfilesq);
               int d22 = distance(oppkp,maxfilesq);
               int x1 = d21 + maxfilesq.Rank(side) -1;
               int x2 = d22 + minfilesq.Rank(side) -1;
               int y = 0;
               if (board.Side() != side) y = 1;
               if (x1+y >= 8 && x2+y >= 8)
               {
                   score += UNCATCHABLE_PAWN;
                   uncatchables++;
               }
               else if (x2+y < 8 && uncatchable(board,
                      Square(minfilesq.File(),
                             x2+y,
                             side),
                      kp,
                      maxfilesq,side))
               {
                   score += UNCATCHABLE_PAWN;
                   uncatchables++;
               }
               else if (x1+y < 8 && uncatchable(board,
                      Square(maxfilesq.File(),
                             x1+y,
                             side),
                      kp,
                      minfilesq,side))
               {
                   score += UNCATCHABLE_PAWN;
                   uncatchables++;
               }
           }
           if (!uncatchables)
           {
              // Centralize the king
              score += KingCenterScores[kp];
              if (kp.Rank(side) > 4)
                 score += ADVANCED_KING;
              // bonus for king near pawns
              Bitmap king_squares(Bearing::king_attacks[kp]);
              king_squares.and(
                Bitmap::or(board.pawn_bits[White],board.pawn_bits[Black]));
              score += king_squares.bit_count()*KING_NEAR_PAWN;
           }
       }
       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
           Bitmap tmp(Bearing::king_attacks[(int)kp]);
           tmp.and(Bitmap::or(board.pawn_bits[White],board.pawn_bits[Black]));
           score += KING_NEAR_PAWN*tmp.bit_count();
           // give a bonus for unstoppable passers depending on the
           // material our opponent has.  (Our opponent has
           // at least one piece).
           const Material &opp_mat = board.getMaterial(oside);
           int xtramat = opp_mat.value() - 2000;
           static int factor[] = { 10, 9, 8, 6, 4, 2, 1, 1, 1, 1, 1};
           if (xtramat < 2000)
           {
              for (int i = 0; i < passers_count; i++)
              {
	          Square pawnpos(passers[i]);
                  if (uncatchable(board,pawnpos,kp,oppkp,side))
                  {
	             score += UNCATCHABLE_PAWN2*factor[xtramat/300]/10;
                  }
              }
           }
       }
   }

   if (!endgame)
   {
   /** castling **/
   int atcks = 0;
   switch (board.CastleStatus(side))
   {
       case Board::CanCastleEitherSide:
         score += CAN_CASTLE; 
	 break;
       case Board::CanCastleKSide:
         score += CANT_CASTLE_Q_SIDE; 
	 break;
       case Board::CanCastleQSide:
         score += CANT_CASTLE_K_SIDE; 
         break;
       case Board::CastledKSide:
         score += KCASTLE; break;
       case Board::CastledQSide:
         score += QCASTLE; break;
       case Board::CantCastleEitherSide:
         score += CANT_CASTLE; break;
    }
    }
    return score;
}

int Scoring::king_safety( const Board &board)
{
   if (endgame)
       return 0;
   else
       return 10*king_safety(board,board.Side())/9 -
          9*king_safety(board,board.OppositeSide())/10;
}

int Scoring::king_safety( const Board &board, const ColorType side)
{
    const ColorType oside = OppositeColor(side);
    Square kp(board.KingPos(side));
    Piece our_pawn(Piece::Pawn,side);
    Piece our_bishop(Piece::Bishop,side);
    int score = 0;
    const byte *data = KingSquares[(int)kp];
    
    Bitmap sources, source;
    int safe_square = 0;
    const int mlevel = material_level[oside];
    if (kp.Rank(side) != 1)
    {
        int king_off_back = KING_OFF_BACK_RANK[mlevel];
        score += king_off_back;
    }
    int dir = (side == White ? -RankIncr : RankIncr);
    int defects = 0;
    const int kpfile = kp.File();
    if (kpfile >= 6)
    {
	   Square pawnsq(6,2,side);
           if (board[pawnsq] != our_pawn)
                score += BP_COVER_MISSING[mlevel];
	   if (board.PFileCount(5,side) == 0)
           {
               if (board.PFileCount(5,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
           pawnsq = Square((int)pawnsq+1);
           if (board[pawnsq] != our_pawn && board[pawnsq] !=
               our_bishop)
           {
                score += NP_COVER_MISSING[mlevel];
           }
	   if (board.PFileCount(6,side) == 0)
           {
               if (board.PFileCount(6,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
           pawnsq = Square((int)pawnsq+1);
           if (board[pawnsq] != our_pawn)
                score += RP_COVER_MISSING[mlevel];
	   if (board.PFileCount(7,side) == 0)
           {
               if (board.PFileCount(7,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
    }
    else if (kpfile <= 3)
    {
	   Square pawnsq(1,2,side);
           if (board[pawnsq] != our_pawn)
                score += RP_COVER_MISSING[mlevel];
	   if (board.PFileCount(0,side) == 0)
           {
               if (board.PFileCount(0,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
           pawnsq = Square((int)pawnsq+1);
           if (board[pawnsq] != our_pawn && board[pawnsq] != our_bishop)
           {
                score += BP_COVER_MISSING[mlevel];
           }
	   if (board.PFileCount(1,side) == 0)
           {
               if (board.PFileCount(1,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
           pawnsq = Square((int)pawnsq+1);
           if (board[pawnsq] != our_pawn)
                score += RP_COVER_MISSING[mlevel];
	   if (board.PFileCount(2,side) == 0)
           {
               if (board.PFileCount(2,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
    }
    else
    {
	   if (board.PFileCount(4,side) == 0)
           {
               if (board.PFileCount(4,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
	   if (board.PFileCount(3,side) == 0)
           {
               if (board.PFileCount(3,oside) == 0)
	          defects += 2; //KING_NEAR_OPEN_FILE;
               else
                  defects += 1; //KING_NEAR_HALF_OPEN_FILE;
           }
    }
    score += defects*KING_NEAR_OPEN_FILE[mlevel];
    if (kp.InCorner())
    {
       score += KING_IN_CORNER;
    }
    Bitmap rooksqueens(board.rooksqueens[oside]);
    rooksqueens.and(king_file_danger[kpfile-1]);
    score += DANGER_ON_FILE*rooksqueens.bit_count();
    rooksqueens = board.rooksqueens[oside];
    rooksqueens.and(king_rank_danger[kp.Rank(White)-1]);
    score += DANGER_ON_RANK*rooksqueens.bit_count();
    if (kp.Rank(side) < 4)
    {

        if (side == White)
        {
           Bitmap katcks(w_king_attacks[kp]);
           Bitmap patcks(w_pawn_attacks[kp.File()-1]);
           Bitmap result(Bitmap::and(katcks,board.occupied[Black]));
           result.andNot(board.pawn_bits[Black]);
           int attacks = result.bit_count();
           score += KING_PIECE_PROXIMITY[attacks];
           result.and(board.queen_bits[Black]);
           if (!result.is_clear())
             score += KING_QUEEN_PROXIMITY;
           // check for enemy pawns near the king
           patcks.and(board.pawn_bits[Black]);
           if (!patcks.is_clear())
           {
               // pawns near the king are bad
               score += KING_PAWN_PROXIMITY[patcks.bit_count()];
               patcks.and(board.all_pawn_attacks(Black));
               // pawns near the king and defended by other pawns
               // are bad
               if (!patcks.is_clear())
                  score += PAWN_STORM;
           }
           // pawns on 5th rank are ready to storm and are bad
           Piece enemyPawn(Piece::Pawn,Black);
           int n = 0;
           if (kp.File() < 4)
           {
               if (board[32] == enemyPawn)
                  ++n;
               if (board[33] == enemyPawn)
                  ++n;
               if (board[34] == enemyPawn)
                  ++n;
               score += PAWN_STORM_5TH[n];
           }
           else if (kp.File() > 5)
           {
               if (board[39] == enemyPawn)
                  ++n;
               if (board[38] == enemyPawn)
                  ++n;
               if (board[37] == enemyPawn)
                  ++n;
               score += PAWN_STORM_5TH[n];
           }
           if ((int)kp == 56 || (int)kp == 57)
           {
              Piece p = board[40];
              if (!p.IsEmpty() && p.Color() != side 
                && (p.Type() == Piece::Queen || p.Type() == Piece::Bishop))
              {
                 score += KING_TRAPPED_IN_CORNER;
              }
           }
           else if ((int)kp == 62 || (int)kp == 63)
           {
              Piece p = board[47];
              if (!p.IsEmpty() && p.Color() != side 
                && (p.Type() == Piece::Queen || p.Type() == Piece::Bishop))
              {
                 score += KING_TRAPPED_IN_CORNER;
              }
           }
        }
        else
        {
           Bitmap katcks(b_king_attacks[kp]);
           Bitmap patcks(b_pawn_attacks[kp.File()-1]);
           Bitmap result(Bitmap::and(katcks,board.occupied[White]));
           result.andNot(board.pawn_bits[White]);
           int attacks = result.bit_count();
           score += KING_PIECE_PROXIMITY[attacks];
           result.and(board.queen_bits[White]);
           if (!result.is_clear())
             score += KING_QUEEN_PROXIMITY;
           // check for enemy pawns near the king
           patcks.and(board.pawn_bits[White]);
           if (!patcks.is_clear())
           {
               score += KING_PAWN_PROXIMITY[patcks.bit_count()];
               patcks.and(board.all_pawn_attacks(White));
               if (!patcks.is_clear())
                  score += PAWN_STORM;
           }
           // pawns on 5th rank are ready to storm and are bad
           Piece enemyPawn(Piece::Pawn,White);
           int n = 0;
           if (kp.File() < 4)
           {
               if (board[40] == enemyPawn)
                  ++n;
               if (board[41] == enemyPawn)
                  ++n;
               if (board[42] == enemyPawn)
                  ++n;
               score += PAWN_STORM_5TH[n];
           }
           else if (kp.File() > 5)
           {
               if (board[47] == enemyPawn)
                  ++n;
               if (board[46] == enemyPawn)
                  ++n;
               if (board[45] == enemyPawn)
                  ++n;
               score += PAWN_STORM_5TH[n];
           }
           if ((int)kp == 0 || (int)kp == 1)
           {
              Piece p = board[16];
              if (!p.IsEmpty() && p.Color() != side 
                && (p.Type() == Piece::Queen || p.Type() == Piece::Bishop))
              {
                 score += KING_TRAPPED_IN_CORNER;
              }
           }
           else if ((int)kp == 6 || (int)kp == 7)
           {
              Piece p = board[23];
              if (!p.IsEmpty() && p.Color() != side 
                && (p.Type() == Piece::Queen || p.Type() == Piece::Bishop))
              {
                 score += KING_TRAPPED_IN_CORNER;
              }
           }
        }
    }
    return score;
}


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

void Scoring::scores(const Board &board, 
           int material[], int position[], int ksafety[])
{
    endgame = in_endgame(board);
    material[White] = board.getMaterial(White).value();
    material[Black] = board.getMaterial(Black).value();
    position[White] = positional_score(board,White);
    position[Black] = positional_score(board,Black);
    ksafety[White] = king_safety(board,White);
    ksafety[Black] = king_safety(board,Black);
}

int Scoring::positional_score( const Board &board )
// returns a positional score
{
    int score;
    endgame = in_endgame(board);
    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());
}
