evaluate loose pieces

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

evaluate loose pieces

Post by lucasart »

One of the many eval components that was missing in my program. After several attempts, I eventually implemented this in the following simplistic way

Code: Select all

static void eval_loose(const Board *B, eval_t *e)
// detect loose pieces and attribute small bonuses for attacking them
{
	assert(B->initialized && e);

	for &#40;unsigned color = White; color <= Black; color++) &#123;
		const unsigned us = color, them = opp_color&#40;us&#41;;
		
		// loose pawns = undefended pawns
		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
		uint64_t enemy_pieces = B->all&#91;them&#93; & ~B->b&#91;them&#93;&#91;Pawn&#93;;
		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;a bit simplistic at the moment, at least it's fast&#41;
		while &#40;hanging&#41; &#123;
			unsigned sq = next_bit&#40;&hanging&#41;, victim = B->piece_on&#91;sq&#93;;
			e->op&#91;us&#93; += 5 + Material&#91;Opening&#93;&#91;victim&#93;/32;
			e->eg&#91;us&#93; += 10 + Material&#91;EndGame&#93;&#91;victim&#93;/32;
		&#125;
	&#125;
&#125;
Just to clarify the code a little, B->st->attacks[color][piece] is a bitboard of squares attacked by (color,piece). For example B->st->attacks[Black][Knight] is the set of squares attacked by black knights. And B->st->attacks[color][NoPiece] is the set of squares attacked by any piece of color. The rest should hopefully be self-obvious.

So what I'm doing is basically:
1/ loose pawn = pawn that is not defended
2/ loose piece = piece that is not defended by a pawn, or piece that is attacked by a pawn (regardless of whether it's defended or not).
3/ hanging = loose + attacked

And the scoring function is very simplistic. There is a base score and a linear part proportional to the hanging piece (victim) value.

This approach seems rather stupid when I think about it, but in self-testing it scored almost +48 elo (1000 games) to my surprise. I suppose this kind of feature will never give a huge elo increase anyway, so a compromise has to be found between slowing down the eval and doing something intelligent but complex, and having an over simplistic fast code.

I wonder how others have done this, what they've tried, and with what kind of results.

Intuitively, my thought of hanging pieces is the following:
1/ the quiescent search will grab hanging pieces if it can. but it will choose a stand pat score when the side to move has pieces hanging. so if your opponent has a hanging piece and the quiescent search doesn't chose to take it, there's generally a good reason for it (losing SEE, checks involved, or other complications such as discovering new attacks etc.). However, even if the opponent hanging piece is SEE defended, or defended by other considerations (discovering other attacks etc.) it also means that in a way some opponent pieces are not free of movement and are tied to maintain these defensive tactics
2/ when you have only one hanging piece, it shouldn't be penalized too strongly, and the penalty should typically be the cost of a tempo (unless the piece is actually trapped and a tempo won't save it, but this would be hard to code in the eval in an efficient way).
3/ when you have several hanging pieces, there is no 100% rule nor even a 90% rule, but there are some significant chances that you'll end up losing one. Of course tactical complications such as forks, checks, passed pawn pushes can allow you to leave hanging pieces, so it's hard to know in the eval whether you're going to lose a piece or not.
4/ when you have many hanging pieces, the chances of being able to defend all of them by means of tactical threats becomes smaller and smaller. Perhaps I should start increasing the hanging piece penalty with the number of hanging pieces ?

Anyway, thoughts, suggestions welcome
lkaufman
Posts: 5960
Joined: Sun Jan 10, 2010 6:15 am
Location: Maryland USA

Re: evaluate loose pieces

Post by lkaufman »

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.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: evaluate loose pieces

Post by lucasart »

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.
Yes, of course I can have a look at IvanHoe's source code, and many other source codes. But I want to build my own stuff from my own understanding rather than copy... Anyway, I'll have a look at how IvanHoe does it and Stockfish too, just to see if there are some ideas (not code) I can use there.
User avatar
michiguel
Posts: 6401
Joined: Thu Mar 09, 2006 8:30 pm
Location: Chicago, Illinois, USA

Re: evaluate loose pieces

Post by michiguel »

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?
lkaufman
Posts: 5960
Joined: Sun Jan 10, 2010 6:15 am
Location: Maryland USA

Re: evaluate loose pieces

Post by lkaufman »

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.
User avatar
Eelco de Groot
Posts: 4567
Joined: Sun Mar 12, 2006 2:40 am
Full name:   

Re: evaluate loose pieces

Post by Eelco de Groot »

lucasart 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.
Yes, of course I can have a look at IvanHoe's source code, and many other source codes. But I want to build my own stuff from my own understanding rather than copy... Anyway, I'll have a look at how IvanHoe does it and Stockfish too, just to see if there are some ideas (not code) I can use there.
The surprising thing about Stockfish way of implementing is that at least I could not find code that takes into account undefended ("loose") pieces in general for evaluating attacks from the opponent, only a defence by one's own pawns. I think I introduced including defences by other pieces (which is all already existing bitboardcode in Stockfish, but it just was not used for evaluating attacks) to Rainbow Serpent and other clones of Stockfish then, just like Larry did in Rybka :) but it is not much tested. Maybe it is only not in Stockfish because it did not bring much elo, I don't know.

Evaluating attacked pieces in general was not in Fruit or Toga, which being non bitboard programs is not such a surprise. But also not in Glaurung after that moved to bitboards and I also can't remember hearing Bob Hyatt talk about this so it probably is not yet in Crafty? So from that point of view it is also maybe not such a surprise Vas had not heard of it yet, and the introduction in Stockfish by the Stockfish team came indeed after the code from Ippolit went public. That is the other side of the medal, if I just want to talk about Stockfish attack code.

I have experimented also, in one Ancalagon I think it was, with increased bonuses if the attacked piece does not have the move (sente/gote code) but I was not enthusiastic in the end about what probably amounts to search instabilities caused by this. Also have experimented with more than just additive bonuses in Stockfish if more than just one piece is attacked, like you are suggesting in your other post Lucas, but the way it is implemented in Stockfish makes it a bit hard (or more expensive) to count attacks for instance, it does not count separate attacks from a type of piece on another type of piece, just registers if the second type is attacked, not how many times it is attacked. This code I also abandoned, it was also too messy I think. Also experiments with X-Ray attacks are possible, to include pins but in a 32 bit environment the needed popcounts are a bit expensive. The X-ray/pin detection code is still lying around somewhere in old archived code but not in use in Rainbow Serpent now.

Regards, 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
BubbaTough
Posts: 1154
Joined: Fri Jun 23, 2006 5:18 am

Re: evaluate loose pieces

Post by BubbaTough »

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
User avatar
Desperado
Posts: 879
Joined: Mon Dec 15, 2008 11:45 am

Re: evaluate loose pieces

Post by Desperado »

Hello everyone,

just to give my 2 cents on that topic, i want to give an idea i used
a while back (and maybe i do it in future again).

_Similar_ to king safty approach (like fruit uses), hanging pieces
can be counted and weighted. This works pretty fast because
mobility(attacks) have to be simply masked to get attackers on that square,
so the attackcounter is incremented and a weight counter will
be summed up too. Finally you can compute from these 2 values a
score, just like many of you do with king safty, the same technique.

So the overhead is minimal, it is fine to tune and easy to implement.
The tradeoff between accuracy and speed is of course a matter of
taste, but it is just another way to handle hanging pieces, and _can_
be used as good evaluation heuristic.

maybe someone wants to try it out for himself :-)

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

