aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam <adammegarules1@gmail.com>2026-08-16 18:05:27 +0200
committerAdam <adammegarules1@gmail.com>2026-08-16 18:05:27 +0200
commit2f401ec6422a5d44fa1eb64b5b7d1d05c8075b19 (patch)
tree6204da14914b7bcfade0061b7e4bdd68eeff3535
parent55ccaf0c531e1bea330f65c8d2f1c739819c2459 (diff)
refactor(board): refactoring pawn move generation code for better perfomance and quality
-rw-r--r--src/moves.cpp129
-rw-r--r--src/moves.hpp4
2 files changed, 70 insertions, 63 deletions
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<std::uint64_t, 64> computeKnightAttacks() {
return attacks;
}
+constexpr std::array<std::array<std::uint64_t, 64>, 2> computePawnAttacks() {
+ std::array<std::array<std::uint64_t, 64>, 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<size_t>(color)][static_cast<size_t>(i)] = bb;
+ }
+ }
+ return attacks;
+}
+
constexpr std::array<std::uint64_t, 64> KNIGHT_ATTACKS = computeKnightAttacks();
+constexpr std::array<std::array<std::uint64_t, 64>, 2> PAWN_ATTACKS =
+ computePawnAttacks();
static void GenerateKnightMoves(Game *b, std::vector<uint16_t> &moves,
bool GenerateQuietMoves) {
@@ -64,16 +92,17 @@ static void GenerateKnightMoves(Game *b, std::vector<uint16_t> &moves,
}
};
-void GeneratePawnMoves(Game *b, uint8_t from, std::vector<uint16_t> &moves,
- bool quietMoves) {
- Piece pawn = b->pieces[from];
+void GeneratePawnMoves(const Game &g, uint8_t from,
+ std::vector<uint16_t> &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<int, 2> 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<uint16_t> &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<uint8_t>(PositionToIndex(position)),
- static_cast<uint8_t>(next), QUEEN));
+ if (!oneStepOccupied && quietMoves) {
+ if (IndexToPosition(next).rank == (pawn.color ? 7 : 0)) {
+ moves.push_back(CreateMove(from, static_cast<uint8_t>(next), QUEEN));
+ moves.push_back(CreateMove(from, static_cast<uint8_t>(next), ROOK));
+ moves.push_back(CreateMove(from, static_cast<uint8_t>(next), BISHOP));
+ moves.push_back(CreateMove(from, static_cast<uint8_t>(next), KNIGHT));
+ } else {
+ moves.push_back(CreateMove(from, static_cast<uint8_t>(next)));
+ }
- moves.push_back(
- CreateMove(static_cast<uint8_t>(PositionToIndex(position)),
- static_cast<uint8_t>(next), ROOK));
+ const bool twoStepOccupied =
+ (g.PieceBitboard & (1ULL << (from + (step * 2)))) != 0;
+ if (position.rank == startingRank[static_cast<size_t>(pawn.color)] &&
+ !twoStepOccupied) {
moves.push_back(
CreateMove(static_cast<uint8_t>(PositionToIndex(position)),
- static_cast<uint8_t>(next), BISHOP));
- moves.push_back(
- CreateMove(static_cast<uint8_t>(PositionToIndex(position)),
- static_cast<uint8_t>(next), KNIGHT));
- } else {
- moves.push_back(
- CreateMove(static_cast<uint8_t>(PositionToIndex(position)),
- static_cast<uint8_t>(next)));
- }
-
- int startingRank = pawn.color ? 1 : 6;
- uint8_t twoSteps = static_cast<uint8_t>(from + (step * 2));
- if (position.rank == startingRank &&
- b->pieces[twoSteps].type == NONEPIECE) {
- moves.push_back(CreateMove(
- static_cast<uint8_t>(PositionToIndex(position)), twoSteps));
+ static_cast<uint8_t>(from + (step * 2))));
}
};
- for (int fileOffset : {-1, 1}) {
- uint8_t targetFile = static_cast<uint8_t>(position.file + fileOffset);
- uint8_t targetRank =
- static_cast<uint8_t>(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<size_t>(pawn.color)][from];
- uint8_t target = static_cast<uint8_t>((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<uint8_t>(PositionToIndex(position)), target));
- }
+ while (pawn_attacks != 0) {
+ auto i = static_cast<uint8_t>(__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<uint8_t>(PositionToIndex(position)), target, QUEEN));
- moves.push_back(CreateMove(
- static_cast<uint8_t>(PositionToIndex(position)), target, ROOK));
- moves.push_back(CreateMove(
- static_cast<uint8_t>(PositionToIndex(position)), target, BISHOP));
- moves.push_back(CreateMove(
- static_cast<uint8_t>(PositionToIndex(position)), target, KNIGHT));
- } else {
- moves.push_back(CreateMove(
- static_cast<uint8_t>(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<uint16_t> 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<uint16_t> &moves,
bool GenerateQuietMoves);
-void GeneratePawnMoves(Game *b, uint8_t from, std::vector<uint16_t> &moves,
- bool GenerateQuietMoves);
+void GeneratePawnMoves(const Game &g, uint8_t from,
+ std::vector<uint16_t> &moves, bool GenerateQuietMoves);
bool IsSquareAttacked(Game *g, Position square, bool byColor);
void GenerateCastlingMoves(int from, Game *g, std::vector<uint16_t> &moves);
GameState GetNewGameState(Game *g);