Mailboxes to bitboards transition

Discussion of chess software programming and technical issues.

Moderator: Ras

federico
Posts: 32
Joined: Sun Oct 22, 2017 4:36 am
Location: Canada
Full name: Federico Rojo

Re: Mailboxes to bitboards transition

Post by federico »

emadsen wrote: Sun Dec 26, 2021 6:01 pm
Chessnut1071 wrote: Fri Dec 24, 2021 3:49 am I have a very simple bitboard, a precomputed move vector (8) for each of the 64 squares. I just use the intrinsics TrailingZeros and LeadigZeros after I XOR out the last move to find the next move. I stop at a blocker.

btw, that 13.8 million was in debug mode. Also, I capture the following data for each move:
1 check 2 double check 3 discovered check 4 pin 5 ent passant 6 pawn promotion 7 move direction 8 capture 9 defender.
It's difficult for me to understand your move generation logic without seeing your code. In previous discussions- if I recall correctly- I and other developers have asked how your code accounts for every permutation of occupancy along a move direction ray. I don't recall seeing that code. Though "I stop at a blocker" suggests your move generator depends on a loop and does some bookkeeping to track blockers in each direction. Sharing your code would clarify this.
Chessnut1071 wrote: Sat Dec 25, 2021 8:48 pm ??? supect ???

10,000,000 engine call x AVG 59 pseudo moves = 590,000,000 (nodes) / 21.57 seconds ( release mode ) = 27.352,804 nodes per second

Actually, from what I'm hearing, my engine is super SLOW! Are you judging from legal moves or pseudo moves?
"Engine calls" is not a phrase anyone on this forum is familiar with. It does not have a clear, unambiguous definition chess engine programmers agree upon. Also unclear is the significance of 59 average pseudo-legal moves. How did you measure that number? Just because the root position has 59 pseudo-legal moves does not mean, on average, every position in the search tree has 59 pseudo-legal moves. Likely less.

The standard metric used by TalkChess forum members is Nodes Per Second (NPS), measured by incrementing the node count whenever Board.PlayMove(move) is called. Correct me if I'm wrong (forum members), but I believe that's the metric most engine developers use. It measures the rate at which an engine examines legally reachable (well, other than via null move) positions.

Also, it's helpful to state whether you've measured NPS during perft (pure move generation) or during search (usually alpha / beta minimax + depth reductions + pruning).
I don't know if there's a standard, but if there is would really like to know. You mentioned that nodes are counted whenever the make move function is called. I presume it excludes null moves for instance. Are there any other exceptions? How about illegal moves? My engine generates pseudo legal and to know if legal, i try the move. Is there a link to a talkchess thread or cpw page that discusses this topic?

In my case, i count nodes as every call to the recursive AB function and Qsearch functions, excluding leaf nodes, repetitions, 50 rule and TT hits.
User avatar
hgm
Posts: 28353
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Mailboxes to bitboards transition

Post by hgm »

Normally the reported nps refers to number of calls to the evaluaton, or to the move generator. In engines leaf nodes typically call both (the move generator to ascertain there are no captures to search). In engines one typically refrains from frivolous calling of MakeMove, but only calls it if the aim i to actually do something in that node. Then calling MakeMoves amounts to the same thing. It is very easy in that case to drive up the nps enormously, by making lots of moves to nodes where nothing is done: you can do nothing an extremely large number of times per second. Although the MakeMove is extremely cheap compared to move generation, it is not absolutely free, so such pointless making of moves still slows down the engine.
tcusr
Posts: 325
Joined: Tue Aug 31, 2021 10:32 pm
Full name: tcusr

Re: Mailboxes to bitboards transition

Post by tcusr »

federico wrote: Sun Dec 26, 2021 6:34 pm In my case, i count nodes as every call to the recursive AB function and Qsearch functions, excluding leaf nodes, repetitions, 50 rule and TT hits.
same
User avatar
emadsen
Posts: 440
Joined: Thu Apr 26, 2012 1:51 am
Location: Oak Park, IL, USA
Full name: Erik Madsen

Re: Mailboxes to bitboards transition

Post by emadsen »

federico wrote: Sun Dec 26, 2021 6:34 pm I don't know if there's a standard, but if there is would really like to know. You mentioned that nodes are counted whenever the make move function is called. I presume it excludes null moves for instance. Are there any other exceptions? How about illegal moves? My engine generates pseudo legal and to know if legal, i try the move. Is there a link to a talkchess thread or cpw page that discusses this topic?

In my case, i count nodes as every call to the recursive AB function and Qsearch functions, excluding leaf nodes, repetitions, 50 rule and TT hits.
I'm glad you asked. I discovered I over-count by incrementing node count during move legality testing. I'll remove that. I do increment node count when playing a null move. I advance the position counter and update a few position properties, so I consider that a node.

