Page 1 of 1

Lazy Eval

PostPosted: 19 Aug 2005, 16:06
by Eduardo Waghabi
Lazy Eval:

How many of you guys use it? How much of a speed-up did you get? What do you generally think about it?

I?m doing some experimenting with a very simple implementation, that is, I compute material balance with piece-square tables and check the value against alpha and beta using a defined margin. On Q-Search only.

So far the results haven?t been so clear. Setting a too small safe margin makes the program faster, but it plays worse.

Apparently, for my current evaluation function, 225 (centipawns) is the safest value that still gets me some speed up, namely 4%. With 100 I?ve got 7%, but the version got worse scores on EPD suites than the non-LE version.

In games between versions, LE version seems to do better, but barely.

So I?m not sure if the benefits are worth the risk of tactical blunders.

Re: Lazy Eval

PostPosted: 19 Aug 2005, 18:15
by Dann Corbit
Eduardo Waghabi wrote:Lazy Eval:

How many of you guys use it?

It is used in Beowulf.
How much of a speed-up did you get?

Considerable. Probably adds 50 Elo.

What do you generally think about it?

It is a very good idea.

I?m doing some experimenting with a very simple implementation, that is, I compute material balance with piece-square tables and check the value against alpha and beta using a defined margin. On Q-Search only.

So far the results haven?t been so clear. Setting a too small safe margin makes the program faster, but it plays worse.

Apparently, for my current evaluation function, 225 (centipawns) is the safest value that still gets me some speed up, namely 4%. With 100 I?ve got 7%, but the version got worse scores on EPD suites than the non-LE version.

In games between versions, LE version seems to do better, but barely.

So I?m not sure if the benefits are worth the risk of tactical blunders.


Here is the lazy eval from Beowulf:
Code: Select all
/* LazyEval() basically just sums up the material score on the board. It also
 * flags and scores passed pawns.  It does this by remembering where the first
 * and last pawns of each colour in each file were and checking for pawns without
 * opposing blockers / flanking attackers. */
int LazyEval(Board *B) {
  BITBOARD pieces;
  int sq,p,Pawns[16],npawns=0,x, offside, wkx, bkx, dangerw=0, dangerb=0;
  int LastBlackPawn[8]={64,64,64,64,64,64,64,64},LastWhitePawn[8]={-1,-1,-1,-1,-1,-1,-1,-1};
  int score=0;
  int wmin=0,wmaj=0,bmin=0,bmaj=0;
#ifdef ExchangeOff
  int sdiff=0;
#endif
#ifdef DEBUG_EVAL
  int oldscore = 0, material=0;
#endif

  B->Gamestage = Gamestage[B->WPts + B->BPts];
  wmat = bmat = TRUE;

   /* Danger level of opposing pieces (for scoring connected PPs and offside majorities). */
  if (B->WhiteQueens) dangerb=2;
  else if (B->WhiteRooks && B->WhiteBishops) dangerb=1;
  if (B->BlackQueens) dangerw=2;
  else if (B->BlackRooks && B->BlackBishops) dangerw=1;

#ifdef DEBUG_EVAL
  fprintf(stdout,"Lazy Eval\n---------\n");
  fprintf(stdout,"Game Stage = %d   [0=Opening, 5=Late Endgame]\n",B->Gamestage);
#endif

  /* Loop through all pieces, adding up their basic scores */
  /* White pieces */
  pieces = B->WhitePawns;
  while (pieces) {
    sq = FirstPiece(pieces);
    score += PAWN_SCORE + PawnPosition[B->Gamestage][sq];
    LastWhitePawn[File(sq)] = sq;
    Pawns[npawns++] = sq;
    RemoveFirst(pieces);
  }
  pieces = B->WhiteRooks;
  while (pieces) {
    sq = FirstPiece(pieces);
    score += ROOK_SCORE + RookPosition[sq];
    wmaj++;
    RemoveFirst(pieces);
  }
  pieces = B->WhiteKnights;
  while (pieces) {
    sq = FirstPiece(pieces);
    score += KNIGHT_SCORE + KnightPosition[sq];
    wmin++;
    RemoveFirst(pieces);
  }
  pieces = B->WhiteBishops;
  while (pieces) {
    sq = FirstPiece(pieces);
    score += BISHOP_SCORE + BishopPosition[sq];
    wmin++;
    RemoveFirst(pieces);
  }
  pieces = B->WhiteQueens;
  while (pieces) {
    sq = FirstPiece(pieces);
    score += QUEEN_SCORE + QueenPosition[sq];
    if (B->Gamestage <= Middle && Rank(sq)<Rank5)
      score -= OVERSTRETCHED * (1 + Middle - B->Gamestage);
    wmaj += 2;
    RemoveFirst(pieces);
  }

  /* Black pieces */
  pieces = B->BlackPawns;
  while (pieces) {
    sq = FirstPiece(pieces);
    score -= (PAWN_SCORE + PawnPosition[B->Gamestage][(Flip[sq])]);
    if (LastBlackPawn[File(sq)] == 64)
      LastBlackPawn[File(sq)] = sq;
    Pawns[npawns++] = sq;
    RemoveFirst(pieces);
  }
  pieces = B->BlackRooks;
  while (pieces) {
    sq = FirstPiece(pieces);
    score -= (ROOK_SCORE + RookPosition[(Flip[sq])]);
    bmaj++;
    RemoveFirst(pieces);
  }
  pieces = B->BlackKnights;
  while (pieces) {
    sq = FirstPiece(pieces);
    score -= (KNIGHT_SCORE + KnightPosition[(Flip[sq])]);
    bmin++;
    RemoveFirst(pieces);
  }
  pieces = B->BlackBishops;
  while (pieces) {
    sq = FirstPiece(pieces);
    score -= (BISHOP_SCORE + BishopPosition[(Flip[sq])]);
    bmin++;
    RemoveFirst(pieces);
  }
  pieces = B->BlackQueens;
  while (pieces) {
    sq = FirstPiece(pieces);
    score -= (QUEEN_SCORE + QueenPosition[(Flip[sq])]);
    if (B->Gamestage <= Middle && Rank(sq) > Rank4)
      score += OVERSTRETCHED * (1 + Middle - B->Gamestage);
    bmaj += 2;
    RemoveFirst(pieces);
  }

   /* King locations */
  switch (B->Gamestage) {
    case (Opening)  : score += CornerBoard3[B->WhiteKing] - CornerBoard3[B->BlackKing]; break;
    case (EarlyMid) : score += CornerBoard2[B->WhiteKing] - CornerBoard2[B->BlackKing]; break;
    case (Middle)   : score += CornerBoard2[B->WhiteKing] - CornerBoard2[B->BlackKing]; break;
    case (LateMid)  : score += CornerBoard[B->WhiteKing]  - CornerBoard[B->BlackKing];  break;
    case (Endgame)  : score += CentreBoard[B->WhiteKing]  - CentreBoard[B->BlackKing];  break;
    case (LateEnd)  : score += CentreBoard2[B->WhiteKing] - CentreBoard2[B->BlackKing]; break;
  }
  wkx = File(B->WhiteKing);
  bkx = File(B->BlackKing);
 
#ifdef DEBUG_EVAL
  for (sq=0;sq<64;sq++) {
    if (B->pieces[sq]>0) material += PieceValue100[(B->pieces[sq])];
    else material -= PieceValue100[-(B->pieces[sq])];
  }
  fprintf(stdout,"\nMaterial Eval : %d\n",material);
  fprintf(stdout,"Positional Eval : %d\n",score-material);
#endif

   /* Reset Passed Pawn Bitboards */
  PassedMask = HiddenMask = EMPTY;
   /* Cycle through the pawns we found earlier.  Check for passed pawns. */
  if (B->Gamestage != Opening) {
    for (p=0;p<npawns;p++) {
      sq = Pawns[p];
      x = File(sq);
      if (B->pieces[sq]==wpawn) {
        if (LastBlackPawn[x]>sq &&
          (x==0 || LastBlackPawn[x-1]>=(sq-1)) &&
          (x==7 || LastBlackPawn[x+1]>sq)) {
          PassedMask |= Mask[sq];
        }
        /* Check for disguised passed pawns, where only one of a pair of connected pawns
         * is blocked by an opposing pawn.  Clearly, one of the two will be able to convert
         * to a passer, provided the pawn on the open file has no attackers on its other
         * side.  Only consider pawns where the blocker is on its 3rd rank or less.  */
        else if (Rank(sq)<Rank4 && Mask[sq-8]&B->BlackPawns && !(FileUpMask[sq-8]&B->BlackPawns) &&
          ((x>0 && B->pieces[sq+7]==wpawn && !(FileUpMask[sq+7]&B->BlackPawns) &&
           (x==1 || !(FileUpMask[sq+6]&B->BlackPawns)) ||
          (x<7 && B->pieces[sq+9]==wpawn && !(FileUpMask[sq+9]&B->BlackPawns) &&
           (x==6 || !(FileUpMask[sq+10]&B->BlackPawns)))))) {
          HiddenMask |= Mask[sq];
        }
      }
      else {   
        if (LastWhitePawn[x]<sq &&
          (x==0 || LastWhitePawn[x-1]<sq) &&
          (x==7 || LastWhitePawn[x+1]<=(sq+1))) {
          PassedMask |= Mask[sq];
        }
          /* Check for disguised passed pawns, where only one of a pair of connected pawns
           * is blocked by a opposing pawn.  Clearly, one of the two will be able to convert
           * to a passer, provided the pawn on the open file has no attackers on its other
           * side.   Only consider pawns where the blocker is on its 3rd rank or less.  */
        else if (Rank(sq)>Rank5 && Mask[sq+8]&B->WhitePawns && !(FileDownMask[sq+8]&B->WhitePawns) &&
          ((x>0 && B->pieces[sq-9]==bpawn && !(FileDownMask[sq-9]&B->WhitePawns) &&
          (x==1 || !(FileDownMask[sq-10]&B->WhitePawns)) ||
          (x<7 && B->pieces[sq-7]==bpawn && !(FileDownMask[sq-7]&B->WhitePawns) &&
          (x==6 || !(FileDownMask[sq-6]&B->WhitePawns)))))) {
          HiddenMask |= Mask[sq];
        }
      }
    }
  }
 
  /* Encourage Trade Off if Ahead */
#ifdef ExchangeOff
  if (B->WPts != B->BPts) {
    /* Calculate half the sum of the scores of pieces captured so far this game,
     * and subtract it from the loser's score.  Hence the winner is encouraged to
     * trade off to lower total scores. */
    sdiff = (78 - (B->WPts + B->BPts)) >> 1;
    if (B->WPts > B->BPts) score += sdiff;
    else                   score -= sdiff;
#ifdef DEBUG_EVAL
    if (B->WPts > B->BPts) fprintf(stdout,"Trade Off score : %d\n",sdiff);
    else fprintf(stdout,"Trade Off score : %d\n",-sdiff);
#endif
  }
#endif
 
   /* Evaluate Passed Pawns */
  if (PassedMask|HiddenMask) score += EvalPassedPawns(B,dangerw,dangerb);
#ifdef DEBUG_EVAL
  oldscore = score;
#endif

  /* Extra minor pieces in the endgame really help.  Q or 2R vs.
   * 3 minors is usually bad.  Rook vs. 2 minors is bad.  Much of
   * this code is based on that of Crafty v18.12, by Prof. R. Hyatt.
   * I added in some clauses of my own.  In particular, I only
   * apply this bonus before the late endgame. */
#ifdef USE_EXTRA_MINOR
  if (B->Gamestage < LateEnd) {
    /* Count number of minors and majors for each side */
    if (wmin != bmin) {
      switch(abs(wmaj-bmaj)) {
      case 0:
        if (wmin > bmin) score += EXTRA_MINOR;
        else score -= EXTRA_MINOR;
        break;
      case 1:
        if (bmaj==wmaj+1 && wmin > bmin+1) score += EXTRA_MINOR;
        else if (wmaj==bmaj+1 && bmin > wmin+1) score -= EXTRA_MINOR;
        break;
      case 2:
        if (bmaj==wmaj+2 && wmin > bmin+2) score += EXTRA_MINOR;
        else if (wmaj==bmaj+2 && bmin > wmin+2) score -= EXTRA_MINOR;
        break;
      }
    }
#ifdef DEBUG_EVAL
    if (oldscore != score) fprintf(stdout,"Extra Minor (%d)\n",score-oldscore);
    oldscore = score;
#endif
  }
#endif 

  /* Test for an offside pawn majority for each side.  This is when the opposition
   * has a pawn majority on the opposite side of the board to your king.  We define
   * 'offside' as any file more than 2 distant from the king. This is penalised. */
  offside = Count(B->WhitePawns&OffsideMask[wkx]) - Count(B->BlackPawns&OffsideMask[wkx]);
  if (offside<0) {
    switch (dangerw) {
     case (2): score -= OffsidePenalty[-offside]; break;
     case (1): score -= OffsidePenalty2[-offside]; break;
     case (0): score -= OffsidePenalty3[-offside]; break;
    }
  }
  offside = Count(B->BlackPawns&OffsideMask[bkx]) - Count(B->WhitePawns&OffsideMask[bkx]);
  if (offside<0) {
    switch (dangerb) {
     case (2): score += OffsidePenalty[-offside]; break;
     case (1): score += OffsidePenalty2[-offside]; break;
     case (0): score += OffsidePenalty3[-offside]; break;
    }
  }
#ifdef DEBUG_EVAL
  if (score!=oldscore) fprintf(stdout,"Offside Pawns : %d\n",score-oldscore);
#endif

  /* If there is no mating material for one side then the score cannot favour that side.
   * Make sure we keep track of the score before truncation, in case we have to do a
   * full eval here. */
  prematscore = score;
  if (bmaj==0 && wmaj==0) CheckMatingMaterial(B);
  if (!wmat) score = min(0,score);
  if (!bmat) score = max(0,score);
#ifdef DEBUG_EVAL
  if (!wmat) fprintf(stdout,"No mating material for white\n");
  if (!bmat) fprintf(stdout,"No mating material for black\n");
#endif
  B->wmaj = wmaj;
  B->wmin = wmin;
  B->bmaj = bmaj;
  B->bmin = bmin;
#ifdef DRAWISH_ENDINGS
  /* Test for a 'drawish' ending, and reduce the score if so */
  if (Drawish(B)) score = (score * (50+B->WPts+B->BPts)) / 100;
#ifdef DEBUG_EVAL
  if (Drawish(B)) fprintf(stdout,"Drawish Position (score reduced)\n");
#endif
#endif

#ifdef DEBUG_EVAL
  fprintf(stdout,"\nTotal Lazy Eval : %d\n",score);
#endif
  return score;
}