#include #include "MoveGenerator.hpp" #include "Board.hpp" #include "BoardState.hpp" void MoveGenerator::generatePawnMoves(const BoardState &bs, const Square &from, MoveVec &moves) { BitBoard targets = 0; if (bs.eps.has_value()) { targets |= BitBoard::fromIndex(bs.eps.value().index()); } generatePawnMoves(bs, from, targets, moves); } void MoveGenerator::generatePawnMoves(const BoardState &bs, const Square &from, BitBoard targets, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); targets |= (*bs.pieceBBs)[Board::toIndex(!bs.turn)]; BitBoard movesBB; if (bs.turn == PieceColor::White) { movesBB = BitBoard::pawnNorthAttacks(fromBB, targets) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; movesBB |= BitBoard::pawnNorthMoves(fromBB, ~bs.occupiedBB); } else { movesBB = BitBoard::pawnSouthAttacks(fromBB, targets) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; movesBB |= BitBoard::pawnSouthMoves(fromBB, ~bs.occupiedBB); } bool isPromotion = movesBB & (Rank1 | Rank8); if (isPromotion) { generateMovesWithPromotion(bs, from, movesBB, moves); } else { generateMoves(bs, from, movesBB, PieceType::Pawn, moves); } } void MoveGenerator::generateBishopMoves(const BoardState &bs, const Square &from, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); auto movesBB = BitBoard::bishopAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; generateMoves(bs, from, movesBB, PieceType::Bishop, moves); } void MoveGenerator::generateMoves(const BoardState &bs, const Square &from, BitBoard movesBB, PieceType pt, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); while (movesBB) { auto to = Square(movesBB.pop()); auto toBB = BitBoard::fromIndex(to.index()); if (isCheck(bs, fromBB, toBB)) { continue; } moves.emplace_back(from, to, score(bs, toBB, pt)); } } void MoveGenerator::generateMovesWithPromotion(const BoardState &bs, const Square &from, BitBoard movesBB, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); while (movesBB) { auto to = Square(movesBB.pop()); auto toBB = BitBoard::fromIndex(to.index()); if (isCheck(bs, fromBB, toBB)) { continue; } for (const auto &kItem: Piece::PromotionTypes) { unsigned s = Piece::PromotionScore[Board::toIndex(kItem) - 2] + score(bs, toBB, PieceType::Pawn); moves.emplace_back(from, to, static_cast(kItem), s); } } } void MoveGenerator::generateKingMoves(const BoardState &bs, const Square &from, MoveGenerator::MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); auto movesBB = BitBoard::kingMoves(fromBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; if ((bs.cr & CastlingRights::All) != CastlingRights::None) { auto checkCR = CastlingRights::White; auto castlingRank = WhiteCastlingRank; if (bs.turn == PieceColor::Black) { checkCR = CastlingRights::Black; castlingRank = BlackCastlingRank; } checkCR &= bs.cr; if (checkCR != CastlingRights::None) { // Generate attacked squares BitBoard target = CastlingRanks | bs.occupiedBB; auto attacked = generateAttackedSquares(bs, ~target, !bs.turn); movesBB |= BitBoard::castlingMoves(fromBB & ~attacked, (*bs.pieceBBs)[Board::toIndex(PieceType::Rook)], ~bs.occupiedBB) & castlingRank; if ((checkCR & CastlingRights::KingSide) == CastlingRights::None) { movesBB &= ~GFile; } if ((checkCR & CastlingRights::QueenSide) == CastlingRights::None) { movesBB &= ~CFile; } } } generateMoves(bs, from, movesBB, PieceType::King, moves); } void MoveGenerator::generateRookMoves(const BoardState &bs, const Square &from, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); auto movesBB = BitBoard::rookAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; generateMoves(bs, from, movesBB, PieceType::Rook, moves); } void MoveGenerator::generateQueenMoves(const BoardState &bs, const Square &from, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); auto movesBB = BitBoard::queenAttacks(fromBB, ~bs.occupiedBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; generateMoves(bs, from, movesBB, PieceType::Queen, moves); } void MoveGenerator::generateKnightMoves(const BoardState &bs, const Square &from, MoveVec &moves) { auto fromBB = BitBoard::fromIndex(from.index()); auto movesBB = BitBoard::knightMoves(fromBB) & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; generateMoves(bs, from, movesBB, PieceType::Knight, moves); } BitBoard MoveGenerator::generateAttackedSquares(const BoardState &bs, BitBoard target, PieceColor opColor) { auto opponentBB = (*bs.pieceBBs)[Board::toIndex(opColor)]; BitBoard attacked = 0; // pawns if (opColor == PieceColor::White) { attacked |= BitBoard::pawnNorthAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Pawn)] & opponentBB, target); } else { attacked |= BitBoard::pawnSouthAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Pawn)] & opponentBB, target); } // knights attacked |= BitBoard::knightMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Knight)] & opponentBB) & ~target; // Bishop attacked |= BitBoard::bishopAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Bishop)] & opponentBB, target); // Rook attacked |= BitBoard::rookAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Rook)] & opponentBB, target); // Queen attacked |= BitBoard::queenAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB, target); // King attacked |= BitBoard::kingMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & opponentBB) & ~target; return attacked; } inline bool MoveGenerator::isCheck(const BoardState &bs, const BitBoard &fromBB, const BitBoard &toBB) { auto kingBB = (*bs.pieceBBs)[Board::toIndex(bs.turn)] & (*bs.pieceBBs)[Board::toIndex(PieceType::King)]; auto changeBB = (fromBB ^ toBB); auto target = bs.occupiedBB ^ changeBB; if (kingBB & fromBB) { if (toBB & bs.occupiedBB) { target ^= toBB; } if ((toBB & ~bs.occupiedBB)) { kingBB ^= changeBB; } } auto attacked = generateAttackedSquares(bs, ~target, !bs.turn); return attacked & kingBB; } inline unsigned MoveGenerator::score(const BoardState &bs, const BitBoard &toBB, const PieceType moved) { unsigned score = 0; auto opponentBB = bs.occupiedBB & ~(*bs.pieceBBs)[Board::toIndex(bs.turn)]; // Check Capture auto captureBB = toBB & opponentBB; if (captureBB) { auto captured = bs.pieceType(captureBB); score = Piece::MVV_LVA[Board::toIndex(captured)][Board::toIndex(moved)]; } return score; } unsigned MoveGenerator::Mobility(const BoardState &bs, PieceColor color) { unsigned count = 0; auto colorMask = (*bs.pieceBBs)[Board::toIndex(color)]; auto targets = (*bs.pieceBBs)[Board::toIndex(!color)]; // Pawns auto pawnsBB = (*bs.pieceBBs)[Board::toIndex(PieceType::Pawn)] & colorMask; if (color == PieceColor::White) { auto movesBB = BitBoard::pawnNorthAttacks(pawnsBB, targets) & ~colorMask; movesBB |= BitBoard::pawnNorthMoves(pawnsBB, ~bs.occupiedBB); count += movesBB.count(); } else { auto movesBB = BitBoard::pawnSouthAttacks(pawnsBB, targets) & ~colorMask; movesBB |= BitBoard::pawnSouthMoves(pawnsBB, ~bs.occupiedBB); count += movesBB.count(); } // Bishops count += (BitBoard::bishopAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Bishop)] & colorMask, ~bs.occupiedBB) & ~colorMask).count(); // Knights count += (BitBoard::knightMoves((*bs.pieceBBs)[Board::toIndex(PieceType::Knight)] & colorMask) & ~colorMask).count(); // Rook count += (BitBoard::rookAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Rook)] & colorMask, ~bs.occupiedBB) & ~colorMask).count(); // Queen count += (BitBoard::queenAttacks((*bs.pieceBBs)[Board::toIndex(PieceType::Queen)] & colorMask, ~bs.occupiedBB) & ~colorMask).count(); // King count += (BitBoard::kingMoves((*bs.pieceBBs)[Board::toIndex(PieceType::King)] & colorMask) & ~colorMask).count(); return count; }