diff --git a/Board.cpp b/Board.cpp index 204cee3..59d2173 100644 --- a/Board.cpp +++ b/Board.cpp @@ -10,26 +10,28 @@ Board::Board() { } void Board::setPiece(const Square &square, const Piece::Optional &piece) { + if (!piece.has_value()) + return; auto index = square.index(); - for (auto &item : mPieceBBs) { - clearIndex(item, index); + for (auto &bb : mPieceBBs) { + clearIndex(bb, index); } - setIndex(mPieceBBs[piece->typeVal() + piece->colorVal()], index); + setIndex(mPieceBBs[toIndex(piece->type())], index); + setIndex(mPieceBBs[toIndex(piece->color())], index); + setIndex(mOccupiedBB, index); } Piece::Optional Board::piece(const Square &square) const { - int i = 0; BitBoard mask = indexToBitBoard(square.index()); - for (const auto &kPieceBb : mPieceBBs) { - if (kPieceBb & mask) { - return Piece::fromValue(i); - } - - i++; + if (!(mOccupiedBB & mask)) { + return std::nullopt; } - return std::nullopt; + + auto c = pieceColor(mask); + auto t = pieceType(mask); + return Piece(c, t); } void Board::setTurn(PieceColor turn) { @@ -41,11 +43,11 @@ PieceColor Board::turn() const { } void Board::setCastlingRights(CastlingRights cr) { - mCr = cr; + mCR = cr; } CastlingRights Board::castlingRights() const { - return mCr; + return mCR; } void Board::setEnPassantSquare(const Square::Optional &square) { @@ -57,7 +59,56 @@ Square::Optional Board::enPassantSquare() const { } void Board::makeMove(const Move &move) { - (void) move; + + BitBoard fromBB = indexToBitBoard(move.from().index()); + BitBoard toBB = indexToBitBoard(move.to().index()); + BitBoard changeBB = fromBB ^ toBB; + + // If Piece is captured + if (mOccupiedBB & toBB) { + auto capturedPiece = Piece(!mTurn, pieceType(toBB)); + mPieceBBs[toIndex(capturedPiece.color())] ^= toBB; + mPieceBBs[toIndex(capturedPiece.type())] ^= toBB; + mOccupiedBB ^= fromBB; + + if (toBB & CastlingRanks) { // Check castling rights + handleCastlingRights(capturedPiece, toBB); + } + } else { + mOccupiedBB ^= changeBB; // update occupied bitboard + } + + auto movedPiece = Piece(mTurn, pieceType(fromBB)); + mPieceBBs[toIndex(movedPiece.color())] ^= changeBB; // update color bitboard + + // TODO verify if we should check if piece == Pawn + if (move.promotion().has_value()) { + mPieceBBs[toIndex(movedPiece.type())] ^= fromBB; // Remove old piece + mPieceBBs[toIndex(move.promotion().value())] ^= toBB; // Set new piece + } else { + mPieceBBs[toIndex(movedPiece.type())] ^= changeBB; // remove and set piece bitboard + } + + if ((fromBB & CastlingRanks)) { + handleCastlingRights(movedPiece, fromBB); + + if (isMoveCastling(fromBB, toBB, movedPiece)) { + BitBoard rookBB; + if (toBB & GFile) { // Kingside + rookBB = (fromBB << 3) | (fromBB << 1); + } else { + rookBB = (fromBB >> 4) | (fromBB >> 1); + } + + mPieceBBs[toIndex(PieceType::Rook)] ^= rookBB; + mPieceBBs[toIndex(movedPiece.color())] ^= rookBB; + mOccupiedBB ^= rookBB; + } + } + + + // change turn + mTurn = !mTurn; } void Board::pseudoLegalMoves(MoveVec &moves) const { @@ -69,26 +120,72 @@ void Board::pseudoLegalMovesFrom(const Square &from, (void) from; (void) moves; } +void Board::handleCastlingRights(const Piece &piece, const BitBoard &bb) { + if (piece.type() != PieceType::King && piece.type() != PieceType::Rook) { + return; + } + + auto crColor = CastlingRights::White; + auto crKingside = CastlingRights::WhiteKingside; + auto crQueenside = CastlingRights::WhiteQueenside; + + if (piece.color() == PieceColor::Black) { + crColor = CastlingRights::Black; + crKingside = CastlingRights::BlackKingside; + crQueenside = CastlingRights::BlackQueenside; + } + + if ((mCR & crColor) == CastlingRights::None) { + return; + } + + if (piece.type() == PieceType::King) { + mCR &= ~crColor; + return; + } + + if (piece.type() == PieceType::Rook) { + if (bb & (HFile & CastlingRanks)) { // Kingside + mCR &= ~crKingside; + } else if (bb & (AFile & CastlingRanks)) { // Queenside + mCR &= ~crQueenside; + } + } +} + +bool Board::isMoveCastling(const BitBoard &from, const BitBoard &to, const Piece &piece) { + if (piece.type() != PieceType::King) + return false; + + if (!(to & CastlingSquares)) { + return false; + } + + if (piece.color() == PieceColor::White) { + return (from & indexToBitBoard(E1)); + } + + return (from & indexToBitBoard(E8)); +} std::ostream &operator<<(std::ostream &os, const Board &board) { // For debugging only, performance isn't important - for (int i = 63; i >= 0; i--) { - // Get the piece for this index. Assume it exists. - auto piece = board.piece(Square::fromIndex(i).value()); + for (int i = 7; i >= 0; i--) { + int rank = i * 8; + for (int j = 0; j < 8; j++) { + // Get the piece for this index. Assume it exists. + auto piece = board.piece(Square::fromIndex(rank + j).value()); - // Print piece, otherwise '.'; - if (piece.has_value()) { - os << piece.value(); - } else { - os << '.'; - } + // Print piece, otherwise '.'; + if (piece.has_value()) { + os << piece.value(); + } else { + os << '.'; + } - // If a file is done, output newline - if (i % 8 == 0) { - os << '\n'; - } else { os << ' '; } + os << '\n'; } return os; diff --git a/Board.hpp b/Board.hpp index 3b0796a..737f4d2 100644 --- a/Board.hpp +++ b/Board.hpp @@ -10,7 +10,11 @@ #include #include -#define BB_NUM 12 // 6 pieces, 2 colors +#define BB_NUM 8 // 6 pieces, 2 colors + +enum class BoardIndex { + +}; class Board { public: @@ -39,21 +43,81 @@ public: private: BitBoard mPieceBBs[BB_NUM] = {}; + BitBoard mOccupiedBB = 0; + PieceColor mTurn = PieceColor::White; - CastlingRights mCr; + CastlingRights mCR = CastlingRights::None; unsigned mEPS = 64; - static inline void clearIndex(BitBoard &b, unsigned i) { + enum DefinedBoards : BitBoard { + AFile = 0x0101010101010101, + BFile = AFile << 1, + CFile = AFile << 2, + DFile = AFile << 3, + EFile = AFile << 4, + FFile = AFile << 5, + GFile = AFile << 6, + HFile = AFile << 7, + WhiteCastlingRank = (1ULL << A2) - 1, + BlackCastlingRank = (~1ULL << H7), + CastlingRanks = WhiteCastlingRank | BlackCastlingRank, + CastlingSquares = (WhiteCastlingRank | BlackCastlingRank) & (CFile | GFile), + }; + + void handleCastlingRights(const Piece &piece, const BitBoard &bb); + // Check if the move is castling without checking the rights or validity. + static bool isMoveCastling(const BitBoard &from, const BitBoard &to, const Piece &piece); + + static inline void clearIndex(BitBoard &b, const unsigned i) { b &= ~(1ULL << i); } - static inline void setIndex(BitBoard &b, unsigned i) { + static inline void setIndex(BitBoard &b, const unsigned i) { b |= 1ULL << i; } - static inline BitBoard indexToBitBoard(unsigned i) { + static inline BitBoard indexToBitBoard(const unsigned i) { return (1ULL << i); } + + static inline int toIndex(PieceType t) { + return static_cast(t); + } + static inline int toIndex(PieceColor c) { + return static_cast(c); + } + + inline PieceColor pieceColor(const BitBoard &mask) const { + auto color = PieceColor::White; + if (!(mPieceBBs[static_cast(color)] & mask)) { + color = !color; + } + + return color; + } + + inline PieceType pieceType(const BitBoard &mask) const { + for (int i = 2; i < BB_NUM; i++) { + if (mPieceBBs[i] & mask) { + return static_cast(i); + } + } + + // Should not happen + return static_cast(0); + } + + inline void applyMask(const BitBoard &mask) { + for (auto &item : mPieceBBs) { + item ^= mask; + } + } + + // Returns the number of trailing 0-bits in b. + // WARN: Check for 0! + static inline int getLSB(const BitBoard b) { + return __builtin_ctzll(b); + } }; std::ostream &operator<<(std::ostream &os, const Board &board); diff --git a/Piece.cpp b/Piece.cpp index 40d1ef7..480dded 100644 --- a/Piece.cpp +++ b/Piece.cpp @@ -19,18 +19,10 @@ PieceColor Piece::color() const { return mColor; } -int Piece::colorVal() const { - return static_cast(mColor); -} - PieceType Piece::type() const { return mType; } -int Piece::typeVal() const { - return static_cast(mType); -} - std::optional Piece::pieceTypeFromSymbol(char symbol) { switch (toupper(symbol)) { case 'P': return PieceType::Pawn; @@ -42,9 +34,6 @@ std::optional Piece::pieceTypeFromSymbol(char symbol) { default: return std::nullopt; } } -Piece::Optional Piece::fromValue(unsigned int value) { - return Piece(valToColor(value), valToType(value)); -} bool operator==(const Piece &lhs, const Piece &rhs) { return lhs.color() == rhs.color() && lhs.type() == rhs.type(); diff --git a/Piece.hpp b/Piece.hpp index 8e936c3..04d7d33 100644 --- a/Piece.hpp +++ b/Piece.hpp @@ -5,17 +5,17 @@ #include enum class PieceColor { - White = 0, - Black = 1, + White = 0x0, + Black = 0x1, }; enum class PieceType { - Pawn = 0b000, - Knight = 0b010, - Bishop = 0b100, - Rook = 0b110, - Queen = 0b1000, - King = 0b1010 + Pawn = 2, + Knight, + Bishop, + Rook, + Queen, + King }; class Piece { @@ -27,26 +27,15 @@ public: Piece(PieceColor color, PieceType type); static Optional fromSymbol(char symbol); - static Optional fromValue(unsigned value); static std::optional pieceTypeFromSymbol(char symbol); PieceColor color() const; PieceType type() const; - int colorVal() const; - int typeVal() const; private: const PieceColor mColor; const PieceType mType; - - static inline PieceColor valToColor(unsigned v) { - return static_cast(v % 2); - } - - static inline PieceType valToType(unsigned v) { - return static_cast(v - (v % 2)); - } }; bool operator==(const Piece &lhs, const Piece &rhs); diff --git a/Square.hpp b/Square.hpp index 591469a..21f6beb 100644 --- a/Square.hpp +++ b/Square.hpp @@ -5,6 +5,17 @@ #include #include +enum SquareIndex : unsigned { + A1, B1, C1, D1, E1, F1, G1, H1, + A2, B2, C2, D2, E2, F2, G2, H2, + A3, B3, C3, D3, E3, F3, G3, H3, + A4, B4, C4, D4, E4, F4, G4, H4, + A5, B5, C5, D5, E5, F5, G5, H5, + A6, B6, C6, D6, E6, F6, G6, H6, + A7, B7, C7, D7, E7, F7, G7, H7, + A8, B8, C8, D8, E8, F8, G8, H8 +}; + class Square { public: