// The board. Hybrid representation: bitboards (per piece type and per color) // for fast generation/attacks, plus a piece-on-square mailbox for O(1) // "what's here?" queries. do_move/undo_move keep both in sync, along with the // Zobrist key. One Position is one game line; it is freely copyable. #ifndef CHESS_POSITION_H #define CHESS_POSITION_H #include "types.h" #include "bitboard.h" #include #include namespace chess { class Position { public: /// Parse a FEN string into a position. static Position from_fen(std::string_view fen); /// Serialize back to FEN. std::string to_fen() const; // --- mailbox queries --- Piece piece_on(Square s) const { return board[s]; } bool empty(Square s) const { return board[s] == NO_PIECE; } Color side_to_move() const { return sideToMove; } Square ep_square() const { return epSquare; } int halfmove_clock() const { return rule50; } int fullmove_number() const { return 1 + gamePly / 2; } bool can_castle(Color c, CastlingSide side) const; Square king_square(Color c) const { return lsb(pieces(c, KING)); } // --- bitboard accessors --- Bitboard pieces() const { return byColorBB[WHITE] | byColorBB[BLACK]; } Bitboard pieces(Color c) const { return byColorBB[c]; } Bitboard pieces(PieceType pt) const { return byTypeBB[pt]; } Bitboard pieces(Color c, PieceType pt) const { return byTypeBB[pt] & byColorBB[c]; } // --- attacks / checks --- Bitboard attackers_to(Square s) const { return attackers_to(s, pieces()); } Bitboard attackers_to(Square s, Bitboard occ) const; bool in_check() const; // is side_to_move in check? bool gives_check(Move m); // does m check the opponent? // --- the three you asked for --- void generate_legal(MoveList& list); // defined in movegen.cpp void do_move(Move m); void undo_move(Move m); // Legal moves for a SPECIFIED color (for mobility eval of either side). When c is not // the side to move, temporarily flips side-to-move (and clears the en-passant square, // which belongs to the other side) so generate_legal runs for c, then restores. The // Zobrist key is untouched and unused by move generation, so this leaves the position // observably unchanged. void generate_legal_for(Color c, MoveList& list) { if (sideToMove == c) { generate_legal(list); return; } Color savedSide = sideToMove; Square savedEp = epSquare; sideToMove = c; epSquare = SQ_NONE; generate_legal(list); sideToMove = savedSide; epSquare = savedEp; } // Seed prior-position keys (oldest first, excluding the current position) so // is_draw() can see game history the FEN doesn't carry. Call once, right after // from_fen and before any do_move. void seed_history(const uint64_t* priorKeys, int count); // --- freebies --- uint64_t key() const { return zkey; } bool is_draw() const; // 50-move + threefold + insufficient material void print() const; private: void put_piece(Piece pc, Square s); void remove_piece(Square s); void move_piece(Square from, Square to); bool insufficient_material() const; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; Piece board[SQUARE_NB]; Color sideToMove; int castlingRights; Square epSquare; int rule50; int gamePly; uint64_t zkey; struct Undo { int castlingRights; Square epSquare; int rule50; uint64_t key; Piece captured; }; Undo undoStack[1024]; int undoCount; uint64_t repKeys[1024]; int repCount; }; } // namespace chess #endif // CHESS_POSITION_H