Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include <vector>
- #include <string>
- #include <chrono>
- #include <sstream>
- using namespace std;
- typedef unsigned long long U64;
- // Piece representation
- enum { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING };
- enum { WHITE, BLACK };
- // Bitboard utilities
- #define C64(x) x##ULL
- #define SET_BIT(bb, sq) ((bb) |= (C64(1) << (sq)))
- #define GET_BIT(bb, sq) ((bb) & (C64(1) << (sq)))
- #define POP_BIT(bb, sq) ((bb) &= ~(C64(1) << (sq)))
- int count_bits(U64 bb) {
- int count = 0;
- while (bb) {
- count++;
- bb &= bb - 1;
- }
- return count;
- }
- int lsb(U64 bb) {
- if (!bb) return -1;
- return count_bits((bb & -bb) - 1);
- }
- struct Move {
- int from, to, piece, captured, promoted, flags;
- Move(int f = 0, int t = 0, int p = 0, int c = -1, int pr = -1, int fl = 0)
- : from(f), to(t), piece(p), captured(c), promoted(pr), flags(fl) {}
- };
- class Position {
- public:
- U64 pieces[2][6]; // [color][piece_type]
- U64 occupied[2];
- U64 all_occupied;
- int side;
- int ep_square;
- int castle_rights;
- int fifty_move;
- Position() {
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < 6; j++) {
- pieces[i][j] = 0;
- }
- occupied[i] = 0;
- }
- all_occupied = 0;
- side = WHITE;
- ep_square = -1;
- castle_rights = 15;
- fifty_move = 0;
- }
- void set_fen(const string& fen) {
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < 6; j++) pieces[i][j] = 0;
- occupied[i] = 0;
- }
- istringstream ss(fen);
- string board, turn, castle, ep;
- ss >> board >> turn >> castle >> ep;
- int sq = 56;
- for (char c : board) {
- if (c == '/') sq -= 16;
- else if (isdigit(c)) sq += (c - '0');
- else {
- int color = isupper(c) ? WHITE : BLACK;
- c = tolower(c);
- int piece = (c == 'p') ? PAWN : (c == 'n') ? KNIGHT : (c == 'b') ? BISHOP :
- (c == 'r') ? ROOK : (c == 'q') ? QUEEN : KING;
- SET_BIT(pieces[color][piece], sq);
- sq++;
- }
- }
- side = (turn == "w") ? WHITE : BLACK;
- castle_rights = 0;
- for (char c : castle) {
- if (c == 'K') castle_rights |= 1;
- if (c == 'Q') castle_rights |= 2;
- if (c == 'k') castle_rights |= 4;
- if (c == 'q') castle_rights |= 8;
- }
- ep_square = (ep == "-") ? -1 : (ep[0] - 'a') + (ep[1] - '1') * 8;
- update_occupancy();
- }
- void update_occupancy() {
- occupied[WHITE] = occupied[BLACK] = 0;
- for (int i = 0; i < 6; i++) {
- occupied[WHITE] |= pieces[WHITE][i];
- occupied[BLACK] |= pieces[BLACK][i];
- }
- all_occupied = occupied[WHITE] | occupied[BLACK];
- }
- U64 get_attacks_pawn(int sq, int color) {
- U64 att = 0;
- if (color == WHITE) {
- if (sq % 8 != 0) att |= C64(1) << (sq + 7);
- if (sq % 8 != 7) att |= C64(1) << (sq + 9);
- } else {
- if (sq % 8 != 0) att |= C64(1) << (sq - 9);
- if (sq % 8 != 7) att |= C64(1) << (sq - 7);
- }
- return att;
- }
- U64 get_attacks_knight(int sq) {
- U64 att = 0;
- int offsets[] = {17, 15, 10, 6, -6, -10, -15, -17};
- int rank = sq / 8, file = sq % 8;
- for (int off : offsets) {
- int to = sq + off;
- if (to < 0 || to > 63) continue;
- int tr = to / 8, tf = to % 8;
- if (abs(tr - rank) + abs(tf - file) == 3) att |= C64(1) << to;
- }
- return att;
- }
- U64 get_attacks_king(int sq) {
- U64 att = 0;
- int offsets[] = {8, -8, 1, -1, 9, -9, 7, -7};
- int rank = sq / 8, file = sq % 8;
- for (int off : offsets) {
- int to = sq + off;
- if (to < 0 || to > 63) continue;
- int tr = to / 8, tf = to % 8;
- if (abs(tr - rank) <= 1 && abs(tf - file) <= 1) att |= C64(1) << to;
- }
- return att;
- }
- U64 get_attacks_slider(int sq, int dir[4][2], U64 occ) {
- U64 att = 0;
- for (int i = 0; i < 4; i++) {
- int r = sq / 8, f = sq % 8;
- while (true) {
- r += dir[i][0]; f += dir[i][1];
- if (r < 0 || r > 7 || f < 0 || f > 7) break;
- int to = r * 8 + f;
- att |= C64(1) << to;
- if (occ & (C64(1) << to)) break;
- }
- }
- return att;
- }
- U64 get_attacks_rook(int sq, U64 occ) {
- int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
- return get_attacks_slider(sq, dir, occ);
- }
- U64 get_attacks_bishop(int sq, U64 occ) {
- int dir[4][2] = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
- return get_attacks_slider(sq, dir, occ);
- }
- bool is_square_attacked(int sq, int by_color) {
- if (get_attacks_pawn(sq, 1 - by_color) & pieces[by_color][PAWN]) return true;
- if (get_attacks_knight(sq) & pieces[by_color][KNIGHT]) return true;
- if (get_attacks_king(sq) & pieces[by_color][KING]) return true;
- U64 rook_att = get_attacks_rook(sq, all_occupied);
- if (rook_att & (pieces[by_color][ROOK] | pieces[by_color][QUEEN])) return true;
- U64 bishop_att = get_attacks_bishop(sq, all_occupied);
- if (bishop_att & (pieces[by_color][BISHOP] | pieces[by_color][QUEEN])) return true;
- return false;
- }
- void generate_moves(vector<Move>& moves) {
- int us = side, them = 1 - side;
- // Pawn moves
- U64 pawns = pieces[us][PAWN];
- while (pawns) {
- int from = lsb(pawns);
- int to, rank = from / 8;
- // Single push
- to = us == WHITE ? from + 8 : from - 8;
- if (to >= 0 && to <= 63 && !(all_occupied & (C64(1) << to))) {
- if ((us == WHITE && rank == 6) || (us == BLACK && rank == 1)) {
- for (int pr = KNIGHT; pr <= QUEEN; pr++)
- moves.push_back(Move(from, to, PAWN, -1, pr, 0));
- } else {
- moves.push_back(Move(from, to, PAWN, -1, -1, 0));
- }
- // Double push
- if ((us == WHITE && rank == 1) || (us == BLACK && rank == 6)) {
- int to2 = us == WHITE ? from + 16 : from - 16;
- if (!(all_occupied & (C64(1) << to2)))
- moves.push_back(Move(from, to2, PAWN, -1, -1, 1));
- }
- }
- // Captures
- U64 attacks = get_attacks_pawn(from, us);
- U64 targets = attacks & occupied[them];
- while (targets) {
- to = lsb(targets);
- int cap = -1;
- for (int p = 0; p < 6; p++) {
- if (GET_BIT(pieces[them][p], to)) { cap = p; break; }
- }
- if ((us == WHITE && rank == 6) || (us == BLACK && rank == 1)) {
- for (int pr = KNIGHT; pr <= QUEEN; pr++)
- moves.push_back(Move(from, to, PAWN, cap, pr, 0));
- } else {
- moves.push_back(Move(from, to, PAWN, cap, -1, 0));
- }
- POP_BIT(targets, to);
- }
- // En passant
- if (ep_square != -1 && (attacks & (C64(1) << ep_square))) {
- moves.push_back(Move(from, ep_square, PAWN, PAWN, -1, 2));
- }
- POP_BIT(pawns, from);
- }
- // Knight moves
- U64 knights = pieces[us][KNIGHT];
- while (knights) {
- int from = lsb(knights);
- U64 attacks = get_attacks_knight(from) & ~occupied[us];
- while (attacks) {
- int to = lsb(attacks);
- int cap = -1;
- if (GET_BIT(occupied[them], to)) {
- for (int p = 0; p < 6; p++) {
- if (GET_BIT(pieces[them][p], to)) { cap = p; break; }
- }
- }
- moves.push_back(Move(from, to, KNIGHT, cap, -1, 0));
- POP_BIT(attacks, to);
- }
- POP_BIT(knights, from);
- }
- // Bishop moves
- U64 bishops = pieces[us][BISHOP];
- while (bishops) {
- int from = lsb(bishops);
- U64 attacks = get_attacks_bishop(from, all_occupied) & ~occupied[us];
- while (attacks) {
- int to = lsb(attacks);
- int cap = -1;
- if (GET_BIT(occupied[them], to)) {
- for (int p = 0; p < 6; p++) {
- if (GET_BIT(pieces[them][p], to)) { cap = p; break; }
- }
- }
- moves.push_back(Move(from, to, BISHOP, cap, -1, 0));
- POP_BIT(attacks, to);
- }
- POP_BIT(bishops, from);
- }
- // Rook moves
- U64 rooks = pieces[us][ROOK];
- while (rooks) {
- int from = lsb(rooks);
- U64 attacks = get_attacks_rook(from, all_occupied) & ~occupied[us];
- while (attacks) {
- int to = lsb(attacks);
- int cap = -1;
- if (GET_BIT(occupied[them], to)) {
- for (int p = 0; p < 6; p++) {
- if (GET_BIT(pieces[them][p], to)) { cap = p; break; }
- }
- }
- moves.push_back(Move(from, to, ROOK, cap, -1, 0));
- POP_BIT(attacks, to);
- }
- POP_BIT(rooks, from);
- }
- // Queen moves
- U64 queens = pieces[us][QUEEN];
- while (queens) {
- int from = lsb(queens);
- U64 attacks = (get_attacks_rook(from, all_occupied) |
- get_attacks_bishop(from, all_occupied)) & ~occupied[us];
- while (attacks) {
- int to = lsb(attacks);
- int cap = -1;
- if (GET_BIT(occupied[them], to)) {
- for (int p = 0; p < 6; p++) {
- if (GET_BIT(pieces[them][p], to)) { cap = p; break; }
- }
- }
- moves.push_back(Move(from, to, QUEEN, cap, -1, 0));
- POP_BIT(attacks, to);
- }
- POP_BIT(queens, from);
- }
- // King moves
- U64 kings = pieces[us][KING];
- int from = lsb(kings);
- U64 attacks = get_attacks_king(from) & ~occupied[us];
- while (attacks) {
- int to = lsb(attacks);
- int cap = -1;
- if (GET_BIT(occupied[them], to)) {
- for (int p = 0; p < 6; p++) {
- if (GET_BIT(pieces[them][p], to)) { cap = p; break; }
- }
- }
- moves.push_back(Move(from, to, KING, cap, -1, 0));
- POP_BIT(attacks, to);
- }
- // Castling
- if (us == WHITE) {
- if ((castle_rights & 1) && !(all_occupied & (C64(1) << 5)) &&
- !(all_occupied & (C64(1) << 6)) &&
- !is_square_attacked(4, BLACK) && !is_square_attacked(5, BLACK))
- moves.push_back(Move(4, 6, KING, -1, -1, 3));
- if ((castle_rights & 2) && !(all_occupied & (C64(1) << 3)) &&
- !(all_occupied & (C64(1) << 2)) && !(all_occupied & (C64(1) << 1)) &&
- !is_square_attacked(4, BLACK) && !is_square_attacked(3, BLACK))
- moves.push_back(Move(4, 2, KING, -1, -1, 3));
- } else {
- if ((castle_rights & 4) && !(all_occupied & (C64(1) << 61)) &&
- !(all_occupied & (C64(1) << 62)) &&
- !is_square_attacked(60, WHITE) && !is_square_attacked(61, WHITE))
- moves.push_back(Move(60, 62, KING, -1, -1, 3));
- if ((castle_rights & 8) && !(all_occupied & (C64(1) << 59)) &&
- !(all_occupied & (C64(1) << 58)) && !(all_occupied & (C64(1) << 57)) &&
- !is_square_attacked(60, WHITE) && !is_square_attacked(59, WHITE))
- moves.push_back(Move(60, 58, KING, -1, -1, 3));
- }
- }
- void make_move(const Move& m) {
- int us = side, them = 1 - us;
- // Remove piece from origin
- POP_BIT(pieces[us][m.piece], m.from);
- // Handle capture
- if (m.captured != -1) {
- POP_BIT(pieces[them][m.captured], m.to);
- }
- // Handle en passant capture
- if (m.flags == 2) {
- int cap_sq = us == WHITE ? m.to - 8 : m.to + 8;
- POP_BIT(pieces[them][PAWN], cap_sq);
- }
- // Place piece at destination
- if (m.promoted != -1) {
- SET_BIT(pieces[us][m.promoted], m.to);
- } else {
- SET_BIT(pieces[us][m.piece], m.to);
- }
- // Handle castling
- if (m.flags == 3) {
- if (m.to == 6) { // White kingside
- POP_BIT(pieces[WHITE][ROOK], 7);
- SET_BIT(pieces[WHITE][ROOK], 5);
- } else if (m.to == 2) { // White queenside
- POP_BIT(pieces[WHITE][ROOK], 0);
- SET_BIT(pieces[WHITE][ROOK], 3);
- } else if (m.to == 62) { // Black kingside
- POP_BIT(pieces[BLACK][ROOK], 63);
- SET_BIT(pieces[BLACK][ROOK], 61);
- } else if (m.to == 58) { // Black queenside
- POP_BIT(pieces[BLACK][ROOK], 56);
- SET_BIT(pieces[BLACK][ROOK], 59);
- }
- }
- // Update en passant square
- if (m.flags == 1) {
- ep_square = us == WHITE ? m.to - 8 : m.to + 8;
- } else {
- ep_square = -1;
- }
- // Update castling rights
- if (m.piece == KING) {
- if (us == WHITE) castle_rights &= ~3;
- else castle_rights &= ~12;
- }
- if (m.piece == ROOK) {
- if (m.from == 0) castle_rights &= ~2;
- if (m.from == 7) castle_rights &= ~1;
- if (m.from == 56) castle_rights &= ~8;
- if (m.from == 63) castle_rights &= ~4;
- }
- side = them;
- update_occupancy();
- }
- };
- U64 perft(Position& pos, int depth) {
- if (depth == 0) return 1;
- vector<Move> moves;
- pos.generate_moves(moves);
- if (depth == 1) return moves.size();
- U64 nodes = 0;
- for (const Move& m : moves) {
- Position new_pos = pos;
- new_pos.make_move(m);
- // Skip if king is in check after move
- int us = 1 - new_pos.side;
- int king_sq = lsb(new_pos.pieces[us][KING]);
- if (new_pos.is_square_attacked(king_sq, new_pos.side)) continue;
- nodes += perft(new_pos, depth - 1);
- }
- return nodes;
- }
- int main() {
- Position pos;
- pos.set_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
- cout << "Chess Move Generator with Perft\n";
- cout << "================================\n\n";
- for (int depth = 1; depth <= 5; depth++) {
- auto start = chrono::high_resolution_clock::now();
- U64 nodes = perft(pos, depth);
- auto end = chrono::high_resolution_clock::now();
- auto duration = chrono::duration_cast<chrono::milliseconds>(end - start);
- cout << "Depth " << depth << ": " << nodes << " nodes";
- cout << " (" << duration.count() << " ms";
- if (duration.count() > 0) {
- cout << ", " << (nodes * 1000 / duration.count()) << " nps";
- }
- cout << ")\n";
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment