evaluate loose pieces

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: evaluate loose pieces

Post by Gerd Isenberg »

lkaufman wrote:A bonus for attacks on loose pieces (or up-attacks) is quite standard. I introduced it in Rybka, it was duplicated in Ippolit, and of course is in Komodo. Since the Ippolit code is open-source, there is nothing secret about how this was done.
I was not aware by the term loose piece, and used "hanging piece" for undefended pieces (but not pawns) instead (cpw), even if not immediate exposed to capture. In English does the term "hanging piece" always imply it might be captured immediately? Or is it synonym to loose piece?

However, I understand loose pawns avoids the ambiguity with hanging pawns.
User avatar
Eelco de Groot
Posts: 4580
Joined: Sun Mar 12, 2006 2:40 am
Full name:   

Re: evaluate loose pieces

Post by Eelco de Groot »

mcostalba wrote:Here is the code in SF:
Hi Marco,

I think the change you propose is not really what Lucas means with "loose pieces" the usual term, I don't use it myself much though, is as Gerd says "hanging pieces" The definition on Wikipedia for hanging pieces, hanging pawns are another matter, from http://en.wikipedia.org/wiki/Glossary_of_chess
Hanging
Unprotected and exposed to capture. It is not the same as en prise since a piece en prise may be protected. To "hang a piece" is to lose it by failing to move or protect it
So if a pice is not defended by a pawn and there is already a bonus given for attacking it, the extra effort to see if there are non-pawn material defenders is not so great anymore, and in that case the piece really is "hanging". My own version of that code in Stockfish is a bit experimental and not very streamlined code (if the indents are wrong; I tried to fix them for about forty minutes, it probably has to do with original Stockfish code made in Unix and modified under Windows and some conflicting line endings still there):

