I'm currently working on a chess engine and have a pseudo-legal move generator working. I'm looking for a way to efficiently generate a bitboard of pinned pieces, so I could skip them when generating legal moves. How would one go about doing this, given 12 bitboards for each piece, and a bitboard called UNSAFE with all the squares the opponent is threatening?
This task is nontrivial and cannot be obtained easily or setwise from the available bitboards.
The following solution is an example for how such a task might be done for a generic slider - only obvious substitutions are necessary to convert this code to be usable for bishops, rooks and queens.
First, identify the set of possible pinning pieces by calculating sliding attacks from the king square, using the opposite side as the set of occupied squares.
BitBoard bb_pinners = slider_moves(king_square, bb_other_side) & bb_other_side;
All bitboard engines should have some function similar to
rook_moves, which uses magic bitboards, PEXT or Koch-Stone to generate sliding moves from a square, given a bitboard of occupied squares as blockers to the slider.
To determine the set of pinned pieces from the set of pinners, it is necessary to iterate through the set of possible pinners (this is why pinned pieces cannot be calculated setwise).
For each pinner, we consider the set of bits between the pinner square and the king square (using the utility function
between), determine if there is only one bit set, and if so, add that bit to the set of pinned pieces.
BitBoard bb_pinned = 0;
Square sq = pop_bit(bb_pinners);
BitBoard bb_possible_pinned = between(sq, king_square) & bb_own_side;
bb_pinned |= bb_possible_pinned;
pop_bit finds the index of and removes the least significant bit of bb_pinners.
Stockfish implements a similar strategy in
slider_blockers to determine possibly pinned pieces.
It is also worth mentioning that even with a set of these blockers, this does not mean that they may be excluded from legal move generation because if the pinned piece is a slider itself of the correct type, it may still have legal moves despite being pinned, under the above algorithm's definition.
Pinned pieces only exist when attackers are sliding pieces (bishop, rook, queen). Also pinned pieces can move along the attack ray to the king, you cannot exclude them from legal moves directly. For example, the rook can move d4 and d6 in the board image below and the pawn can do enpassant in the 2nd example. You can check all 8 directions N, W, E, S, NW, NE, SW, SE to get all pinned pieces.
A piece is pinned if it suits the rules below.
- It is a friend piece
- It is the closest piece to the friend king placed on the path from king to the end of the board on specific direction (NORTH, SOUTH-EAST etc.)
- The second closest piece after the friend is the enemy and can attack along the direction to the king. Rook and queen for the N-S-W-E. Bishop and queen for NW-NE-SW-SE.
For example I have shared my code to find the pinned piece on the NORTH. A8 is LSB and H1 is MSB in my bitboard. You can change
BitScanForward() according to your bitboard design and direction that is checking for pinned piece. (for south it must be
BitScanForward() in my code)
public static ulong Pinned_NORTH(ulong king_loc, ulong friendPieces, ulong allPieces, ulong rookEnemies, ulong queenEnemies)
//Gets all vertical sliding enemies
ulong straightSlidingEnemies = rookEnemies | queenEnemies;
//if no rook or queen enemies exist, there is no pinned piece
if(straightSlidingEnemies == 0)
//get all squares on the North side of the king
ulong northSquares= CrossLookup.NORTH[king_loc];
//get intersected pieces at the north side of the king, rook and queen in the example
ulong intersectedPieces = northSquares& allPieces;
//get closest piece to the king in intersectedPieces, rook in the example
ulong closestPiece = intersectedPieces.BitScanReverse();
//check if the closest piece is our friend
ulong friend = closestPiece & friendPieces;
//if it is ENEMY, there cannot be a pinned piece in the NORTH
if (friend == 0)
//Get second closest piece and check if it is an enemy that is a rook or a queen who can attack from the NORTH
ulong slidingEnemy = (intersectedPieces & ~closestPiece ).BitScanReverse() & straightSlidingEnemies ;
//if it is NOT A SLIDING ENEMY, there cannot be a pinned piece in the NORTH
if (slidingEnemy == 0)
//Otherwise closest piece is a pinned piece and can only move along the ray from king to the enemy