I don't count nodes during move generation, piece mobility evaluation, or any other evaluation. So once I correct the move legality over-counting, that reduces NPS in my engine to 1) played legal moves and 2) played null moves.

Compared to strict measurements of Elo, NPS is a nebulous concept.
Erik Madsen | My C# chess engine: https://www.madchess.net
Chessnut1071
Posts: 313
Joined: Tue Aug 03, 2021 2:41 pm
Full name: Bill Beame

Re: Mailboxes to bitboards transition

Post by Chessnut1071 »

emadsen wrote: Mon Dec 27, 2021 12:46 am
federico wrote: Sun Dec 26, 2021 6:34 pm I don't know if there's a standard, but if there is would really like to know. You mentioned that nodes are counted whenever the make move function is called. I presume it excludes null moves for instance. Are there any other exceptions? How about illegal moves? My engine generates pseudo legal and to know if legal, i try the move. Is there a link to a talkchess thread or cpw page that discusses this topic?

In my case, i count nodes as every call to the recursive AB function and Qsearch functions, excluding leaf nodes, repetitions, 50 rule and TT hits.
I'm glad you asked. I discovered I over-count by incrementing node count during move legality testing. I'll remove that. I do increment node count when playing a null move. I advance the position counter and update a few position properties, so I consider that a node.

I don't count nodes during move generation, piece mobility evaluation, or any other evaluation. So once I correct the move legality over-counting, that reduces NPS in my engine to 1) played legal moves and 2) played null moves.

Compared to strict measurements of Elo, NPS is a nebulous concept.
? on your engine. When you develop the list of pseudo moves how much information do you take along with the piece and move. Many report that they take a few as possible since many pseudo moves will be thrown away. In addition to piece and move, I take capture/defender, move direction, check, double check, discovered check, check direction, attacker, attacker location and ent passant. The time overhead is only 25% for that information, and if you take it later the time savings disappears. My objective is checkmate, not ELO, so I may need more info for my objective. I'd be interested to see how much and when you take all the other associated data with a given move.
User avatar
emadsen
Posts: 440
Joined: Thu Apr 26, 2012 1:51 am
Location: Oak Park, IL, USA
Full name: Erik Madsen

Re: Mailboxes to bitboards transition

Post by emadsen »

Chessnut1071 wrote: Mon Dec 27, 2021 7:38 pmWhen you develop the list of pseudo moves how much information do you take along with the piece and move... In addition to piece and move, I take capture/defender, move direction, check, double check, discovered check, check direction, attacker, attacker location and ent passant.
I collect similar information minus some check details. During pseudo-legal move generation, MadChess collects:
  • From Square
  • To Square
  • Victim (or Piece.None)
  • Attacker (the moving piece: Piece.Pawn, Piece.Knight, etc)
  • Piece-specific properties (En Passant, Pawn Move, Double Pawn Move, Promoted Piece, King Move, Castling)
During staged move generation (first TT move, then captures, then non-captures), MadChess collects:
  • Is Best Move (from TT)
  • Killer Score (0, 1, 2)
  • History Score
During move legality testing, MadChess collects IsCheck.

All of this is packed into a ulong Move with the following structure:

Code: Select all

// Move Bits
// Higher priority moves have higher ulong value.

// 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
// 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// B|CapV   |CapA   |Promo  |Kil|History                                                |!|O|K|E|2|P|C|From         |To

// B =     Best Move
// CapV =  Capture Victim
// CapA =  Capture Attacker (inverted)
// Promo = Promoted Piece
// Kil =   Killer Move
// ! =     Played
// O =     Castling
// K =     King Move
// E =     En Passant Capture
// 2 =     Double Pawn Move
// P =     Pawn Move
// C =     Check
// From =  From (one extra bit for illegal square)
// To =    To (one extra bit for illegal square)
More important move aspects are packed into higher-order bits. This has the advantage of enabling sorting of moves by priority simply by sorting their ulong value. Sorting is done in stages (sort captures during the capture move generation stage, sort non-captures during their stage).
Erik Madsen | My C# chess engine: https://www.madchess.net
Chessnut1071
Posts: 313
Joined: Tue Aug 03, 2021 2:41 pm
Full name: Bill Beame

Re: Mailboxes to bitboards transition

Post by Chessnut1071 »

emadsen wrote: Mon Dec 27, 2021 10:53 pm
Chessnut1071 wrote: Mon Dec 27, 2021 7:38 pmWhen you develop the list of pseudo moves how much information do you take along with the piece and move... In addition to piece and move, I take capture/defender, move direction, check, double check, discovered check, check direction, attacker, attacker location and ent passant.
I collect similar information minus some check details. During pseudo-legal move generation, MadChess collects:
  • From Square
  • To Square
  • Victim (or Piece.None)
  • Attacker (the moving piece: Piece.Pawn, Piece.Knight, etc)
  • Piece-specific properties (En Passant, Pawn Move, Double Pawn Move, Promoted Piece, King Move, Castling)
