Best bitboard design?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Best bitboard design?

Post by hgm »

Kotlov wrote: Fri May 14, 2021 1:49 pm
hgm wrote: Fri May 14, 2021 1:34 pm You mean the u->searched++; and the nodeCnt++;? The latter is the (global) total node count for calculatin the nps. u->searched is local to the node, and is used at the end of the node to determine whether that node was a leaf or not. For the purpose of counting the number of leaf nodes.
No, I mean only nodeCnt.

Code: Select all

int Search(UndoInfo *u) // pass all parameters in a struct
{
  UndoInfo undo;
  int pvMove;
  int curMove, noncapts = 10000;
  undo.pv = pvPtr;
  undo.first = msp;
  nodeCnt++;

 ......................


if(PATH) printf("       new=%08x existing=%08x worse=%08x\n       undetected=%08x todo=%08x\n", u->newThreats, u->existingThreats, worseThreats, u->undetectedThreats, todo);
    if(!todo && gap > 0) {                             // opponent has no non-futile move
      u-> parentAlpha = -u->alpha;                     // fail-high score (-u->alpha is parent's beta)
      attackers -= SZ; presence[stm] = u->oldPresence; // undo updates
      u->searched++; nodeCnt++; qsCnt += (u->depth <= 0);
      return 1;                                        // and abort move ('overkill pruning')
   
............................

}
two increment in one node.
OK, I see. This is not twice, it either uses one or the other.

At some point I split the code in Search() over two routines, introcin a routine SearchCapture(), which also incorporated the tasks of MakeMove() and UnMake(). So Search() no longer calls itself recursively (for captures), but the calling graph is Search -> SearchCapture -> Search. The combination (SearchCapture, Search) thus does what in a more conventional design would be done by Search(), and this is what I count as a node. (To be able to compare with conventional desins.) But SearchCapture() doesn't always call Search(); when no (non-futile) captures can be generated it returns before the move sorting and the move loop (which are in Search()).

But I have to increment nodeCnt in Search() to also count the invocations through non-captures. So there needs to be a conditional increment in SearchCapture(), which only increments nodeCnt when it doesn't continue callin Search().
User avatar
Kotlov
Posts: 266
Joined: Fri Jul 10, 2015 9:23 pm
Location: Russia

Re: Best bitboard design?

Post by Kotlov »

hgm wrote: Fri May 14, 2021 2:31 pm
Kotlov wrote: Fri May 14, 2021 1:49 pm
hgm wrote: Fri May 14, 2021 1:34 pm You mean the u->searched++; and the nodeCnt++;? The latter is the (global) total node count for calculatin the nps. u->searched is local to the node, and is used at the end of the node to determine whether that node was a leaf or not. For the purpose of counting the number of leaf nodes.
No, I mean only nodeCnt.

Code: Select all

int Search(UndoInfo *u) // pass all parameters in a struct
{
  UndoInfo undo;
  int pvMove;
  int curMove, noncapts = 10000;
  undo.pv = pvPtr;
  undo.first = msp;
  nodeCnt++;

 ......................


if(PATH) printf("       new=%08x existing=%08x worse=%08x\n       undetected=%08x todo=%08x\n", u->newThreats, u->existingThreats, worseThreats, u->undetectedThreats, todo);
    if(!todo && gap > 0) {                             // opponent has no non-futile move
      u-> parentAlpha = -u->alpha;                     // fail-high score (-u->alpha is parent's beta)
      attackers -= SZ; presence[stm] = u->oldPresence; // undo updates
      u->searched++; nodeCnt++; qsCnt += (u->depth <= 0);
      return 1;                                        // and abort move ('overkill pruning')
   
............................

}
two increment in one node.
OK, I see. This is not twice, it either uses one or the other.

At some point I split the code in Search() over two routines, introcin a routine SearchCapture(), which also incorporated the tasks of MakeMove() and UnMake(). So Search() no longer calls itself recursively (for captures), but the calling graph is Search -> SearchCapture -> Search. The combination (SearchCapture, Search) thus does what in a more conventional design would be done by Search(), and this is what I count as a node. (To be able to compare with conventional desins.) But SearchCapture() doesn't always call Search(); when no (non-futile) captures can be generated it returns before the move sorting and the move loop (which are in Search()).

But I have to increment nodeCnt in Search() to also count the invocations through non-captures. So there needs to be a conditional increment in SearchCapture(), which only increments nodeCnt when it doesn't continue callin Search().
It's kind of SEE or not?
Eugene Kotlov
Hedgehog 2.1 64-bit coming soon...
User avatar
Steve Maughan
Posts: 1221
Joined: Wed Mar 08, 2006 8:28 pm
Location: Florida, USA

Re: Best bitboard design?

Post by Steve Maughan »

Hi Martin — welcome back (it's tough to truly kick this addiction :D ). I'm looking forward to the new version of Colossus, or maybe "White Knight"? I can still remember playing White Knight on a BBC computer while still at school.

Steve
http://www.chessprogramming.net - Maverick Chess Engine
User avatar
MartinBryant
Posts: 69
Joined: Thu Nov 21, 2013 12:37 am
Location: Manchester, UK
Full name: Martin Bryant

Re: Best bitboard design?

Post by MartinBryant »

Thanks Steve :)
It is indeed an addiction! I remember talking to Jonathan Schaeffer back in the Chinook days and likening it to a disease that goes into remission before flaring up again... I called it "engine fever"! :D
I'd better not have another eleven years off though as I might not have that many years left! :shock: :wink:
User avatar
Kotlov
Posts: 266
Joined: Fri Jul 10, 2015 9:23 pm
Location: Russia

Re: Best bitboard design?

Post by Kotlov »

hgm wrote: Fri May 14, 2021 1:34 pm I am not sure where the 5.9Mnps was achieved. The latest state was 8.0Mnps, on a 2.2GHz i3 CPU. Also not sure if I uploaded the exact source of that version; I do not consider this final, but I had to take a break from the project to tend to other matters.
r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1
depth 12

http://hgm.nubati.net/mailbox7.c

Code: Select all

 1  -16       484 0 e2a6 b4c3 b2c3 e6d5
 2  -16      1786 0 e2a6 b4c3 b2c3 e6d5
 3  -27      4343 0 e2a6 e6d5 g2h3 b4c3 d2c3 d5e4
 4  -27     56079 0 e2a6 e6d5 g2h3 b4c3 d2c3 d5e4
 5  -37    133894 0 e2a6 e6d5 c3d5 b6d5 e4d5 e7e5
 6  -32    373764 0 e2a6 e6d5 c3d5 b6d5 a6b7 a8b8 b7d5 f6d5 e5f7 e7f7 e4d5 g7b2 f3f7 e8f7
 7  -32    842679 0 e2a6 e6d5 c3d5 b6d5 a6b7 a8b8 b7d5 f6d5 e5f7 e7f7 e4d5 g7b2 f3f7 e8f7
 8  -32   4204874 0 e2a6 e6d5 c3d5 b6d5 a6b7 a8b8 b7d5 f6d5 e5f7 e7f7 e4d5 g7b2 f3f7 e8f7
 9  -96  11003938 0 d5e6 e7e6 e2a6 e6e5 g2h3 b4c3 d2c3 e5h5 f3f4
10  -82  36080442 0 e2a6 e6d5 e5g6 f7g6 c3b5 d5e4 f3g3 e8f8 g2h3 c7c5 g3g6 h8h3
11  -89  89503028 0 e2a6 e6d5 c3d5 b6d5 a6b7 a8b8 e5f7 b8b7 f7h8 g7h8 c2c4
12  -67 370787572 0 e2a6 e6d5 c3d5 b6d5 a6b7 a8b8 e5f7 b8b7 f7h8 g7h8 c2c4 d5b6 g2h3 b6c4
t = 36.511 sec
 370787572 nodes (10.2 Mnps)
 329076072 QS (88.8%)
  49885859 evals (15.2%)
  18017058 stand-pats (5.5%)
  25287363 leaves (7.7%)
   1711277 move gens
         0 map gens
0.17 pins/move
captures: 85.3%
Hedgehog 2.105 64-bit (dev)

Code: Select all

go depth 12
info depth 1 score cp 39 time 17 nodes 53776 nps 3163294 pv d5e6
info depth 2 score cp 39 time 17 nodes 83900 nps 4935294 pv d5e6 e7e6
info depth 3 score cp -7 time 32 nodes 116662 nps 3645687 pv d5e6 e7e6 e2a6
info depth 4 score cp -7 time 32 nodes 135999 nps 4249968 pv d5e6 e7e6 e2a6 e6e5
info depth 5 score cp 5 time 48 nodes 166744 nps 3473833 pv d5e6 e7e6 e2a6 e6e5 d2f4
info depth 6 score cp 1 time 64 nodes 242743 nps 3792859 pv d5e6 e7e6 e2a6 e6e5 d2f4 e5e7
info depth 7 score cp -28 time 173 nodes 792097 nps 4578595 pv e2a6 b4c3 d2c3 e6d5 e5g4 h3g2 f3g2
info depth 8 score cp -41 time 298 nodes 1397203 nps 4688600 pv e2a6 b4c3 d2c3 e6d5 e5g4 h3g2 f3g2 b6c4
info depth 9 score cp -53 time 798 nodes 4453518 nps 5580849 pv e2a6 e6d5 c3b5 e7e5 d2f4 e5b2 b5c7 e8f8 e1g1 d5e4
info depth 10 score cp -87 time 1783 nodes 10569666 nps 5928023 pv e2a6 e6d5 c3b5 e7e5 d2f4 e5b2 b5c7 e8f8 e1g1 d5e4 f4d6
info depth 11 score cp -76 time 4849 nodes 29568062 nps 6097764 pv e2a6 e6d5 c3d5 f6d5 e1f1 e7e5 e4d5 e8g8 a1e1 e5d4 e1d1
info depth 12 score cp -54 time 9934 nodes 60620339 nps 6102309 pv e2a6 e6d5 c3d5 f6d5 e5d3 f7f5 e4e5 g7e5 e1c1 h3g2 f3g2 h8g8
bestmove e2a6

Hardware: AMD Ryzen 3 3200U with Radeon Vega Mobile Gfx with 5,9 GB Memory
Eugene Kotlov
Hedgehog 2.1 64-bit coming soon...
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Best bitboard design?

Post by hgm »

So mailbox was 1.66 times faster (10.2 Mnps vs 6.1Mnps)?

How can you have such a large number of nodes at 1 ply? You have more than 100 times as many as the reference search!

I don't quite understand your question about SEE. There is no SEE in the reference search. Just MVV/LVA ordering. The node starts by generating captures, and determining the MVV-wise best. If that doesn't stand any chance to get above alpha, it is done. This happens in SearchCapture(). If there is a capture to search, it goes on to call Search().
User avatar
Kotlov
Posts: 266
Joined: Fri Jul 10, 2015 9:23 pm
Location: Russia

Re: Best bitboard design?

Post by Kotlov »

hgm wrote: Fri May 14, 2021 7:29 pm So mailbox was 1.66 times faster (10.2 Mnps vs 6.1Mnps)?
I think that if remove all inhibiting functions from my code, except for the brute search, it might become faster (but it would become weaker)
Eugene Kotlov
Hedgehog 2.1 64-bit coming soon...
User avatar
hgm
Posts: 27808
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Best bitboard design?

Post by hgm »

Sure, that makes it difficult to compare different programs. It could also depend on how exactly you count nodes, how you handle futility pruning, perhaps even which margins you use, etc. This is why I made a reference search, with a clearly specified task.