pattern coding in bitboards

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

PK
Posts: 893
Joined: Mon Jan 15, 2007 11:23 am
Location: Warsza

pattern coding in bitboards

Post by PK »

As some of You may know, currently I am trying to implement some pawn chain knowledge in Rodent. Moreover, I was lucky enough to get some progress, confirmed both by a test match and by finding more correct moves in King's Indian structures.

The only thing that bugs me (pun intended!) is code. It is ugly. It is bloated. It basically looks like a copy-paste of a flow chart diagram - a decision tree that works right now, but may quickly become unmanageable. Below I show one section, two chains and 4 bonuses only. sd is side for which we evaluate, op is the other side, RelSqBb returns a bitboard of a single square relative for the side (i.e. D4,BLACK -> D5).

Code: Select all

if (RelSqBb(E4, sd) & bbPc(p, op, P)) {

   if (RelSqBb(D5, sd) & bbPc(p, op, P) // c6-d5-e4 triad
   && RelSqBb(C6, sd) & bbPc(p, op, P)) {
      if (RelSqBb(D4, sd) & bbPc(p, sd, P)
      && RelSqBb(E3, sd) & bbPc(p, sd, P))
         mgResult -= bigChainScore;
      else
         mgResult -= smallChainScore;
   }

   if (RelSqBb(F3, sd) & bbPc(p, op, P) // d5-e4-f3 triad
   && RelSqBb(D5, sd) & bbPc(p, op, P)) {
      if (RelSqBb(E3, sd) & bbPc(p, sd, P)) 
         mgResult -= bigChainScore;
      else
         mgResult -= smallChainScore;
   }
}
Now, does any of You know a better way of coding this kind of knowledge? Can it be represented in a better way (I thought about masks and PopCnt, but it cuts off only a couple of lines)? Can this pattern recognition be coded in a way acceptable for Stockfish developers?
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: pattern coding in bitboards

Post by mcostalba »

PK wrote:As some of You may know, currently I am trying to implement some pawn chain knowledge in Rodent. Moreover, I was lucky enough to get some progress, confirmed both by a test match and by finding more correct moves in King's Indian structures.

The only thing that bugs me (pun intended!) is code. It is ugly. It is bloated. It basically looks like a copy-paste of a flow chart diagram - a decision tree that works right now, but may quickly become unmanageable. Below I show one section, two chains and 4 bonuses only. sd is side for which we evaluate, op is the other side, RelSqBb returns a bitboard of a single square relative for the side (i.e. D4,BLACK -> D5).

Code: Select all

if (RelSqBb(E4, sd) & bbPc(p, op, P)) {

   if (RelSqBb(D5, sd) & bbPc(p, op, P) // c6-d5-e4 triad
   && RelSqBb(C6, sd) & bbPc(p, op, P)) {
      if (RelSqBb(D4, sd) & bbPc(p, sd, P)
      && RelSqBb(E3, sd) & bbPc(p, sd, P))
         mgResult -= bigChainScore;
      else
         mgResult -= smallChainScore;
   }

   if (RelSqBb(F3, sd) & bbPc(p, op, P) // d5-e4-f3 triad
   && RelSqBb(D5, sd) & bbPc(p, op, P)) {
      if (RelSqBb(E3, sd) & bbPc(p, sd, P)) 
         mgResult -= bigChainScore;
      else
         mgResult -= smallChainScore;
   }
}
Now, does any of You know a better way of coding this kind of knowledge? Can it be represented in a better way (I thought about masks and PopCnt, but it cuts off only a couple of lines)? Can this pattern recognition be coded in a way acceptable for Stockfish developers?
Well regarding Stockfish developers, I see 2 immediate improvments:

1) Naming is obscure, please use better and clear names that even people not deeply involved with the code can understand

2) Split this evaluation in 2: one for white pieces and another for black. This get rids of all the 'relative' stuff and is also faster. In SF we use templates in this case, so to not duplicate the source. But I suggest to use this approach also in C, at least to start removing cruft and better see possibilities for further improvment.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: pattern coding in bitboards

Post by mcostalba »

I have rewritten your code, using current SF functions.

It would be like this:

Code: Select all

    if ((theirPawns & SQ_C6) && (theirPawns & SQ_D5) && (theirPawns & SQ_E4))
        mgResult -= (ourPawns & SQ_D4) && (ourPawns & SQ_E3) ? bigChainScore : smallChainScore;

    if ((theirPawns & SQ_D5) && (theirPawns & SQ_E4) && (theirPawns & SQ_F3))
        mgResult -= (ourPawns & SQ_E3) ? bigChainScore : smallChainScore;
If you have a lot of this formulas, perhaps would make sense to add something more specific.
PK
Posts: 893
Joined: Mon Jan 15, 2007 11:23 am
Location: Warsza

Re: pattern coding in bitboards

Post by PK »

Thank You, this was helpful. BTW, if I propose a chain patch, what would be preconditions for it to be tested? Symmetry (i.e. adding similar queenside code)? Trying one chain at a time?
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: pattern coding in bitboards

Post by mcostalba »

mcostalba wrote: If you have a lot of this formulas, perhaps would make sense to add something more specific.
Indeed it is enough to define somewhere

Code: Select all

 const Bitboard BB = 0;
To be able to write (with current SF code):