Code: Select all


  template<Color Us>
  Score evaluate_threats&#40;const Position& pos, EvalInfo& ei&#41; &#123;

    const Color Them = &#40;Us == WHITE ? BLACK &#58; WHITE&#41;;

    Bitboard b;
    Score score = SCORE_ZERO;

    // Enemy pieces not defended by a pawn and under our attack
    Bitboard weakEnemies =  pos.pieces&#40;Them&#41;
                          & ~ei.attackedBy&#91;Them&#93;&#91;PAWN&#93;
                          & ei.attackedBy&#91;Us&#93;&#91;0&#93;;
    if (!weakEnemies&#41;
        return SCORE_ZERO;

    // Add bonus according to type of attacked enemy piece &#91;pt2&#93; and to the
    // type of attacking piece &#91;pt1&#93;, from knights to queens. Kings are not
    // considered under pt2 because that is already handled in king evaluation.
    for &#40;PieceType pt1 = KNIGHT; pt1 <= KING; pt1++)
    &#123;
        b = ei.attackedBy&#91;Us&#93;&#91;pt1&#93; & weakEnemies;
        if &#40;b&#41;
            for &#40;PieceType pt2 = PAWN; pt2 < KING; pt2++)
            &#123;
                Bitboard bb = b & pos.pieces&#40;pt2&#41;;
                if &#40;bb&#41;
                &#123;
                   score += ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
                   do &#123; // Is the piece undefended? 
                         Square s = pop_1st_bit&#40;&bb&#41;;
                         if (!&#40;ei.attackedBy&#91;Them&#93;&#91;0&#93; & s&#41;)
                         &#123;
                            score += ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
                            if &#40;pt2 == PAWN && (!pos.square_is_empty&#40;s + pawn_push&#40;Them&#41;) || &#40;ei.attackedBy&#91;Us&#93;&#91;0&#93; & s + pawn_push&#40;Them&#41;)))
                            &#123;
                               score += make_score&#40;5, 10&#41;;
                               // Nearing a pawn ending and the pawn may become a candidate pawn or a passed pawn?
                               File f = file_of&#40;s&#41;;
                               if &#40;ei.pi->file_is_half_open&#40;Us, f&#41; && !pos.non_pawn_material&#40;Them&#41;)
                                   score += make_score&#40;0, 2*relative_rank&#40;Them, s&#41;);
                            &#125;
                          &#125;
                          // Does the piece attack our king?
                          if (&#40;ei.kingAttackersBB&#91;Us&#93; & s&#41;)
                              score += make_score&#40;25, 0&#41;;
                   &#125; while &#40;bb&#41;;
                &#125;
             &#125;
    &#125;
    return score;
  &#125;
Some of the extra bonuses introduced are just small and may not make much of a difference.

Eelco
Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: evaluate loose pieces

Post by lucasart »

mcostalba wrote:Here is the code in SF:

Code: Select all

  // evaluate_threats<>() assigns bonuses according to the type of attacking piece
  // and the type of attacked one.

  template<Color Us>
  Score evaluate_threats&#40;const Position& pos, EvalInfo& ei&#41; &#123;

    const Color Them = &#40;Us == WHITE ? BLACK &#58; WHITE&#41;;

    Bitboard b;
    Score score = SCORE_ZERO;

    // Enemy pieces not defended by a pawn and under our attack
    Bitboard weakEnemies =  pos.pieces&#40;Them&#41;
                          & ~ei.attackedBy&#91;Them&#93;&#91;PAWN&#93;
                          & ei.attackedBy&#91;Us&#93;&#91;0&#93;;
    if (!weakEnemies&#41;
        return SCORE_ZERO;

    // Add bonus according to type of attacked enemy piece and to the
    // type of attacking piece, from knights to queens. Kings are not
    // considered because are already handled in king evaluation.
    for &#40;PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
    &#123;
        b = ei.attackedBy&#91;Us&#93;&#91;pt1&#93; & weakEnemies;
        if &#40;b&#41;
            for &#40;PieceType pt2 = PAWN; pt2 < KING; pt2++)
                if &#40;b & pos.pieces&#40;pt2&#41;)
                    score += ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
    &#125;
    return score;
  &#125;
If I have understood correctly the idea of Lucas, it could be implemented changing weakEnemies definition to:

Code: Select all


// Enemy pieces not defended by a pawn and under our attack
Bitboard weakEnemies =  pos.pieces&#40;Them&#41;
                      & ~ei.attackedBy&#91;Them&#93;&#91;PAWN&#93;
                      & ei.attackedBy&#91;Us&#93;&#91;0&#93;;

// Add enemy non-pawns attacked by our pawns, even if defended by a pawn
weakEnemies |= &#40;pos.pieces&#40;Them&#41; ^ pos.pieces&#40;PAWN, Them&#41;) & ei.attackedBy&#91;Us&#93;&#91;PAWN&#93;;

But it seems to be redundant with this piece of code in evaluate_piecs() that already does the same thing.

Code: Select all

        // Decrease score if we are attacked by an enemy pawn. Remaining part
        // of threat evaluation must be done later when we have full attack info.
        if &#40;ei.attackedBy&#91;Them&#93;&#91;PAWN&#93; & s&#41;
            score -= ThreatenedByPawnPenalty&#91;Piece&#93;;
I wasn't advocating to make any changes to SF. Just curious to know what refinments I could bring to my implementation. And indeed I use almost the same definition as SF for loose pieces. The scoring however is where SF is more clever.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: evaluate loose pieces

Post by lucasart »

BubbaTough wrote:
lkaufman wrote:I think you are right. I have a vague recollection that it was in some of the engines of around 1990, perhaps in RexChess and/or in various Fidelity machines and others of that time. But perhaps it was just for pieces attacked by the last-moved piece, I don't remember this detail.
Yes, and I did it in my first engine Pawniac, I think that was either 1989 or 1990. Basically, like many things, the idea that "attacking loose pieces is good" is a pretty intuitive concept, and it seems silly to try to identify an origination of it, though the specific conditions and weights are always interesting.

-Sam
And I'm sure Bob did it in the 70s, in the first assembly versions of Cray Blitz ;)
BubbaTough
Posts: 1154
Joined: Fri Jun 23, 2006 5:18 am

Re: evaluate loose pieces

Post by BubbaTough »

lucasart wrote:
BubbaTough wrote:
lkaufman wrote:I think you are right. I have a vague recollection that it was in some of the engines of around 1990, perhaps in RexChess and/or in various Fidelity machines and others of that time. But perhaps it was just for pieces attacked by the last-moved piece, I don't remember this detail.
Yes, and I did it in my first engine Pawniac, I think that was either 1989 or 1990. Basically, like many things, the idea that "attacking loose pieces is good" is a pretty intuitive concept, and it seems silly to try to identify an origination of it, though the specific conditions and weights are always interesting.

-Sam
And I'm sure Bob did it in the 70s, in the first assembly versions of Cray Blitz ;)
Exactly :).

-Sam
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: evaluate loose pieces

Post by mcostalba »

Eelco de Groot wrote: I think the change you propose is not really what Lucas means with "loose pieces" the usual term,
I have looked at his code, not how it is called.
Eelco de Groot wrote: My own version of that code in Stockfish is a bit experimental and not very streamlined code
Your code is too complicated to work ;-)

You are attacking 3 concepts (loose pieces, passed pawns and king attacks) in a single piece of code: it doesn't work like this. I'd suggest a more orthogonal approach, one different function for each concept. A possible implementation of your idea that seems an acceptable compromise between speed and accuracy could be:

Code: Select all

  template<Color Us>
  Score evaluate_threats&#40;const Position& pos, EvalInfo& ei&#41; &#123;

    const Color Them = &#40;Us == WHITE ? BLACK &#58; WHITE&#41;;

    Bitboard b, bb;
    Score score = SCORE_ZERO;

    // Enemy pieces not defended by a pawn and under our attack
    Bitboard weakEnemies =  pos.pieces&#40;Them&#41;
                          & ~ei.attackedBy&#91;Them&#93;&#91;PAWN&#93;
                          & ei.attackedBy&#91;Us&#93;&#91;0&#93;;
    if (!weakEnemies&#41;
        return SCORE_ZERO;

    Bitboard undefended = weakEnemies & ~ei.attackedBy&#91;Them&#93;&#91;0&#93;;

    // Add bonus according to type of attacked enemy piece and to the
    // type of attacking piece, from knights to queens. Kings are not
    // considered because are already handled in king evaluation.
    for &#40;PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
    &#123;
        b = ei.attackedBy&#91;Us&#93;&#91;pt1&#93; & weakEnemies;
        if &#40;b&#41;
            for &#40;PieceType pt2 = PAWN; pt2 < KING; pt2++)
                if &#40;bb = b & pos.pieces&#40;pt2&#41;, bb&#41;
                    score += bb & undefended ? 2 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;
                                             &#58; 1 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
    &#125;
    return score;
  &#125;
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: evaluate loose pieces

Post by lucasart »

mcostalba wrote: A possible implementation of your idea that seems an acceptable compromise between speed and accuracy could be:

Code: Select all

...
                    score += bb & undefended ? 2 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;
                                             &#58; 1 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
...
seems like a reasonable compromise indeed. i'll go ahead and try this in DoubleCheck.
BubbaTough
Posts: 1154
Joined: Fri Jun 23, 2006 5:18 am

Re: evaluate loose pieces

Post by BubbaTough »

lucasart wrote:
mcostalba wrote: A possible implementation of your idea that seems an acceptable compromise between speed and accuracy could be:

Code: Select all

...
                    score += bb & undefended ? 2 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;
                                             &#58; 1 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
...
seems like a reasonable compromise indeed. i'll go ahead and try this in DoubleCheck.
I would adjust this a bit...I doubt when a bishop attacks a queen it is half as valuable when the queen is guarded.

-Sam
User avatar
Rebel
Posts: 7026
Joined: Thu Aug 18, 2011 12:04 pm
Full name: Ed Schröder

Re: evaluate loose pieces

Post by Rebel »

michiguel wrote:
lkaufman wrote:A bonus for attacks on loose pieces (or up-attacks) is quite standard. I introduced it in Rybka, it was duplicated in Ippolit, and of course is in Komodo. Since the Ippolit code is open-source, there is nothing secret about how this was done.
And it has been in Gaviota for at least 10 years, which makes me think that it must be in many other engines, long before all those.

Miguel
EDIT: Should not this be widespread?
Since day one.

http://www.top-5000.nl/authors/rebel/ch ... htm#ATTACK

Last time I checked (a couple of months ago) even at nowadays deep depths still good for 30+ elo. Amazing because one would expect a diminishing return effect.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: evaluate loose pieces

Post by lucasart »

mcostalba wrote:
Eelco de Groot wrote: I think the change you propose is not really what Lucas means with "loose pieces" the usual term,
I have looked at his code, not how it is called.
Eelco de Groot wrote: My own version of that code in Stockfish is a bit experimental and not very streamlined code
Your code is too complicated to work ;-)

You are attacking 3 concepts (loose pieces, passed pawns and king attacks) in a single piece of code: it doesn't work like this. I'd suggest a more orthogonal approach, one different function for each concept. A possible implementation of your idea that seems an acceptable compromise between speed and accuracy could be:

Code: Select all

  template<Color Us>
  Score evaluate_threats&#40;const Position& pos, EvalInfo& ei&#41; &#123;

    const Color Them = &#40;Us == WHITE ? BLACK &#58; WHITE&#41;;

    Bitboard b, bb;
    Score score = SCORE_ZERO;

    // Enemy pieces not defended by a pawn and under our attack
    Bitboard weakEnemies =  pos.pieces&#40;Them&#41;
                          & ~ei.attackedBy&#91;Them&#93;&#91;PAWN&#93;
                          & ei.attackedBy&#91;Us&#93;&#91;0&#93;;
    if (!weakEnemies&#41;
        return SCORE_ZERO;

    Bitboard undefended = weakEnemies & ~ei.attackedBy&#91;Them&#93;&#91;0&#93;;

    // Add bonus according to type of attacked enemy piece and to the
    // type of attacking piece, from knights to queens. Kings are not
    // considered because are already handled in king evaluation.
    for &#40;PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
    &#123;
        b = ei.attackedBy&#91;Us&#93;&#91;pt1&#93; & weakEnemies;
        if &#40;b&#41;
            for &#40;PieceType pt2 = PAWN; pt2 < KING; pt2++)
                if &#40;bb = b & pos.pieces&#40;pt2&#41;, bb&#41;
                    score += bb & undefended ? 2 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;
                                             &#58; 1 * ThreatBonus&#91;pt1&#93;&#91;pt2&#93;;
    &#125;
    return score;
  &#125;
I think I've found another compromise that (in my testing) performs better. The idea is to make the bonus non linear, in the same logic as king safety. After all the reason why these bonus for attacking hanging pieces are small, are because the hanging pieces can be defended and it's hard to know whether we'll end up losing a piece or not. But one thing is sure, the more undefended hanging pieces, the more probability we won't be able to defend all of them.

Code: Select all

static void eval_loose&#40;const Board *B, eval_t *e&#41;
// detect loose pieces and attribute small bonuses for attacking them
&#123;
	assert&#40;B->initialized && e&#41;;

	for &#40;unsigned color = White; color <= Black; color++) &#123;
		const unsigned us = color, them = opp_color&#40;us&#41;;
		
		// loose pawns = undefended pawns
		const uint64_t loose_pawns = B->b&#91;them&#93;&#91;Pawn&#93; & ~B->st->attacks&#91;them&#93;&#91;NoPiece&#93;;
		
		// loose pieces = pieces either attacked by a pawn or not defended by a pawn
		const uint64_t enemy_pieces = B->all&#91;them&#93; & ~B->b&#91;them&#93;&#91;Pawn&#93;;
		const uint64_t loose_pieces = enemy_pieces & (~B->st->attacks&#91;them&#93;&#91;Pawn&#93; | B->st->attacks&#91;us&#93;&#91;Pawn&#93;);
		
		// hanging = loose and attacked
		uint64_t hanging = &#40;loose_pawns | loose_pieces&#41; & B->st->attacks&#91;us&#93;&#91;NoPiece&#93;;
		
		// scoring &#40;non linear&#41;
		unsigned weighted_bonus&#91;NB_PHASE&#93; = &#123;0, 0&#125;, risk = 1;
		while &#40;hanging&#41; &#123;
			const unsigned sq = next_bit&#40;&hanging&#41;, victim = B->piece_on&#91;sq&#93;;
			weighted_bonus&#91;Opening&#93; += 5 + Material&#91;Opening&#93;&#91;victim&#93;/32;
			weighted_bonus&#91;EndGame&#93; += 10 + Material&#91;EndGame&#93;&#91;victim&#93;/32;
			if (!test_bit&#40;B->st->attacks&#91;them&#93;&#91;NoPiece&#93;, sq&#41;)
				risk++;
		&#125;
		e->op&#91;us&#93; += weighted_bonus&#91;Opening&#93; * risk;
		e->eg&#91;us&#93; += weighted_bonus&#91;EndGame&#93; * risk;
	&#125;
&#125;
You might want to try something like this in SF, although you'll need to modify it somehow as SF separates clearly the case of pawn attacks from the rest (I mix it all together here)