Chess Engine Pawns
2026-01-10
Edited: 2026-05-31
Pawn move generation using bitboards. For pawn pushes (moving pawn forward), we can implement this by shifting the bitboard up (for white pawns), and then taking the intersection of the empty board (to ensure no piece is blocking the push). Note that pawns in their starting position can move up two squares.
U64 wSinglePushTargets(U64 wpawns, U64 empty) {
return nortOne() & empty;
}
U64 wDblPushTargets(U64 wpawns, U64 empty) {
const U64 rank4 = C64(0x00000000FF000000);
U64 singlePushs = wSinglePushTargets(,);
// if a pawn is not in the fourth rank after double pushing,
// then it is not in its starting position and therefore
// not allowed to move two squares
return nortOne() & empty & rank4;
}
U64 bSinglePushTargets(U64 bpawns, U64 empty) {
return soutOne() & empty;
}
U64 bDoublePushTargets(U64 bpawns, U64 empty) {
const U64 rank5 = C64(0x000000FF00000000);
U64 singlePushs = bSinglePushTargets(,);
return soutOne() & empty & rank5;
}We use separate implementations for optimization. To check beforehand if a pawn is allowed to push, shift the empty board in the reverse direction, and if the pawn is there, that means it can push
U64 wPawnsAble2Push(U64 wpawns, U64 empty) {
return soutOne() & wpawns;
}
U64 wPawnsAble2DblPush(U64 wpawns, U64 empty) {
const U64 rank4 = C64(0x00000000FF000000);
U64 emptyRank3 = soutOne(&) & empty;
return wPawnsAble2Push(,);
}For attacks, since pawns attack diagonally we can shift the bitboards of the pawns diagonally one square
U64 wPawnEastAttacks(U64 wpawns) {return noEaOne();}
U64 wPawnWestAttacks(U64 wpawns) {return noWeOne();}
U64 bPawnEastAttacks(U64 bpawns) {return soEaOne();}
U64 bPawnWestAttacks(U64 bpawns) {return soWeOne();}
U64 wPawnAnyAttacks(U64 wpawns) {
return wPawnEastAttacks() | wPawnWestAttacks();
}
U64 wPawnDblAttacks(U64 wpawns) {
return wPawnEastAttacks() & wPawnWestAttacks();
}
U64 wPawnSingleAttacks(U64 wpawns) {
return wPawnEastAttacks() ^ wPawnWestAttacks();
}For efficiency, we precomputed attacks from a given position into an array, and then by intersecting that with the bitboard of black pieces we can determine if a capture is possible, like the following
whitePawnAttacks = arrPawnAttacks[white][sqOfWhitePawn];
if (whitePawnAttacks & pieceBB[black]) -> pseudo legal captures possibleEn passant is kind of difficult to implement. Note that a pawn can only be captured via en passant if if double-moved, and if there are enemy pawns next to it.