During staged move generation (first TT move, then captures, then non-captures), MadChess collects:
  • Is Best Move (from TT)
  • Killer Score (0, 1, 2)
  • History Score
During move legality testing, MadChess collects IsCheck.

All of this is packed into a ulong Move with the following structure:

Code: Select all

// Move Bits
// Higher priority moves have higher ulong value.

// 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
// 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// B|CapV   |CapA   |Promo  |Kil|History                                                |!|O|K|E|2|P|C|From         |To

// B =     Best Move
// CapV =  Capture Victim
// CapA =  Capture Attacker (inverted)
// Promo = Promoted Piece
// Kil =   Killer Move
// ! =     Played
// O =     Castling
// K =     King Move
// E =     En Passant Capture
// 2 =     Double Pawn Move
// P =     Pawn Move
// C =     Check
// From =  From (one extra bit for illegal square)
// To =    To (one extra bit for illegal square)
More important move aspects are packed into higher-order bits. This has the advantage of enabling sorting of moves by priority simply by sorting their ulong value. Sorting is done in stages (sort captures during the capture move generation stage, sort non-captures during their stage).
Great example, I'm going to convert my 11 byte structure to a UInt64 byte. Learn something new here every day. thx
User avatar
emadsen
Posts: 440
Joined: Thu Apr 26, 2012 1:51 am
Location: Oak Park, IL, USA
Full name: Erik Madsen

Re: Mailboxes to bitboards transition

Post by emadsen »

Chessnut1071 wrote: Tue Dec 28, 2021 1:35 am Great example, I'm going to convert my 11 byte structure to a UInt64 byte. Learn something new here every day. thx
Glad to help. Note that a ulong is 8 bytes = 64 bits. Perhaps you mean 11 ints = 11 * 4 bytes = 44 bytes? Compacting moves to a ulong should improve your engine's move generation speed. I have no idea how much.

Refer to the Move class to see how to get data in and out of the structure via static methods that apply bit masks and shifts.
Erik Madsen | My C# chess engine: https://www.madchess.net
Chessnut1071
Posts: 313
Joined: Tue Aug 03, 2021 2:41 pm
Full name: Bill Beame

Re: Mailboxes to bitboards transition

Post by Chessnut1071 »

emadsen wrote: Tue Dec 28, 2021 1:58 am
Chessnut1071 wrote: Tue Dec 28, 2021 1:35 am Great example, I'm going to convert my 11 byte structure to a UInt64 byte. Learn something new here every day. thx
Glad to help. Note that a ulong is 8 bytes = 64 bits. Perhaps you mean 11 ints = 11 * 4 bytes = 44 bytes? Compacting moves to a ulong should improve your engine's move generation speed. I have no idea how much.

Refer to the Move class to see how to get data in and out of the structure via static methods that apply bit masks and shifts.
I need a total of 48 bits for the data I store, so, I have 12 more bits for new stuff. This has been a huge transition for me. I was working in integers when I first joined. After reading the benefits of bitboards, I started working in bytes and bits. This was like a new language for me, but, the speed is absolutely incredible. I have some problems which my computer takes 136 days to solve using my older engines. This is like a new found toy.
Chessnut1071
Posts: 313
Joined: Tue Aug 03, 2021 2:41 pm
Full name: Bill Beame

Re: Mailboxes to bitboards transition

Post by Chessnut1071 »

emadsen wrote: Tue Dec 28, 2021 1:58 am
Chessnut1071 wrote: Tue Dec 28, 2021 1:35 am Great example, I'm going to convert my 11 byte structure to a UInt64 byte. Learn something new here every day. thx
Glad to help. Note that a ulong is 8 bytes = 64 bits. Perhaps you mean 11 ints = 11 * 4 bytes = 44 bytes? Compacting moves to a ulong should improve your engine's move generation speed. I have no idea how much.

Refer to the Move class to see how to get data in and out of the structure via static methods that apply bit masks and shifts.
Just finished my bitboard engine although it's the first draft and not quite optimum yet. My integer application of mailbox averaged 2.94 seconds per game solution. The bitboard averaged .045 seconds, or, over 65wx faster on the same solutions. UNBELIEVABLE. I probably had serious inefficiencies with my old engine since it was basically integer arrays with dozens of loops. Going to bitboards forced me to work in bits & bytes instead of integers. Bit operations seem to eliminate those loops for very fast bit shifts. I couldn't believe the speed increase; however, it's still nowhere near the speed of most of you on this board. I'm getting closer. I'm not sure where to go next although I'm going to research magic bitboards, since they're supposed to be even faster. The thing I like most about bitboards is the reduction in sort time, one 64-bit variable instead of 12 integer fields per record. Since the sort requirement gets more intensive the higher the ply level, it should significantly reduce the speed even further.

In spite of the significant increase in speed, I still need to get over 100x this speed for my objective, otherwise, I have problems.

thx for the ideas everyone.