Re: evaluate loose pieces

Post by mcostalba »

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;;
BubbaTough
Posts: 1154
Joined: Fri Jun 23, 2006 5:18 am

Re: evaluate loose pieces

Post by BubbaTough »

Eelco de Groot wrote:
The surprising thing about Stockfish way of implementing is that at least I could not find code that takes into account undefended ("loose") pieces in general for evaluating attacks from the opponent, only a defence by one's own pawns. I think I introduced including defences by other pieces (which is all already existing bitboardcode in Stockfish, but it just was not used for evaluating attacks) to Rainbow Serpent and other clones of Stockfish then, just like Larry did in Rybka :) but it is not much tested. Maybe it is only not in Stockfish because it did not bring much elo, I don't know.

...

I have experimented also, in one Ancalagon I think it was, with increased bonuses if the attacked piece does not have the move (sente/gote code) but I was not enthusiastic in the end about what probably amounts to search instabilities caused by this. Also have experimented with more than just additive bonuses in Stockfish if more than just one piece is attacked, like you are suggesting in your other post Lucas, but the way it is implemented in Stockfish makes it a bit hard (or more expensive) to count attacks for instance, it does not count separate attacks from a type of piece on another type of piece, just registers if the second type is attacked, not how many times it is attacked. This code I also abandoned, it was also too messy I think. Also experiments with X-Ray attacks are possible, to include pins but in a 32 bit environment the needed popcounts are a bit expensive. The X-ray/pin detection code is still lying around somewhere in old archived code but not in use in Rainbow Serpent now.

Regards, Eelco
Yes, the Stockfish code is not intuitive to me. Edsel had something vaguely similar in Hannibal, and I did a lot of the same experiments you did without finding a good improvement. Basically my conclusion was that Stockfish has a good marriage between speed of calculation, and help to the search (in my opinion these bonuses in eval have a primary contribution of molding the search space). It was a good learning experience for me as it helped me improve some other weird eval functions I had been playing with using a similar "inexact but not too slow" style.

-Sam