Bitboard of a bishops target squares.

Discussion of chess software programming and technical issues.

Moderator: Ras

User avatar
lithander
Posts: 918
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: Bitboard of a bishops target squares.

Post by lithander »

lithander wrote: Wed Oct 13, 2021 5:22 pm ...but it seems that indeed for the bishops there's no simple solution like for the rooks.

Code: Select all

U64 diagonalMask(int sq) {
   const U64 maindia = C64(0x8040201008040201);
   int diag =8*(sq & 7) - (sq & 56);
   int nort = -diag & ( diag >> 31);
   int sout =  diag & (-diag >> 31);
   return (maindia >> sout) << nort;
}

U64 antiDiagMask(int sq) {
   const U64 maindia = C64(0x0102040810204080);
   int diag =56- 8*(sq&7) - (sq&56);
   int nort = -diag & ( diag >> 31);
   int sout =  diag & (-diag >> 31);
   return (maindia >> sout) << nort;
}
I de-obfuscated the code to understand what it does. Here's an alternative way to write it:

Code: Select all

int rank = square / 8;
int file = square & 7;

const ulong DIAGONAL = 0x8040201008040201UL;
int verticalShift = file - rank;
int bitShift = verticalShift << 3;//to shift one rank means to shift by 8 bits
ulong bbDiagonal = bitShift > 0 ? DIAGONAL >> bitShift : DIAGONAL << -bitShift;

const ulong ANTIDIAGONAL = 0x0102040810204080;
verticalShift = 7 - file - rank;
bitShift = verticalShift << 3;//to shift one rank means to shift by 8 bits
ulong bbAntiDiagonal = bitShift > 0 ? ANTIDIAGONAL >> bitShift : ANTIDIAGONAL << -bitShift;
So you basically just have to shift the diagonals vertically and you won't have a problem with any wrap-around bits.
If you think in terms of files and ranks the computation of the value to shift by is a little more intuitive to understand.
Once you know the amount of ranks to shift by multiply with 8 (or shift by 3) because each rank is actually 8 squares.
Now you can't pass a negative 2nd operand to a right-shift. You have to use a left shift. Easily expressed with the ternary operator.

My curiosity is sated. Now what to do in the actual move generator of a competitive engine is a completely different question of course. And aesthetically I really like Mergi's solution with the 15-slot lookup table.
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
pedrojdm2021
Posts: 157
Joined: Fri Apr 30, 2021 7:19 am
Full name: Pedro Duran

Re: Bitboard of a bishops target squares.

Post by pedrojdm2021 »

I think you need it to populate the pre-calculated the bishop and rook attack tables, right?

In my engine i use this function (i also take care of blockers, because of it is used to populate the MagicBitboards table, but you can ignore that):

Code: Select all

public static ulong ComputeSlidingAttacks(int _square , int _piece , ulong _blockerMask)
        {
            ulong attacks = 0ul;
            int rank_origin = _square / 8;
            int file_origin = _square % 8;
            if (_piece == ChessPiece.Bishop)
            {
                // bottom-left
                for (int rank = rank_origin + 1,file = file_origin - 1; rank < 8 && file >= 0; rank++ , file--)
                {
                    int square = file + rank * 8 ;
                    BitUtility.SetBit(ref attacks ,square); 
                    if (BitUtility.ContainsBit(_blockerMask , square)) break;
                }
                // bottom-right
                for (int rank = rank_origin + 1,file = file_origin + 1; rank < 8 && file < 8; rank++ , file++)
                {
                    int square = file + rank * 8 ;
                    BitUtility.SetBit(ref attacks , square);
                    if (BitUtility.ContainsBit(_blockerMask , square)) break;
                }
                // top-left
                for (int rank = rank_origin - 1,file = file_origin - 1; rank >= 0 && file >= 0; rank-- , file--) 
                {
                    int square = file + rank * 8 ;
                    BitUtility.SetBit(ref attacks ,square);
                    if(BitUtility.ContainsBit(_blockerMask , square)) break;
                }
                // top-right 
                for (int rank = rank_origin - 1,file = file_origin + 1; rank >= 0 && file < 8; rank-- , file++) 
                {
                    int square = file + rank * 8 ;
                    BitUtility.SetBit(ref attacks ,square);
                    if (BitUtility.ContainsBit(_blockerMask , square)) break;
                }
            }else if (_piece == ChessPiece.Rook)
            {
                // top
                for (int rank = rank_origin + 1 ; rank < 8; rank++ )
                {
                    int square = file_origin + rank * 8;
                    BitUtility.SetBit(ref attacks ,square);
                    if (BitUtility.ContainsBit(_blockerMask , square)) break;
                } 
                // bottom
                for (int rank = rank_origin - 1 ; rank >= 0; rank-- )
                {
                    int square = file_origin + rank * 8;
                    BitUtility.SetBit(ref attacks ,square);
                    if (BitUtility.ContainsBit(_blockerMask , square)) break;
                } 
                 // left
                for (int file = file_origin - 1 ; file >= 0; file-- )
                {
                    int square = file + rank_origin * 8;
                    BitUtility.SetBit(ref attacks ,square);
                    if (BitUtility.ContainsBit(_blockerMask , square)) break;
                }
                // right
                for (int file = file_origin + 1 ; file < 8; file++ ) 
                {
                    int square = file + rank_origin * 8;
                    BitUtility.SetBit(ref attacks , square);
                    if(BitUtility.ContainsBit(_blockerMask , square)) break;
                }
            }
            return attacks;
        }
you can see that is quite simple, for both bishops and rooks, you don't need to unnecesary complicate your code. And as it will be called only on initialization to initialize the pre-calculated arrays, it will not have a negative impact of performance.

good luck with your bitboards adventure!
dangi12012
Posts: 1062
Joined: Tue Apr 28, 2020 10:03 pm
Full name: Daniel Infuehr

Re: Bitboard of a bishops target squares.

Post by dangi12012 »

It will be faster to just write the loops for the bishop. And very easy to read and understand:

Code: Select all

for(int x=X, int y=Y; x<8 && y<8;x++, y++)
{
  mask |= 1ull << (Y*8+X);
}
//Repeat for x,y signatures ++-- ---- --++
Worlds-fastest-Bitboard-Chess-Movegenerator
Daniel Inführ - Software Developer