Code: Select all

if (!(~theirPawns & (BB | SQ_C6 | SQ_D5 | SQ_E4)))
    mgResult -= (~ourPawns & (BB | SQ_D4 | SQ_E3)) ? smallChainScore : bigChainScore;

if (!(~theirPawns & (BB | SQ_D5 | SQ_E4 | SQ_F3)))
    mgResult -= (~ourPawns & SQ_E3) ? smallChainScore : bigChainScore;
User avatar
Evert
Posts: 2929
Joined: Sat Jan 22, 2011 12:42 am
Location: NL

Re: pattern coding in bitboards

Post by Evert »

I haven't committed any relevant code to disk yet, but I was throwing around some ideas for how to add terms like this in Jazz without producing a load of ad-hoc unmaintainable spaghetti.

The idea I came up with is to have a "database" of patterns, something like this:

Code: Select all

struct {
   bitboard_t wpawn, bpawn, wking, bking;
   int mg, eg;
} pawn_patterns[] = {
   { BB_D4 | BB_E3, BB_C6 | BB_D5 | BB_E4, 0, 0, -big_chain, 0 },
   { 0,             BB_C6 | BB_D5 | BB_E4, 0, 0, -small_chain, 0 },
};
In order for the pattern to match, wpawn and bpawn must match exactly, while the positions of the king must match wking and bking (which may be empty to indicate they are not relevant for this particular pattern). The database is defined once, for white's point of view, and then an equivalent database for black is constructed from it by flipping the board and scores when the evaluation is initialised.

The evaluation code then loops over the database and applies any bonus/penalties that match based on it:

Code: Select all

for (all pawn patterns) {
   if ((pattern.wpawn & wpawn) == pattern.wpawn &&
       (pattern.bpawn & bpawn) == bpawn &&
       (!pattern.bking || (pattern.bking & bking)) && 
       (!pattern.wking || (pattern.wking & wking))) {
      mg += pattern.mg;
      eg += pattern.eg;
   }
}
but so far this is all just an idea. I haven't experimented with it yet, and in particular I don't know yet if doing it this way will limit how versatile the pattern matching can be. I like the idea of just having one central pattern library that is automatically updated for white and black.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: pattern coding in bitboards

Post by mcostalba »

PK wrote:Thank You, this was helpful. BTW, if I propose a chain patch, what would be preconditions for it to be tested? Symmetry (i.e. adding similar queenside code)? Trying one chain at a time?
The _only_ precondition for your patch to be tested is that you submit the test yourself on fishtest :-)
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: pattern coding in bitboards

Post by mcostalba »

The previous solution, although very compact, it is probably a bit too tricky for average user, so in this case I'd prefer to write some helper function more but let the code more easily understandable at first read (all those negations do not easy the reading).


Code readibility is always more valuable than 'elegance' of the solution, so in this case my vote goes for the following:

Code: Select all

if (contains(theirPawns, SQ_C6, SQ_D5, SQ_E4))
    mgResult -= contains(ourPawns, SQ_D4, SQ_E3) ? bigChainScore : smallChainScore;

if (contains(theirPawns, SQ_D5, SQ_E4, SQ_F3))
    mgResult -= (ourPawns & SQ_E3) ? bigChainScore : smallChainScore;

Where

Code: Select all

inline bool contains(Bitboard b, Square s1, Square s2) { return (b & s1) && (b & s2); }
inline bool contains(Bitboard b, Square s1, Square s2, Square s3) { return contains(b, s1, s2) && (b & s3); }

User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: pattern coding in bitboards

Post by stegemma »

I would suggest you to separate conditions from the if itself:

Code: Select all


bool bE4Test = RelSqBb(E4, sd) & bbPc(p, op, P);
bool bD5Test = RelSqBb(D5, sd) & bbPc(p, op, P);
bool bC6Test = RelSqBb(C6, sd) & bbPc(p, op, P);
...

if (bE4Test) { 
   if ( bD5Test && bC6Test) { 
...
Using macros or functions to handle RelSqBb(...) & bbPc(...), as suggested by others, could help you more again. Using more clear names could help but the method that i've shown is the best that i can find, to let more clear the if structure.
Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: pattern coding in bitboards

Post by Gerd Isenberg »

Why not working set-wise for defended defenders in two disjoint directions to get the member (center) of at least triple chains?

Code: Select all

U64 noEaOne &#40;U64 b&#41; &#123;return &#40;b << 9&#41; & notAFile;&#125;
U64 soWeOne &#40;U64 b&#41; &#123;return &#40;b >> 9&#41; & notHFile;&#125;
U64 soEaOne &#40;U64 b&#41; &#123;return &#40;b >> 7&#41; & notAFile;&#125;
U64 noWeOne &#40;U64 b&#41; &#123;return &#40;b << 7&#41; & notHFile;&#125;

U64 defendedDefenders1 &#40;U64 b&#41; &#123;return b & soWeOne&#40;b&#41; & noEaOne&#40;b&#41;;&#125;
U64 defendedDefenders2 &#40;U64 b&#41; &#123;return b & soEaOne&#40;b&#41; & noWeOne&#40;b&#41;;&#125;
Now, if d5 is member of defendedDefenders2(pawnsOfOneColor), you know there is a pawn of the same color on e4 and c6.