With so many options to chose from on the wiki I thought I should first start with something original as a challenge to myself. And what I found was pleasently concise and fast and so I stuck with it.
Code: Select all
//returns the index of the least significant bit of the bitboard, bb can't be 0
public static int LSB(ulong bb) => BitOperations.TrailingZeroCount(bb);
//resets the least significant bit of the bitboard
public static ulong ClearLSB(ulong bb) => Bmi1.X64.ResetLowestSetBit(bb);
//public static ulong ClearLSB(ulong bb) => bb & (bb - 1);
const ulong HORIZONTAL = 0x00000000000000FFUL;
const ulong VERTICAL = 0x0101010101010101UL;
public static ulong GetDiagonalTargets(ulong occupation, int square)
{
ulong bbPiece = 1UL << square;
ulong bbBlocker = occupation & ~bbPiece;
//mask the bits below bbPiece
ulong bbBelow = bbPiece - 1;
return GenLines(Diagonals[square], Diagonals[square+64], bbBlocker, bbBelow);
}
public static ulong GetOrthogonalTargets(ulong occupation, int square)
{
ulong bbPiece = 1UL << square;
ulong bbBlocker = occupation & ~bbPiece;
//mask the bits below bbPiece
ulong bbBelow = bbPiece - 1;
//horizontal line through square
ulong bbHorizontal = HORIZONTAL << (square & 56);
//vertical line through square
ulong bbVertical = VERTICAL << (square & 7);
return GenLines(bbHorizontal, bbVertical, bbBlocker, bbBelow);
}
private static ulong GenLines(ulong bbLineA, ulong bbLineB, ulong bbBlocker, ulong bbBelow) =>
GenLine(bbLineA, bbBlocker & bbLineA, bbBelow) |
GenLine(bbLineB, bbBlocker & bbLineB, bbBelow);
private static ulong GenLine(ulong bbLine, ulong bbBlocker, ulong bbBelow)
{
//MaskLow sets all low bits up to and including the lowest blocker above orgin, the rest are zeroed out.
//MaskHigh sets all low bits up to and including the highest blocker below origin, the rest are zerored out.
//The bits of the line that are different between the two masks are the valid targets (including the first blockers on each side)
return (MaskLow(bbBlocker & ~bbBelow) ^ MaskHigh(bbBlocker & bbBelow)) & bbLine;
}
//identify the highest set bit and shift a mask so the bits below are set and the rest are zeroed
private static ulong MaskHigh(ulong bb) => 0x7FFFFFFFFFFFFFFFUL >> BitOperations.LeadingZeroCount(bb | 1);
//identify the lowest set bit and set all bits below while zeroing the rest
private static ulong MaskLow(ulong bb) => bb ^ (bb - 1);
You can compute the diagonals in code just like the orthogonals if you want to be 100% algorithmic but the lookup tables is slightly faster.
Code: Select all
//diagonal line through square
ulong bbDiagonal = VerticalShift(DIAGONAL, file - rank);
//antidiagonal line through square
ulong bbAntiDiagonal = VerticalShift(ANTIDIAGONAL, 7 - file - rank);
//sign of 'ranks' decides between left shift or right shift. Then convert signed ranks to a positiver number of bits to shift by. Each rank has 8 bits e.g. 1 << 3 == 8
private static ulong VerticalShift(in ulong bb, in int ranks) => ranks > 0 ? bb >> (ranks << 3) : bb << -(ranks << 3);
Also if you don't mind me asking, what does your hardware look like.
I use a Ryzen 3600 at 4.2 Ghz. Perft(6) on the startpos runs at 76287K NPS in my implementation.
Zobrist hashing is actually the next thing I'm going to add and I can report back with updated speeds.