From 2f401ec6422a5d44fa1eb64b5b7d1d05c8075b19 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 16 Aug 2026 18:05:27 +0200 Subject: refactor(board): refactoring pawn move generation code for better perfomance and quality --- src/moves.cpp | 129 +++++++++++++++++++++++++++++++--------------------------- src/moves.hpp | 4 +- 2 files changed, 70 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/moves.cpp b/src/moves.cpp index 6299e55..1a23b26 100644 --- a/src/moves.cpp +++ b/src/moves.cpp @@ -34,7 +34,35 @@ constexpr std::array computeKnightAttacks() { return attacks; } +constexpr std::array, 2> computePawnAttacks() { + std::array, 2> attacks{}; + + for (bool color : {false, true}) { + int step = 8 * (color ? 1 : -1); + for (int i = 0; i < 64; i++) { + uint64_t bb = 0; + for (int fileOffset : {-1, 1}) { + int next = i + fileOffset; + next += step; + if (next >= 64 || next < 0) { + continue; + } + const int fileDelta = (next % 8) - (i % 8); + if (fileDelta != 1 && fileDelta != 2 && fileDelta != -1 && + fileDelta != -2) { + continue; + }; + bb |= 1ULL << next; + } + attacks[static_cast(color)][static_cast(i)] = bb; + } + } + return attacks; +} + constexpr std::array KNIGHT_ATTACKS = computeKnightAttacks(); +constexpr std::array, 2> PAWN_ATTACKS = + computePawnAttacks(); static void GenerateKnightMoves(Game *b, std::vector &moves, bool GenerateQuietMoves) { @@ -64,16 +92,17 @@ static void GenerateKnightMoves(Game *b, std::vector &moves, } }; -void GeneratePawnMoves(Game *b, uint8_t from, std::vector &moves, - bool quietMoves) { - Piece pawn = b->pieces[from]; +void GeneratePawnMoves(const Game &g, uint8_t from, + std::vector &moves, bool quietMoves) { + Piece pawn = g.pieces[from]; if (pawn.type != PAWN) { assert(false && "Calling generate pawn moves on non pawn"); return; } - Position position = IndexToPosition(from); - int step = (pawn.color ? 1 : -1) * 8; + const Position position = IndexToPosition(from); + const int step = (pawn.color ? 1 : -1) * 8; int next = from + step; + constexpr std::array startingRank = {6, 1}; // wrap check if (position.rank + (pawn.color ? 1 : -1) < 0 || @@ -81,73 +110,51 @@ void GeneratePawnMoves(Game *b, uint8_t from, std::vector &moves, return; } + const bool oneStepOccupied = (g.PieceBitboard & (1ULL << (from + step))) != 0; + // if piece it want to move to is none and it as legal move - if (b->pieces[next].type == NONEPIECE && quietMoves) { - if (position.rank + (pawn.color ? 1 : -1) == 0 || - position.rank + (pawn.color ? 1 : -1) == 7) { - moves.push_back( - CreateMove(static_cast(PositionToIndex(position)), - static_cast(next), QUEEN)); + if (!oneStepOccupied && quietMoves) { + if (IndexToPosition(next).rank == (pawn.color ? 7 : 0)) { + moves.push_back(CreateMove(from, static_cast(next), QUEEN)); + moves.push_back(CreateMove(from, static_cast(next), ROOK)); + moves.push_back(CreateMove(from, static_cast(next), BISHOP)); + moves.push_back(CreateMove(from, static_cast(next), KNIGHT)); + } else { + moves.push_back(CreateMove(from, static_cast(next))); + } - moves.push_back( - CreateMove(static_cast(PositionToIndex(position)), - static_cast(next), ROOK)); + const bool twoStepOccupied = + (g.PieceBitboard & (1ULL << (from + (step * 2)))) != 0; + if (position.rank == startingRank[static_cast(pawn.color)] && + !twoStepOccupied) { moves.push_back( CreateMove(static_cast(PositionToIndex(position)), - static_cast(next), BISHOP)); - moves.push_back( - CreateMove(static_cast(PositionToIndex(position)), - static_cast(next), KNIGHT)); - } else { - moves.push_back( - CreateMove(static_cast(PositionToIndex(position)), - static_cast(next))); - } - - int startingRank = pawn.color ? 1 : 6; - uint8_t twoSteps = static_cast(from + (step * 2)); - if (position.rank == startingRank && - b->pieces[twoSteps].type == NONEPIECE) { - moves.push_back(CreateMove( - static_cast(PositionToIndex(position)), twoSteps)); + static_cast(from + (step * 2)))); } }; - for (int fileOffset : {-1, 1}) { - uint8_t targetFile = static_cast(position.file + fileOffset); - uint8_t targetRank = - static_cast(position.rank + (pawn.color ? 1 : -1)); + uint64_t enpassant = 0; + if (g.canEnpassant) { + enpassant |= (1ULL << PositionToIndex(g.enPassant)); + } - if (targetFile >= 8) { - continue; - } - if (targetRank >= 8) { - continue; - } + uint64_t pawn_attacks = PAWN_ATTACKS[static_cast(pawn.color)][from]; - uint8_t target = static_cast((targetRank * 8)) + targetFile; + pawn_attacks &= + (pawn.color ? g.BlackPieceBitboard : g.WhitePieceBitboard) | enpassant; - if (IndexToPosition(target) == b->enPassant && b->canEnpassant) { - moves.push_back( - CreateMove(static_cast(PositionToIndex(position)), target)); - } + while (pawn_attacks != 0) { + auto i = static_cast(__builtin_ctzll(pawn_attacks)); + pawn_attacks &= pawn_attacks - 1; - if (b->pieces[target].type != NONEPIECE && - b->pieces[target].color != pawn.color) { - if (targetRank == 0 || targetRank == 7) { - moves.push_back(CreateMove( - static_cast(PositionToIndex(position)), target, QUEEN)); - moves.push_back(CreateMove( - static_cast(PositionToIndex(position)), target, ROOK)); - moves.push_back(CreateMove( - static_cast(PositionToIndex(position)), target, BISHOP)); - moves.push_back(CreateMove( - static_cast(PositionToIndex(position)), target, KNIGHT)); - } else { - moves.push_back(CreateMove( - static_cast(PositionToIndex(position)), target)); - } + if (IndexToPosition(i).rank == (pawn.color ? 7 : 0)) { + moves.push_back(CreateMove(from, i, QUEEN)); + moves.push_back(CreateMove(from, i, KNIGHT)); + moves.push_back(CreateMove(from, i, ROOK)); + moves.push_back(CreateMove(from, i, BISHOP)); + } else { + moves.push_back(CreateMove(from, i)); } } }; @@ -205,7 +212,7 @@ std::vector GetPseudoLegalMoves(Game *g, bool GenerateQuietMoves) { } if (piece.type == PAWN) { - GeneratePawnMoves(g, i, moves, GenerateQuietMoves); + GeneratePawnMoves(*g, i, moves, GenerateQuietMoves); } if (piece.type == BISHOP) { GenerateSlidingMoves(g, i, bishop_Moves, moves, GenerateQuietMoves); diff --git a/src/moves.hpp b/src/moves.hpp index 54ab730..c7ce5e3 100644 --- a/src/moves.hpp +++ b/src/moves.hpp @@ -14,8 +14,8 @@ void GenerateSlidingMoves(Game *g, int from, bool GenerateQuietMoves); void GenerateKingMoves(Game *g, int from, std::vector &moves, bool GenerateQuietMoves); -void GeneratePawnMoves(Game *b, uint8_t from, std::vector &moves, - bool GenerateQuietMoves); +void GeneratePawnMoves(const Game &g, uint8_t from, + std::vector &moves, bool GenerateQuietMoves); bool IsSquareAttacked(Game *g, Position square, bool byColor); void GenerateCastlingMoves(int from, Game *g, std::vector &moves); GameState GetNewGameState(Game *g); -- cgit v1.2.3