aboutsummaryrefslogtreecommitdiff
path: root/src/moves.cpp
diff options
context:
space:
mode:
authorAdam <adammegarules1@gmail.com>2026-08-17 15:22:37 +0200
committerAdam <adammegarules1@gmail.com>2026-08-17 15:22:37 +0200
commitad04f16e478357633c08174e844c8756f7892a32 (patch)
tree6e1ca98dfa67584e86346bfe36c5d98ff6d59b3c /src/moves.cpp
parent26c0c08b1c4fb302db55355bd49181c8740b7ee6 (diff)
refactor(board): refactoring move generation code into better and nice way + adding debug command moves
Diffstat (limited to 'src/moves.cpp')
-rw-r--r--src/moves.cpp352
1 files changed, 197 insertions, 155 deletions
diff --git a/src/moves.cpp b/src/moves.cpp
index 8120f18..ac6e5cb 100644
--- a/src/moves.cpp
+++ b/src/moves.cpp
@@ -10,6 +10,10 @@
#include "board/board.hpp"
#include "moves.hpp"
+static void Assert_message() {
+ std::cout << "Internal error: run in debug to see assert\n";
+}
+
constexpr std::array<std::uint64_t, 64> computeKnightAttacks() {
std::array<std::uint64_t, 64> attacks{};
@@ -64,62 +68,71 @@ 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(const Game &g, std::vector<uint16_t> &moves,
- bool GenerateQuietMoves) {
- uint64_t knights = g.PieceBitboards[g.turn][KNIGHT];
- while (knights != 0) {
- auto from = static_cast<uint8_t>(__builtin_ctzll(knights));
- knights &= knights - 1;
- uint64_t knight_attacks = KNIGHT_ATTACKS[static_cast<size_t>(from)];
- while (knight_attacks != 0) {
- auto next = static_cast<uint8_t>(__builtin_ctzll(knight_attacks));
- knight_attacks &= knight_attacks - 1;
-
- bool hasFriendlyPiece = g.turn
- ? (g.WhitePieceBitboard & (1ULL << next)) > 0
- : (g.BlackPieceBitboard & (1ULL << next)) > 0;
-
- bool isCaptuare = (g.PieceBitboard & (1ULL << next)) > 0;
- if (hasFriendlyPiece) {
- continue;
- }
- if (!GenerateQuietMoves && !isCaptuare) {
- continue;
- }
- moves.push_back(CreateMove(from, next));
+static void GenerateKnightMoves(const uint8_t &from, const Game &g,
+ std::vector<uint16_t> &moves,
+ const move_generate_options &options) {
+ uint64_t knight_attacks = KNIGHT_ATTACKS[static_cast<size_t>(from)];
+ while (knight_attacks != 0) {
+ auto next = static_cast<uint8_t>(__builtin_ctzll(knight_attacks));
+ knight_attacks &= knight_attacks - 1;
+
+ bool hasFriendlyPiece = g.turn
+ ? (g.WhitePieceBitboard & (1ULL << next)) > 0
+ : (g.BlackPieceBitboard & (1ULL << next)) > 0;
+
+ bool isCaptuare = (g.PieceBitboard & (1ULL << next)) > 0;
+ if (hasFriendlyPiece) {
+ continue;
+ }
+ if (options == NON_CAPTUARES_ONLY && isCaptuare) {
+ continue;
+ }
+ if (options == CAPTUARES_ONLY && !isCaptuare) {
+ continue;
}
+ moves.push_back(CreateMove(from, next));
}
};
-void GeneratePawnMoves(const Game &g, uint8_t from,
- std::vector<uint16_t> &moves, bool quietMoves) {
+static void GeneratePawnMoves(const uint8_t &from, const Game &g,
+ std::vector<uint16_t> &moves,
+ const move_generate_options &options) {
+ constexpr std::array<int, 2> startingRank = {6, 1};
+
Piece pawn = g.pieces[from];
if (pawn.type != PAWN) {
assert(false && "Calling generate pawn moves on non pawn");
+ Assert_message();
+ exit(1);
+ return;
+ }
+ if (from >= 64) {
+ assert(false && "from should be valid square index");
+ Assert_message();
+ exit(1);
return;
}
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};
+ auto next = static_cast<uint8_t>(from + step);
- // wrap check
- if (position.rank + (pawn.color ? 1 : -1) < 0 ||
- position.rank + (pawn.color ? 1 : -1) >= 8) {
+ // because fen allow arbitrary position we need to check that pawn is not
+ // gonna go out of board
+ if (next >= 64) {
return;
}
- const bool oneStepOccupied = (g.PieceBitboard & (1ULL << (from + step))) != 0;
+ const bool oneStepOccupied = (g.PieceBitboard & (1ULL << next)) != 0;
// if piece it want to move to is none and it as legal move
- if (!oneStepOccupied && quietMoves) {
+ if (!oneStepOccupied && options != CAPTUARES_ONLY) {
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));
+ moves.push_back(CreateMove(from, next, QUEEN));
+ moves.push_back(CreateMove(from, next, ROOK));
+ moves.push_back(CreateMove(from, next, BISHOP));
+ moves.push_back(CreateMove(from, next, KNIGHT));
} else {
- moves.push_back(CreateMove(from, static_cast<uint8_t>(next)));
+ moves.push_back(CreateMove(from, next));
}
const bool twoStepOccupied =
@@ -133,6 +146,10 @@ void GeneratePawnMoves(const Game &g, uint8_t from,
}
};
+ if (options == NON_CAPTUARES_ONLY) {
+ return;
+ }
+
uint64_t enpassant = 0;
if (g.canEnpassant) {
enpassant |= (1ULL << PositionToIndex(g.enPassant));
@@ -157,31 +174,42 @@ void GeneratePawnMoves(const Game &g, uint8_t from,
}
}
};
-void GenerateKingMoves(Game *g, int from, std::vector<uint16_t> &moves,
- bool GenerateQuietMoves) {
- if (g->pieces[from].type != KING) {
+static void GenerateKingMoves(const Game &g, const uint8_t &from,
+ std::vector<uint16_t> &moves,
+ const move_generate_options &options) {
+ if (g.pieces[from].type != KING) {
assert(false && "calling generate king moves on non king");
- return;
+ Assert_message();
+ exit(1);
}
- if (g->pieces[from].color != g->turn) {
+ if (g.pieces[from].color != g.turn) {
assert(false && "calling generate king moves on king of opposite color");
- return;
+ Assert_message();
+ exit(1);
+ }
+ if (from >= 64) {
+ assert(false && "Expected valid chess square");
+ Assert_message();
+ exit(1);
}
- constexpr std::array<int, 8> king_moves{-1, 1, 8, -8, -9, 9, -7, 7};
- for (int offset : king_moves) {
- uint8_t next = static_cast<uint8_t>(from + offset);
+ constexpr std::array<int, 8> king_offsets{-1, 1, 8, -8, -9, 9, -7, 7};
+ for (int offset : king_offsets) {
+ auto next = static_cast<uint8_t>(from + offset);
if (next >= 64) {
continue;
}
if (std::abs((next % 8) - (from % 8)) > 1) {
continue;
};
- if (g->pieces[next].type != NONEPIECE) {
- if (g->pieces[next].color == g->turn) {
+ if (g.pieces[next].type != NONEPIECE) {
+ if (g.pieces[next].color == g.turn) {
continue;
};
}
- if (!GenerateQuietMoves && g->pieces[next].type == NONEPIECE) {
+ if (options == CAPTUARES_ONLY && g.pieces[next].type == NONEPIECE) {
+ continue;
+ }
+ if (options == NON_CAPTUARES_ONLY && g.pieces[next].type != NONEPIECE) {
continue;
}
@@ -189,51 +217,7 @@ void GenerateKingMoves(Game *g, int from, std::vector<uint16_t> &moves,
}
};
-std::vector<uint16_t> GetPseudoLegalMoves(Game *g, bool GenerateQuietMoves) {
- std::vector<uint16_t> moves;
- moves.reserve(40);
-
- GenerateKnightMoves(*g, moves, GenerateQuietMoves);
-
- constexpr std::array<int, 4> rook_Moves{-1, 1, 8, -8};
- constexpr std::array<int, 4> bishop_Moves{-9, 9, -7, 7};
-
- uint64_t piece_bitboard = g->PieceBitboard;
- while (piece_bitboard != 0) {
- uint8_t i = static_cast<uint8_t>(__builtin_ctzll(piece_bitboard));
- piece_bitboard &= piece_bitboard - 1;
- Piece piece = g->pieces[i];
- if (piece.type == NONEPIECE) {
- assert(false && "got none piece in piece bitboard");
- }
- if (piece.color != g->turn) {
- continue;
- }
-
- if (piece.type == PAWN) {
- GeneratePawnMoves(*g, i, moves, GenerateQuietMoves);
- }
- if (piece.type == BISHOP) {
- GenerateSlidingMoves(g, i, bishop_Moves, moves, GenerateQuietMoves);
- }
- if (piece.type == ROOK) {
- GenerateSlidingMoves(g, i, rook_Moves, moves, GenerateQuietMoves);
- }
- if (piece.type == QUEEN) {
- GenerateSlidingMoves(g, i, rook_Moves, moves, GenerateQuietMoves);
- GenerateSlidingMoves(g, i, bishop_Moves, moves, GenerateQuietMoves);
- }
- if (piece.type == KING) {
- GenerateKingMoves(g, i, moves, GenerateQuietMoves);
- if (GenerateQuietMoves) {
- GenerateCastlingMoves(i, g, moves);
- }
- };
- };
-
- return moves;
-}
-bool IsSquareAttacked(Game *g, Position square, bool byColor) {
+bool IsSquareAttacked(const Game &g, Position square, bool byColor) {
int target = PositionToIndex(square);
// Pawn attacks: a pawn of byColor attacks diagonally "forward" from its
@@ -247,7 +231,7 @@ bool IsSquareAttacked(Game *g, Position square, bool byColor) {
if (file < 0 || file >= 8)
continue;
- Piece p = g->pieces[pawnRank * 8 + file];
+ Piece p = g.pieces[pawnRank * 8 + file];
if (p.type == PAWN && p.color == byColor) {
return true;
}
@@ -265,7 +249,7 @@ bool IsSquareAttacked(Game *g, Position square, bool byColor) {
if (fileDiff != 1 && fileDiff != 2)
continue;
- Piece p = g->pieces[from];
+ Piece p = g.pieces[from];
if (p.type == KNIGHT && p.color == byColor)
return true;
}
@@ -279,7 +263,7 @@ bool IsSquareAttacked(Game *g, Position square, bool byColor) {
if (std::abs((from % 8) - (target % 8)) > 1)
continue;
- Piece p = g->pieces[from];
+ Piece p = g.pieces[from];
if (p.type == KING && p.color == byColor)
return true;
}
@@ -295,7 +279,7 @@ bool IsSquareAttacked(Game *g, Position square, bool byColor) {
if ((dir == 1 || dir == -1) && next / 8 != pos / 8)
break; // horizontal wrap
- Piece p = g->pieces[next];
+ Piece p = g.pieces[next];
if (p.type != NONEPIECE) {
if (p.color == byColor && (p.type == ROOK || p.type == QUEEN))
return true;
@@ -316,7 +300,7 @@ bool IsSquareAttacked(Game *g, Position square, bool byColor) {
if (std::abs((next % 8) - (pos % 8)) != 1)
break; // diagonal wrap
- Piece p = g->pieces[next];
+ Piece p = g.pieces[next];
if (p.type != NONEPIECE) {
if (p.color == byColor && (p.type == BISHOP || p.type == QUEEN))
return true;
@@ -328,44 +312,18 @@ bool IsSquareAttacked(Game *g, Position square, bool byColor) {
return false;
}
-std::vector<uint16_t> GetLegalMoves(Game *g, bool quietMove) {
- std::vector<uint16_t> moves = GetPseudoLegalMoves(g, quietMove);
- std::vector<uint16_t> legalMoves;
- legalMoves.reserve(moves.size());
-
- for (const uint16_t &move : moves) {
- Undo undo = MakeMove(move, g);
-
- bool legal = true;
-
- Position kingPosition = FindKing(g, !g->turn);
-
- // opponent attacks our king?
- if (IsSquareAttacked(g, kingPosition, g->turn)) {
- legal = false;
- }
-
- UndoMove(undo, g);
-
- if (legal) {
- legalMoves.push_back(move);
- }
- }
-
- return legalMoves;
-}
GameState GetNewGameState(Game *g) {
// make sure we dont override game ending states
if (g->state == DRAW || g->state == WHITE_WON || g->state == BLACK_WON) {
return g->state;
}
- auto legalMoves = GetLegalMoves(g);
+ auto legalMoves = GetLegalMoves(g, ALL);
if (legalMoves.empty()) {
- Position kingPosition = FindKing(g, g->turn);
+ Position kingPosition = FindKing(*g, g->turn);
- bool check = IsSquareAttacked(g, kingPosition, !g->turn);
+ bool check = IsSquareAttacked(*g, kingPosition, !g->turn);
if (check) {
g->state = g->turn ? BLACK_WON : WHITE_WON;
@@ -382,10 +340,10 @@ Position IndexToPosition(int i) {
return {.rank = rank, .file = file};
}
-void GenerateSlidingMoves(Game *b, int from,
- const std::array<int, 4> &directions,
- std::vector<uint16_t> &moves,
- bool GenerateQuietMoves) {
+static void GenerateSlidingMoves(const uint8_t &from, const Game &g,
+ const std::array<int, 4> &directions,
+ std::vector<uint16_t> &moves,
+ const move_generate_options options) {
for (uint i = 0; i < directions.size(); i++) {
int direction = directions[i];
@@ -409,71 +367,155 @@ void GenerateSlidingMoves(Game *b, int from,
(i2 / 8 != (i2 - direction) / 8)) {
break;
}
- if (b->pieces[i2].color == b->turn && b->pieces[i2].type != NONEPIECE) {
+ if (g.pieces[i2].color == g.turn && g.pieces[i2].type != NONEPIECE) {
break;
}
- if (!GenerateQuietMoves && b->pieces[i2].type == NONEPIECE) {
+ if (options == CAPTUARES_ONLY && g.pieces[i2].type == NONEPIECE) {
continue;
}
+ if (options == NON_CAPTUARES_ONLY && g.pieces[i2].type != NONEPIECE) {
+ break;
+ }
moves.push_back(
CreateMove(static_cast<uint8_t>(from), static_cast<uint8_t>(i2)));
- if (b->pieces[i2].color != b->turn && b->pieces[i2].type != NONEPIECE) {
+ if (g.pieces[i2].color != g.turn && g.pieces[i2].type != NONEPIECE) {
break;
}
}
};
};
-void GenerateCastlingMoves(int from, Game *g, std::vector<uint16_t> &moves) {
- Piece piece = g->pieces[from];
+static void GenerateCastlingMoves(const uint8_t &from, const Game &g,
+ std::vector<uint16_t> &moves) {
+ Piece piece = g.pieces[from];
if (piece.type != KING) {
assert(false && "Calling generate castling moves on non king piece");
- std::cout << "Internal error\n";
+ Assert_message();
+ exit(1);
+ }
+ if (from >= 64) {
+ assert(false && "expected a valid square index");
+ Assert_message();
exit(1);
}
- if (from != 60 && !g->turn) {
+ if (from != 60 && !g.turn) {
return;
}
- if (from != 4 && g->turn) {
+ if (from != 4 && g.turn) {
return;
}
- bool oneToRight = (g->PieceBitboard & (1ULL << (from + 1))) > 0;
- bool twoToRight = (g->PieceBitboard & (1ULL << (from + 2))) > 0;
- bool oneToLeft = (g->PieceBitboard & (1ULL << (from - 1))) > 0;
- bool twoToLeft = (g->PieceBitboard & (1ULL << (from - 2))) > 0;
- bool threeToLeft = (g->PieceBitboard & (1ULL << (from - 3))) > 0;
+ bool oneToRight = (g.PieceBitboard & (1ULL << (from + 1))) > 0;
+ bool twoToRight = (g.PieceBitboard & (1ULL << (from + 2))) > 0;
+ bool oneToLeft = (g.PieceBitboard & (1ULL << (from - 1))) > 0;
+ bool twoToLeft = (g.PieceBitboard & (1ULL << (from - 2))) > 0;
+ bool threeToLeft = (g.PieceBitboard & (1ULL << (from - 3))) > 0;
- Position kingPosition = FindKing(g, g->turn);
+ Position kingPosition = FindKing(g, g.turn);
- bool check = IsSquareAttacked(g, kingPosition, !g->turn);
+ bool check = IsSquareAttacked(g, kingPosition, !g.turn);
if (check) {
return;
}
auto pathIsSafe = [&](int step) {
- return !IsSquareAttacked(g, IndexToPosition(from + step), !g->turn) &&
- !IsSquareAttacked(g, IndexToPosition(from + (2 * step)), !g->turn);
+ return !IsSquareAttacked(g, IndexToPosition(from + step), !g.turn) &&
+ !IsSquareAttacked(g, IndexToPosition(from + (2 * step)), !g.turn);
};
- if (g->turn) {
+ if (g.turn) {
// white
- if (!oneToRight && !twoToRight && g->whiteCastleKing && pathIsSafe(1)) {
+ if (!oneToRight && !twoToRight && g.whiteCastleKing && pathIsSafe(1)) {
moves.push_back(CreateMove(static_cast<uint8_t>(from),
static_cast<uint8_t>(from + 2)));
}
- if (!oneToLeft && !twoToLeft && !threeToLeft && g->whiteCastleQueen &&
+ if (!oneToLeft && !twoToLeft && !threeToLeft && g.whiteCastleQueen &&
pathIsSafe(-1)) {
moves.push_back(CreateMove(static_cast<uint8_t>(from),
static_cast<uint8_t>(from - 2)));
}
}
- if (!g->turn) {
+ if (!g.turn) {
// black
- if (!oneToRight && !twoToRight && g->blackCastleKing && pathIsSafe(1)) {
+ if (!oneToRight && !twoToRight && g.blackCastleKing && pathIsSafe(1)) {
moves.push_back(CreateMove(static_cast<uint8_t>(from),
static_cast<uint8_t>(from + 2)));
}
- if (!oneToLeft && !twoToLeft && !threeToLeft && g->blackCastleQueen &&
+ if (!oneToLeft && !twoToLeft && !threeToLeft && g.blackCastleQueen &&
pathIsSafe(-1)) {
moves.push_back(CreateMove(static_cast<uint8_t>(from),
static_cast<uint8_t>(from - 2)));
}
}
}
+
+std::vector<uint16_t>
+GetPseudoLegalMoves(const Game &g, const move_generate_options &options) {
+ std::vector<uint16_t> moves;
+ moves.reserve(40);
+
+ constexpr std::array<int, 4> ROOK_MOVES{-1, 1, 8, -8};
+ constexpr std::array<int, 4> BISHOP_MOVES{-9, 9, -7, 7};
+
+ uint64_t piece_bitboard = g.PieceBitboard;
+ while (piece_bitboard != 0) {
+ const auto i = static_cast<uint8_t>(__builtin_ctzll(piece_bitboard));
+ piece_bitboard &= piece_bitboard - 1;
+ Piece piece = g.pieces[i];
+ if (piece.type == NONEPIECE) {
+ assert(false && "got none piece in piece bitboard");
+ }
+ if (piece.color != g.turn) {
+ continue;
+ }
+
+ if (piece.type == PAWN) {
+ GeneratePawnMoves(i, g, moves, options);
+ }
+ if (piece.type == KNIGHT) {
+ GenerateKnightMoves(i, g, moves, options);
+ }
+ if (piece.type == BISHOP) {
+ GenerateSlidingMoves(i, g, BISHOP_MOVES, moves, options);
+ }
+ if (piece.type == ROOK) {
+ GenerateSlidingMoves(i, g, ROOK_MOVES, moves, options);
+ }
+ if (piece.type == QUEEN) {
+ GenerateSlidingMoves(i, g, ROOK_MOVES, moves, options);
+ GenerateSlidingMoves(i, g, BISHOP_MOVES, moves, options);
+ }
+ if (piece.type == KING) {
+ GenerateKingMoves(g, i, moves, options);
+ if (options != CAPTUARES_ONLY) {
+ GenerateCastlingMoves(i, g, moves);
+ }
+ };
+ };
+
+ return moves;
+}
+
+std::vector<uint16_t> GetLegalMoves(Game *g,
+ const move_generate_options &options) {
+ std::vector<uint16_t> moves = GetPseudoLegalMoves(*g, options);
+ std::vector<uint16_t> legalMoves;
+ legalMoves.reserve(moves.size());
+
+ // this is a terrible way to checking it
+ for (const uint16_t &move : moves) {
+ Undo undo = MakeMove(move, g);
+
+ bool legal = true;
+
+ Position kingPosition = FindKing(*g, !g->turn);
+
+ if (IsSquareAttacked(*g, kingPosition, g->turn)) {
+ legal = false;
+ }
+
+ UndoMove(undo, g);
+
+ if (legal) {
+ legalMoves.push_back(move);
+ }
+ }
+
+ return legalMoves;
+}