X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=7166e8f1e8cf1aca22a216d46be5671556948ddb;hp=cf2c7bac5c7d7852624186bf45b4a2f6c95e106b;hb=351ef5c85b6d4b9c71e9da367f0be5ab6e6f8117;hpb=be5b32bb9cbf134ccf8df7c17554557e9828957d diff --git a/src/position.cpp b/src/position.cpp index cf2c7bac..7166e8f1 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -17,26 +17,18 @@ along with this program. If not, see . */ - -//// -//// Includes -//// - -#include #include #include #include -#include #include #include #include "bitcount.h" #include "movegen.h" -#include "movepick.h" #include "position.h" #include "psqtab.h" #include "rkiss.h" -#include "san.h" +#include "thread.h" #include "tt.h" #include "ucioption.h" @@ -44,11 +36,6 @@ using std::string; using std::cout; using std::endl; - -//// -//// Position's static data definitions -//// - Key Position::zobrist[2][8][64]; Key Position::zobEp[64]; Key Position::zobCastle[16]; @@ -58,7 +45,7 @@ Key Position::zobExclusion; Score Position::PieceSquareTable[16][64]; // Material values arrays, indexed by Piece -const Value Position::PieceValueMidgame[17] = { +const Value PieceValueMidgame[17] = { VALUE_ZERO, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, RookValueMidgame, QueenValueMidgame, VALUE_ZERO, @@ -67,7 +54,7 @@ const Value Position::PieceValueMidgame[17] = { RookValueMidgame, QueenValueMidgame }; -const Value Position::PieceValueEndgame[17] = { +const Value PieceValueEndgame[17] = { VALUE_ZERO, PawnValueEndgame, KnightValueEndgame, BishopValueEndgame, RookValueEndgame, QueenValueEndgame, VALUE_ZERO, @@ -76,48 +63,14 @@ const Value Position::PieceValueEndgame[17] = { RookValueEndgame, QueenValueEndgame }; -// Material values array used by SEE, indexed by PieceType -const Value Position::seeValues[] = { - VALUE_ZERO, - PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, - RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10 -}; - namespace { // Bonus for having the side to move (modified by Joona Kiiski) const Score TempoValue = make_score(48, 22); - bool isZero(char c) { return c == '0'; } - - struct PieceLetters : public std::map { - - PieceLetters() { - - operator[]('K') = WK; operator[]('k') = BK; - operator[]('Q') = WQ; operator[]('q') = BQ; - operator[]('R') = WR; operator[]('r') = BR; - operator[]('B') = WB; operator[]('b') = BB; - operator[]('N') = WN; operator[]('n') = BN; - operator[]('P') = WP; operator[]('p') = BP; - operator[](' ') = PIECE_NONE; - operator[]('.') = PIECE_NONE_DARK_SQ; - } - - char from_piece(Piece p) const { - - std::map::const_iterator it; - for (it = begin(); it != end(); ++it) - if (it->second == p) - return it->first; - - assert(false); - return 0; - } - }; - - PieceLetters pieceLetters; + // To convert a Piece to and from a FEN char + const string PieceToChar(".PNBRQK pnbrqk "); } @@ -127,16 +80,17 @@ CheckInfo::CheckInfo(const Position& pos) { Color us = pos.side_to_move(); Color them = opposite_color(us); + Square ksq = pos.king_square(them); - ksq = pos.king_square(them); dcCandidates = pos.discovered_check_candidates(us); + pinned = pos.pinned_pieces(us); - checkSq[PAWN] = pos.attacks_from(ksq, them); + checkSq[PAWN] = pos.attacks_from(ksq, them); checkSq[KNIGHT] = pos.attacks_from(ksq); checkSq[BISHOP] = pos.attacks_from(ksq); - checkSq[ROOK] = pos.attacks_from(ksq); - checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; - checkSq[KING] = EmptyBoardBB; + checkSq[ROOK] = pos.attacks_from(ksq); + checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; + checkSq[KING] = EmptyBoardBB; } @@ -175,14 +129,14 @@ void Position::detach() { /// string. This function is not very robust - make sure that input FENs are /// correct (this is assumed to be the responsibility of the GUI). -void Position::from_fen(const string& fen, bool c960) { +void Position::from_fen(const string& fen, bool isChess960) { /* A FEN string defines a particular position using only the ASCII character set. A FEN string contains six fields. The separator between fields is a space. The fields are: 1) Piece placement (from white's perspective). Each rank is described, starting with rank 8 and ending - with rank 1; within each rank, the contents of each square are described from file a through file h. + with rank 1; within each rank, the contents of each square are described from file A through file H. Following the Standard Algebraic Notation (SAN), each piece is identified by a single letter taken from the standard English names. White pieces are designated using upper-case letters ("PNBRQK") while Black take lowercase ("pnbrqk"). Blank squares are noted using digits 1 through 8 (the number @@ -205,66 +159,64 @@ void Position::from_fen(const string& fen, bool c960) { */ char token; + int hmc, fmn; + size_t p; + Square sq = SQ_A8; std::istringstream ss(fen); - Rank rank = RANK_8; - File file = FILE_A; clear(); + ss >> std::noskipws; // 1. Piece placement field - while (ss.get(token) && token != ' ') + while ((ss >> token) && !isspace(token)) { - if (isdigit(token)) + if ((p = PieceToChar.find(token)) != string::npos) { - file += File(token - '0'); // Skip the given number of files - continue; + put_piece(Piece(p), sq); + sq++; } + else if (isdigit(token)) + sq += Square(token - '0'); // Skip the given number of files else if (token == '/') - { - file = FILE_A; - rank--; - continue; - } - - if (pieceLetters.find(token) == pieceLetters.end()) + sq -= SQ_A3; // Jump back of 2 rows + else goto incorrect_fen; - - put_piece(pieceLetters[token], make_square(file, rank)); - file++; } // 2. Active color - if (!ss.get(token) || (token != 'w' && token != 'b')) + if (!(ss >> token) || (token != 'w' && token != 'b')) goto incorrect_fen; sideToMove = (token == 'w' ? WHITE : BLACK); - if (!ss.get(token) || token != ' ') + if (!(ss >> token) || !isspace(token)) goto incorrect_fen; // 3. Castling availability - while (ss.get(token) && token != ' ') - { - if (token == '-') - continue; - + while ((ss >> token) && !isspace(token)) if (!set_castling_rights(token)) goto incorrect_fen; - } - // 4. En passant square -- ignore if no capture is possible + // 4. En passant square char col, row; - if ( (ss.get(col) && (col >= 'a' && col <= 'h')) - && (ss.get(row) && (row == '3' || row == '6'))) + if ( ((ss >> col) && (col >= 'a' && col <= 'h')) + && ((ss >> row) && (row == '3' || row == '6'))) { - Square fenEpSquare = make_square(file_from_char(col), rank_from_char(row)); - Color them = opposite_color(sideToMove); + st->epSquare = make_square(File(col - 'a') + FILE_A, Rank(row - '1') + RANK_1); - if (attacks_from(fenEpSquare, them) & pieces(PAWN, sideToMove)) - st->epSquare = fenEpSquare; + // Ignore if no capture is possible + Color them = opposite_color(sideToMove); + if (!(attacks_from(st->epSquare, them) & pieces(PAWN, sideToMove))) + st->epSquare = SQ_NONE; } - // 5-6. Halfmove clock and fullmove number are not parsed + // 5. Halfmove clock + if (ss >> std::skipws >> hmc) + st->rule50 = hmc; + + // 6. Fullmove number + if (ss >> fmn) + startPosPlyCounter = (fmn - 1) * 2 + int(sideToMove == BLACK); // Various initialisations castleRightsMask[make_square(initialKFile, RANK_1)] ^= WHITE_OO | WHITE_OOO; @@ -274,7 +226,7 @@ void Position::from_fen(const string& fen, bool c960) { castleRightsMask[make_square(initialQRFile, RANK_1)] ^= WHITE_OOO; castleRightsMask[make_square(initialQRFile, RANK_8)] ^= BLACK_OOO; - isChess960 = c960; + chess960 = isChess960; find_checkers(); st->key = compute_key(); @@ -312,7 +264,7 @@ bool Position::set_castling_rights(char token) { for (Square sq = sqH; sq >= sqA; sq--) if (piece_on(sq) == rook) { - do_allow_oo(c); + set_castle_kingside(c); initialKRFile = square_file(sq); break; } @@ -322,7 +274,7 @@ bool Position::set_castling_rights(char token) { for (Square sq = sqA; sq <= sqH; sq++) if (piece_on(sq) == rook) { - do_allow_ooo(c); + set_castle_queenside(c); initialQRFile = square_file(sq); break; } @@ -332,16 +284,17 @@ bool Position::set_castling_rights(char token) { File rookFile = File(token - 'A') + FILE_A; if (rookFile < initialKFile) { - do_allow_ooo(c); + set_castle_queenside(c); initialQRFile = rookFile; } else { - do_allow_oo(c); + set_castle_kingside(c); initialKRFile = rookFile; } } - else return false; + else + return token == '-'; return true; } @@ -354,44 +307,47 @@ const string Position::to_fen() const { string fen; Square sq; - char emptyCnt = '0'; + char emptyCnt; - for (Rank rank = RANK_8; rank >= RANK_1; rank--) + for (Rank rank = RANK_8; rank >= RANK_1; rank--, fen += '/') { + emptyCnt = '0'; + for (File file = FILE_A; file <= FILE_H; file++) { sq = make_square(file, rank); if (square_is_occupied(sq)) { - fen += emptyCnt; - fen += pieceLetters.from_piece(piece_on(sq)); - emptyCnt = '0'; + if (emptyCnt != '0') + { + fen += emptyCnt; + emptyCnt = '0'; + } + fen += PieceToChar[piece_on(sq)]; } else emptyCnt++; } - fen += emptyCnt; - fen += '/'; - emptyCnt = '0'; + + if (emptyCnt != '0') + fen += emptyCnt; } - fen.erase(std::remove_if(fen.begin(), fen.end(), isZero), fen.end()); - fen.erase(--fen.end()); fen += (sideToMove == WHITE ? " w " : " b "); if (st->castleRights != CASTLES_NONE) { if (can_castle_kingside(WHITE)) - fen += isChess960 ? char(toupper(file_to_char(initialKRFile))) : 'K'; + fen += chess960 ? char(toupper(file_to_char(initialKRFile))) : 'K'; if (can_castle_queenside(WHITE)) - fen += isChess960 ? char(toupper(file_to_char(initialQRFile))) : 'Q'; + fen += chess960 ? char(toupper(file_to_char(initialQRFile))) : 'Q'; if (can_castle_kingside(BLACK)) - fen += isChess960 ? file_to_char(initialKRFile) : 'k'; + fen += chess960 ? file_to_char(initialKRFile) : 'k'; if (can_castle_queenside(BLACK)) - fen += isChess960 ? file_to_char(initialQRFile) : 'q'; + fen += chess960 ? file_to_char(initialQRFile) : 'q'; } else fen += '-'; @@ -406,14 +362,6 @@ const string Position::to_fen() const { void Position::print(Move move) const { const char* dottedLine = "\n+---+---+---+---+---+---+---+---+\n"; - static bool requestPending = false; - - // Check for reentrancy, as example when called from inside - // MovePicker that is used also here in move_to_san() - if (requestPending) - return; - - requestPending = true; if (move) { @@ -428,17 +376,16 @@ void Position::print(Move move) const { for (File file = FILE_A; file <= FILE_H; file++) { Square sq = make_square(file, rank); - char c = (color_of_piece_on(sq) == BLACK ? '=' : ' '); Piece piece = piece_on(sq); if (piece == PIECE_NONE && square_color(sq) == DARK) piece = PIECE_NONE_DARK_SQ; - cout << c << pieceLetters.from_piece(piece) << c << '|'; + char c = (color_of_piece_on(sq) == BLACK ? '=' : ' '); + cout << c << PieceToChar[piece] << c << '|'; } } cout << dottedLine << "Fen is: " << to_fen() << "\nKey is: " << st->key << endl; - requestPending = false; } @@ -512,6 +459,16 @@ Bitboard Position::attackers_to(Square s) const { | (attacks_from(s) & pieces(KING)); } +Bitboard Position::attackers_to(Square s, Bitboard occ) const { + + return (attacks_from(s, BLACK) & pieces(PAWN, WHITE)) + | (attacks_from(s, WHITE) & pieces(PAWN, BLACK)) + | (attacks_from(s) & pieces(KNIGHT)) + | (rook_attacks_bb(s, occ) & pieces(ROOK, QUEEN)) + | (bishop_attacks_bb(s, occ) & pieces(BISHOP, QUEEN)) + | (attacks_from(s) & pieces(KING)); +} + /// Position::attacks_from() computes a bitboard of all attacks /// of a given piece put in a given square. @@ -524,7 +481,7 @@ Bitboard Position::attacks_from(Piece p, Square s) const { case WB: case BB: return attacks_from(s); case WR: case BR: return attacks_from(s); case WQ: case BQ: return attacks_from(s); - default: return NonSlidingAttacksBB[p][s]; + default: return StepAttacksBB[p][s]; } } @@ -537,7 +494,7 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) { case WB: case BB: return bishop_attacks_bb(s, occ); case WR: case BR: return rook_attacks_bb(s, occ); case WQ: case BQ: return bishop_attacks_bb(s, occ) | rook_attacks_bb(s, occ); - default: return NonSlidingAttacksBB[p][s]; + default: return StepAttacksBB[p][s]; } } @@ -592,26 +549,26 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { assert(move_is_ok(m)); assert(pinned == pinned_pieces(side_to_move())); - // Castling moves are checked for legality during move generation. - if (move_is_castle(m)) - return true; + Color us = side_to_move(); + Square from = move_from(m); + + assert(color_of_piece_on(from) == us); + assert(piece_on(king_square(us)) == make_piece(us, KING)); // En passant captures are a tricky special case. Because they are // rather uncommon, we do it simply by testing whether the king is attacked // after the move is made if (move_is_ep(m)) { - Color us = side_to_move(); Color them = opposite_color(us); - Square from = move_from(m); Square to = move_to(m); Square capsq = make_square(square_file(to), square_rank(from)); Square ksq = king_square(us); Bitboard b = occupied_squares(); assert(to == ep_square()); - assert(piece_on(from) == piece_of_color_and_type(us, PAWN)); - assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN)); + assert(piece_on(from) == make_piece(us, PAWN)); + assert(piece_on(capsq) == make_piece(them, PAWN)); assert(piece_on(to) == PIECE_NONE); clear_bit(&b, from); @@ -622,16 +579,11 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { && !(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, them)); } - Color us = side_to_move(); - Square from = move_from(m); - - assert(color_of_piece_on(from) == us); - assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING)); - // If the moving piece is a king, check whether the destination - // square is attacked by the opponent. + // square is attacked by the opponent. Castling moves are checked + // for legality during move generation. if (type_of_piece_on(from) == KING) - return !(attackers_to(move_to(m)) & pieces_of_color(opposite_color(us))); + return move_is_castle(m) || !(attackers_to(move_to(m)) & pieces_of_color(opposite_color(us))); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. @@ -641,46 +593,159 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { } -/// Position::pl_move_is_evasion() tests whether a pseudo-legal move is a legal evasion +/// Position::move_is_pl_slow() takes a position and a move and tests whether +/// the move is pseudo legal. This version is not very fast and should be used +/// only in non time-critical paths. -bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const -{ - assert(is_check()); +bool Position::move_is_pl_slow(const Move m) const { - Color us = side_to_move(); + MoveStack mlist[MAX_MOVES]; + MoveStack *cur, *last; + + last = in_check() ? generate(*this, mlist) + : generate(*this, mlist); + + for (cur = mlist; cur != last; cur++) + if (cur->move == m) + return true; + + return false; +} + + +/// Fast version of Position::move_is_pl() that takes a position a move and a +/// bitboard of pinned pieces as input, and tests whether the move is pseudo legal. + +bool Position::move_is_pl(const Move m) const { + + assert(is_ok()); + + Color us = sideToMove; + Color them = opposite_color(sideToMove); Square from = move_from(m); Square to = move_to(m); + Piece pc = piece_on(from); - // King moves and en-passant captures are verified in pl_move_is_legal() - if (type_of_piece_on(from) == KING || move_is_ep(m)) - return pl_move_is_legal(m, pinned); + // Use a slower but simpler function for uncommon cases + if (move_is_special(m)) + return move_is_pl_slow(m); - Bitboard target = checkers(); - Square checksq = pop_1st_bit(&target); + // Is not a promotion, so promotion piece must be empty + if (promotion_piece_type(m) - 2 != PIECE_TYPE_NONE) + return false; - if (target) // double check ? + // If the from square is not occupied by a piece belonging to the side to + // move, the move is obviously not legal. + if (pc == PIECE_NONE || color_of_piece(pc) != us) return false; - // Our move must be a blocking evasion or a capture of the checking piece - target = squares_between(checksq, king_square(us)) | checkers(); - return bit_is_set(target, to) && pl_move_is_legal(m, pinned); -} + // The destination square cannot be occupied by a friendly piece + if (color_of_piece_on(to) == us) + return false; + + // Handle the special case of a pawn move + if (type_of_piece(pc) == PAWN) + { + // Move direction must be compatible with pawn color + int direction = to - from; + if ((us == WHITE) != (direction > 0)) + return false; + // We have already handled promotion moves, so destination + // cannot be on the 8/1th rank. + if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1) + return false; -/// Position::move_is_check() tests whether a pseudo-legal move is a check + // Proceed according to the square delta between the origin and + // destination squares. + switch (direction) + { + case DELTA_NW: + case DELTA_NE: + case DELTA_SW: + case DELTA_SE: + // Capture. The destination square must be occupied by an enemy + // piece (en passant captures was handled earlier). + if (color_of_piece_on(to) != them) + return false; -bool Position::move_is_check(Move m) const { + // From and to files must be one file apart, avoids a7h5 + if (abs(square_file(from) - square_file(to)) != 1) + return false; + break; - return move_is_check(m, CheckInfo(*this)); + case DELTA_N: + case DELTA_S: + // Pawn push. The destination square must be empty. + if (!square_is_empty(to)) + return false; + break; + + case DELTA_NN: + // Double white pawn push. The destination square must be on the fourth + // rank, and both the destination square and the square between the + // source and destination squares must be empty. + if ( square_rank(to) != RANK_4 + || !square_is_empty(to) + || !square_is_empty(from + DELTA_N)) + return false; + break; + + case DELTA_SS: + // Double black pawn push. The destination square must be on the fifth + // rank, and both the destination square and the square between the + // source and destination squares must be empty. + if ( square_rank(to) != RANK_5 + || !square_is_empty(to) + || !square_is_empty(from + DELTA_S)) + return false; + break; + + default: + return false; + } + } + else if (!bit_is_set(attacks_from(pc, from), to)) + return false; + + if (in_check()) + { + // In case of king moves under check we have to remove king so to catch + // as invalid moves like b1a1 when opposite queen is on c1. + if (type_of_piece_on(from) == KING) + { + Bitboard b = occupied_squares(); + clear_bit(&b, from); + if (attackers_to(move_to(m), b) & pieces_of_color(opposite_color(us))) + return false; + } + else + { + Bitboard target = checkers(); + Square checksq = pop_1st_bit(&target); + + if (target) // double check ? In this case a king move is required + return false; + + // Our move must be a blocking evasion or a capture of the checking piece + target = squares_between(checksq, king_square(us)) | checkers(); + if (!bit_is_set(target, move_to(m))) + return false; + } + } + + return true; } -bool Position::move_is_check(Move m, const CheckInfo& ci) const { + +/// Position::move_gives_check() tests whether a pseudo-legal move is a check + +bool Position::move_gives_check(Move m, const CheckInfo& ci) const { assert(is_ok()); assert(move_is_ok(m)); assert(ci.dcCandidates == discovered_check_candidates(side_to_move())); assert(color_of_piece_on(move_from(m)) == side_to_move()); - assert(piece_on(ci.ksq) == piece_of_color_and_type(opposite_color(side_to_move()), KING)); Square from = move_from(m); Square to = move_to(m); @@ -695,7 +760,7 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const { { // For pawn and king moves we need to verify also direction if ( (pt != PAWN && pt != KING) - || !squares_aligned(from, to, ci.ksq)) + || !squares_aligned(from, to, king_square(opposite_color(side_to_move())))) return true; } @@ -705,22 +770,23 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const { Color us = side_to_move(); Bitboard b = occupied_squares(); + Square ksq = king_square(opposite_color(us)); // Promotion with check ? if (move_is_promotion(m)) { clear_bit(&b, from); - switch (move_promotion_piece(m)) + switch (promotion_piece_type(m)) { case KNIGHT: - return bit_is_set(attacks_from(to), ci.ksq); + return bit_is_set(attacks_from(to), ksq); case BISHOP: - return bit_is_set(bishop_attacks_bb(to, b), ci.ksq); + return bit_is_set(bishop_attacks_bb(to, b), ksq); case ROOK: - return bit_is_set(rook_attacks_bb(to, b), ci.ksq); + return bit_is_set(rook_attacks_bb(to, b), ksq); case QUEEN: - return bit_is_set(queen_attacks_bb(to, b), ci.ksq); + return bit_is_set(queen_attacks_bb(to, b), ksq); default: assert(false); } @@ -736,8 +802,8 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const { clear_bit(&b, from); clear_bit(&b, capsq); set_bit(&b, to); - return (rook_attacks_bb(ci.ksq, b) & pieces(ROOK, QUEEN, us)) - ||(bishop_attacks_bb(ci.ksq, b) & pieces(BISHOP, QUEEN, us)); + return (rook_attacks_bb(ksq, b) & pieces(ROOK, QUEEN, us)) + ||(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, us)); } // Castling with check ? @@ -759,27 +825,51 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const { clear_bit(&b, rfrom); set_bit(&b, rto); set_bit(&b, kto); - return bit_is_set(rook_attacks_bb(rto, b), ci.ksq); + return bit_is_set(rook_attacks_bb(rto, b), ksq); } return false; } +/// Position::do_setup_move() makes a permanent move on the board. It should +/// be used when setting up a position on board. You can't undo the move. + +void Position::do_setup_move(Move m) { + + StateInfo newSt; + + do_move(m, newSt); + + // Reset "game ply" in case we made a non-reversible move. + // "game ply" is used for repetition detection. + if (st->rule50 == 0) + st->gamePly = 0; + + // Update the number of plies played from the starting position + startPosPlyCounter++; + + // Our StateInfo newSt is about going out of scope so copy + // its content before it disappears. + detach(); +} + + /// Position::do_move() makes a move, and saves all information necessary -/// to a StateInfo object. The move is assumed to be legal. -/// Pseudo-legal moves should be filtered out before this function is called. +/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal +/// moves should be filtered out before this function is called. void Position::do_move(Move m, StateInfo& newSt) { CheckInfo ci(*this); - do_move(m, newSt, ci, move_is_check(m, ci)); + do_move(m, newSt, ci, move_gives_check(m, ci)); } void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { assert(is_ok()); assert(move_is_ok(m)); + assert(&newSt != st); nodes++; Key key = st->key; @@ -795,8 +885,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI Value npMaterial[2]; }; - if (&newSt != st) - memcpy(&newSt, st, sizeof(ReducedStateInfo)); + memcpy(&newSt, st, sizeof(ReducedStateInfo)); newSt.previous = st; st = &newSt; @@ -833,7 +922,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI assert(color_of_piece_on(from) == us); assert(color_of_piece_on(to) == them || square_is_empty(to)); - assert(!(ep || pm) || piece == piece_of_color_and_type(us, PAWN)); + assert(!(ep || pm) || piece == make_piece(us, PAWN)); assert(!pm || relative_rank(us, to) == RANK_8); if (capture) @@ -849,13 +938,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->epSquare = SQ_NONE; } - // Update castle rights, try to shortcut a common case - int cm = castleRightsMask[from] & castleRightsMask[to]; - if (cm != ALL_CASTLES && ((cm & st->castleRights) != st->castleRights)) + // Update castle rights if needed + if ( st->castleRights != CASTLES_NONE + && (castleRightsMask[from] & castleRightsMask[to]) != ALL_CASTLES) { key ^= zobCastle[st->castleRights]; - st->castleRights &= castleRightsMask[from]; - st->castleRights &= castleRightsMask[to]; + st->castleRights &= castleRightsMask[from] & castleRightsMask[to]; key ^= zobCastle[st->castleRights]; } @@ -885,7 +973,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI // Update pawn hash key and prefetch in L1/L2 cache st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to]; - prefetchPawn(st->pawnKey, threadID); // Set en passant square, only if moved pawn can be captured if ((to ^ from) == 16) @@ -899,14 +986,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI if (pm) // promotion ? { - PieceType promotion = move_promotion_piece(m); + PieceType promotion = promotion_piece_type(m); assert(promotion >= KNIGHT && promotion <= QUEEN); // Insert promoted piece instead of pawn clear_bit(&(byTypeBB[PAWN]), to); set_bit(&(byTypeBB[promotion]), to); - board[to] = piece_of_color_and_type(us, promotion); + board[to] = make_piece(us, promotion); // Update piece counts pieceCount[us][promotion]++; @@ -938,6 +1025,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI } } + // Prefetch pawn and material hash tables + Threads[threadID].pawnTable.prefetch(st->pawnKey); + Threads[threadID].materialTable.prefetch(st->materialKey); + // Update incremental scores st->value += pst_delta(piece, from, to); @@ -964,10 +1055,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from)) { if (pt != ROOK) - st->checkersBB |= (attacks_from(ci.ksq) & pieces(ROOK, QUEEN, us)); + st->checkersBB |= (attacks_from(king_square(them)) & pieces(ROOK, QUEEN, us)); if (pt != BISHOP) - st->checkersBB |= (attacks_from(ci.ksq) & pieces(BISHOP, QUEEN, us)); + st->checkersBB |= (attacks_from(king_square(them)) & pieces(BISHOP, QUEEN, us)); } } } @@ -1000,7 +1091,7 @@ void Position::do_capture_move(Key& key, PieceType capture, Color them, Square t assert(to == st->epSquare); assert(relative_rank(opposite_color(them), to) == RANK_6); assert(piece_on(to) == PIECE_NONE); - assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN)); + assert(piece_on(capsq) == make_piece(them, PAWN)); board[capsq] = PIECE_NONE; } @@ -1064,8 +1155,8 @@ void Position::do_castle_move(Move m) { Square rfrom = move_to(m); // HACK: See comment at beginning of function Square kto, rto; - assert(piece_on(kfrom) == piece_of_color_and_type(us, KING)); - assert(piece_on(rfrom) == piece_of_color_and_type(us, ROOK)); + assert(piece_on(kfrom) == make_piece(us, KING)); + assert(piece_on(rfrom) == make_piece(us, ROOK)); // Find destination squares for king and rook if (rfrom > kfrom) // O-O @@ -1094,8 +1185,8 @@ void Position::do_castle_move(Move m) { set_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares // Update board array - Piece king = piece_of_color_and_type(us, KING); - Piece rook = piece_of_color_and_type(us, ROOK); + Piece king = make_piece(us, KING); + Piece rook = make_piece(us, ROOK); board[kfrom] = board[rfrom] = PIECE_NONE; board[kto] = king; board[rto] = rook; @@ -1171,15 +1262,15 @@ void Position::undo_move(Move m) { assert(!pm || relative_rank(us, to) == RANK_8); assert(!ep || to == st->previous->epSquare); assert(!ep || relative_rank(us, to) == RANK_6); - assert(!ep || piece_on(to) == piece_of_color_and_type(us, PAWN)); + assert(!ep || piece_on(to) == make_piece(us, PAWN)); if (pm) // promotion ? { - PieceType promotion = move_promotion_piece(m); + PieceType promotion = promotion_piece_type(m); pt = PAWN; assert(promotion >= KNIGHT && promotion <= QUEEN); - assert(piece_on(to) == piece_of_color_and_type(us, promotion)); + assert(piece_on(to) == make_piece(us, promotion)); // Replace promoted piece with a pawn clear_bit(&(byTypeBB[promotion]), to); @@ -1204,7 +1295,7 @@ void Position::undo_move(Move m) { do_move_bb(&(byTypeBB[pt]), move_bb); do_move_bb(&(byTypeBB[0]), move_bb); // HACK: byTypeBB[0] == occupied squares - board[from] = piece_of_color_and_type(us, pt); + board[from] = make_piece(us, pt); board[to] = PIECE_NONE; // Update piece list @@ -1226,7 +1317,7 @@ void Position::undo_move(Move m) { set_bit(&(byTypeBB[st->capturedType]), capsq); set_bit(&(byTypeBB[0]), capsq); - board[capsq] = piece_of_color_and_type(them, st->capturedType); + board[capsq] = make_piece(them, st->capturedType); // Update piece count pieceCount[them][st->capturedType]++; @@ -1273,8 +1364,8 @@ void Position::undo_castle_move(Move m) { rto = relative_square(us, SQ_D1); } - assert(piece_on(kto) == piece_of_color_and_type(us, KING)); - assert(piece_on(rto) == piece_of_color_and_type(us, ROOK)); + assert(piece_on(kto) == make_piece(us, KING)); + assert(piece_on(rto) == make_piece(us, ROOK)); // Remove pieces from destination squares: clear_bit(&(byColorBB[us]), kto); @@ -1294,8 +1385,8 @@ void Position::undo_castle_move(Move m) { // Update board board[rto] = board[kto] = PIECE_NONE; - board[rfrom] = piece_of_color_and_type(us, ROOK); - board[kfrom] = piece_of_color_and_type(us, KING); + board[rfrom] = make_piece(us, ROOK); + board[kfrom] = make_piece(us, KING); // Update piece lists pieceList[us][KING][index[kto]] = kfrom; @@ -1317,7 +1408,7 @@ void Position::undo_castle_move(Move m) { void Position::do_null_move(StateInfo& backupSt) { assert(is_ok()); - assert(!is_check()); + assert(!in_check()); // Back up the information necessary to undo the null move to the supplied // StateInfo object. @@ -1354,7 +1445,7 @@ void Position::do_null_move(StateInfo& backupSt) { void Position::undo_null_move() { assert(is_ok()); - assert(!is_check()); + assert(!in_check()); // Restore information from the our backup StateInfo object StateInfo* backupSt = st->previous; @@ -1377,12 +1468,6 @@ void Position::undo_null_move() { /// move, and one which takes a 'from' and a 'to' square. The function does /// not yet understand promotions captures. -int Position::see(Move m) const { - - assert(move_is_ok(m)); - return see(move_from(m), move_to(m)); -} - int Position::see_sign(Move m) const { assert(move_is_ok(m)); @@ -1393,28 +1478,31 @@ int Position::see_sign(Move m) const { // Early return if SEE cannot be negative because captured piece value // is not less then capturing one. Note that king moves always return // here because king midgame value is set to 0. - if (midgame_value_of_piece_on(to) >= midgame_value_of_piece_on(from)) + if (piece_value_midgame(piece_on(to)) >= piece_value_midgame(piece_on(from))) return 1; - return see(from, to); + return see(m); } -int Position::see(Square from, Square to) const { +int Position::see(Move m) const { + Square from, to; Bitboard occupied, attackers, stmAttackers, b; int swapList[32], slIndex = 1; PieceType capturedType, pt; Color stm; - assert(square_is_ok(from)); - assert(square_is_ok(to)); - - capturedType = type_of_piece_on(to); + assert(move_is_ok(m)); - // King cannot be recaptured - if (capturedType == KING) - return seeValues[capturedType]; + // As castle moves are implemented as capturing the rook, they have + // SEE == RookValueMidgame most of the times (unless the rook is under + // attack). + if (move_is_castle(m)) + return 0; + from = move_from(m); + to = move_to(m); + capturedType = type_of_piece_on(to); occupied = occupied_squares(); // Handle en passant moves @@ -1433,18 +1521,13 @@ int Position::see(Square from, Square to) const { // Find all attackers to the destination square, with the moving piece // removed, but possibly an X-ray attacker added behind it. clear_bit(&occupied, from); - attackers = (rook_attacks_bb(to, occupied) & pieces(ROOK, QUEEN)) - | (bishop_attacks_bb(to, occupied)& pieces(BISHOP, QUEEN)) - | (attacks_from(to) & pieces(KNIGHT)) - | (attacks_from(to) & pieces(KING)) - | (attacks_from(to, WHITE) & pieces(PAWN, BLACK)) - | (attacks_from(to, BLACK) & pieces(PAWN, WHITE)); + attackers = attackers_to(to, occupied); // If the opponent has no attackers we are finished stm = opposite_color(color_of_piece_on(from)); stmAttackers = attackers & pieces_of_color(stm); if (!stmAttackers) - return seeValues[capturedType]; + return PieceValueMidgame[capturedType]; // The destination square is defended, which makes things rather more // difficult to compute. We proceed by building up a "swap list" containing @@ -1452,7 +1535,7 @@ int Position::see(Square from, Square to) const { // destination square, where the sides alternately capture, and always // capture with the least valuable piece. After each capture, we look for // new X-ray attacks from behind the capturing piece. - swapList[0] = seeValues[capturedType]; + swapList[0] = PieceValueMidgame[capturedType]; capturedType = type_of_piece_on(from); do { @@ -1473,7 +1556,7 @@ int Position::see(Square from, Square to) const { // Add the new entry to the swap list assert(slIndex < 32); - swapList[slIndex] = -swapList[slIndex - 1] + seeValues[capturedType]; + swapList[slIndex] = -swapList[slIndex - 1] + PieceValueMidgame[capturedType]; slIndex++; // Remember the value of the capturing piece, and change the side to @@ -1533,22 +1616,6 @@ void Position::clear() { } -/// Position::reset_game_ply() simply sets gamePly to 0. It is used from the -/// UCI interface code, whenever a non-reversible move is made in a -/// 'position fen moves m1 m2 ...' command. This makes it possible -/// for the program to handle games of arbitrary length, as long as the GUI -/// handles draws by the 50 move rule correctly. - -void Position::reset_game_ply() { - - st->gamePly = 0; -} - -void Position::inc_startpos_ply_counter() { - - startPosPlyCounter++; -} - /// Position::put_piece() puts a piece on the given square of the board, /// updating the board array, pieces list, bitboards, and piece counts. @@ -1674,7 +1741,7 @@ Value Position::compute_non_pawn_material(Color c) const { /// Position::is_draw() tests whether the position is drawn by material, /// repetition, or the 50 moves rule. It does not detect stalemates, this /// must be done by the search. - +template bool Position::is_draw() const { // Draw by material? @@ -1683,100 +1750,57 @@ bool Position::is_draw() const { return true; // Draw by the 50 moves rule? - if (st->rule50 > 99 && (st->rule50 > 100 || !is_mate())) + if (st->rule50 > 99 && !is_mate()) return true; // Draw by repetition? - for (int i = 4, e = Min(Min(st->gamePly, st->rule50), st->pliesFromNull); i <= e; i += 2) - if (history[st->gamePly - i] == st->key) - return true; + if (!SkipRepetition) + for (int i = 4, e = Min(Min(st->gamePly, st->rule50), st->pliesFromNull); i <= e; i += 2) + if (history[st->gamePly - i] == st->key) + return true; return false; } +// Explicit template instantiations +template bool Position::is_draw() const; +template bool Position::is_draw() const; + /// Position::is_mate() returns true or false depending on whether the /// side to move is checkmated. bool Position::is_mate() const { - MoveStack moves[MOVES_MAX]; - return is_check() && generate_moves(*this, moves) == moves; + MoveStack moves[MAX_MOVES]; + return in_check() && generate(*this, moves) == moves; } -/// Position::has_mate_threat() tests whether the side to move is under -/// a threat of being mated in one from the current position. +/// Position::init() is a static member function which initializes at +/// startup the various arrays used to compute hash keys and the piece +/// square tables. The latter is a two-step operation: First, the white +/// halves of the tables are copied from the MgPST[][] and EgPST[][] arrays. +/// Second, the black halves of the tables are initialized by mirroring +/// and changing the sign of the corresponding white scores. -bool Position::has_mate_threat() { +void Position::init() { - MoveStack mlist[MOVES_MAX], *last, *cur; - StateInfo st1, st2; - bool mateFound = false; - - // If we are under check it's up to evasions to do the job - if (is_check()) - return false; - - // First pass the move to our opponent doing a null move - do_null_move(st1); - - // Then generate pseudo-legal moves that could give check - last = generate_non_capture_checks(*this, mlist); - last = generate_captures(*this, last); - - // Loop through the moves, and see if one of them gives mate - Bitboard pinned = pinned_pieces(sideToMove); - CheckInfo ci(*this); - for (cur = mlist; cur != last && !mateFound; cur++) - { - Move move = cur->move; - if ( !pl_move_is_legal(move, pinned) - || !move_is_check(move, ci)) - continue; - - do_move(move, st2, ci, true); - - if (is_mate()) - mateFound = true; - - undo_move(move); - } - - undo_null_move(); - return mateFound; -} - - -/// Position::init_zobrist() is a static member function which initializes at -/// startup the various arrays used to compute hash keys. - -void Position::init_zobrist() { - - int i,j, k; RKISS rk; - for (i = 0; i < 2; i++) for (j = 0; j < 8; j++) for (k = 0; k < 64; k++) - zobrist[i][j][k] = rk.rand(); + for (Color c = WHITE; c <= BLACK; c++) + for (PieceType pt = PAWN; pt <= KING; pt++) + for (Square s = SQ_A1; s <= SQ_H8; s++) + zobrist[c][pt][s] = rk.rand(); - for (i = 0; i < 64; i++) - zobEp[i] = rk.rand(); + for (Square s = SQ_A1; s <= SQ_H8; s++) + zobEp[s] = rk.rand(); - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) zobCastle[i] = rk.rand(); zobSideToMove = rk.rand(); zobExclusion = rk.rand(); -} - - -/// Position::init_piece_square_tables() initializes the piece square tables. -/// This is a two-step operation: First, the white halves of the tables are -/// copied from the MgPST[][] and EgPST[][] arrays. Second, the black halves -/// of the tables are initialized by mirroring and changing the sign of the -/// corresponding white scores. - -void Position::init_piece_square_tables() { for (Square s = SQ_A1; s <= SQ_H8; s++) for (Piece p = WP; p <= WK; p++) @@ -1788,13 +1812,15 @@ void Position::init_piece_square_tables() { } -/// Position::flipped_copy() makes a copy of the input position, but with -/// the white and black sides reversed. This is only useful for debugging, -/// especially for finding evaluation symmetry bugs. +/// Position::flip() flips position with the white and black sides reversed. This +/// is only useful for debugging especially for finding evaluation symmetry bugs. -void Position::flipped_copy(const Position& pos) { +void Position::flip() { - assert(pos.is_ok()); + assert(is_ok()); + + // Make a copy of current position before to start changing + const Position pos(*this, threadID); clear(); threadID = pos.thread(); @@ -1808,10 +1834,10 @@ void Position::flipped_copy(const Position& pos) { sideToMove = opposite_color(pos.side_to_move()); // Castling rights - if (pos.can_castle_kingside(WHITE)) do_allow_oo(BLACK); - if (pos.can_castle_queenside(WHITE)) do_allow_ooo(BLACK); - if (pos.can_castle_kingside(BLACK)) do_allow_oo(WHITE); - if (pos.can_castle_queenside(BLACK)) do_allow_ooo(WHITE); + if (pos.can_castle_kingside(WHITE)) set_castle_kingside(BLACK); + if (pos.can_castle_queenside(WHITE)) set_castle_queenside(BLACK); + if (pos.can_castle_kingside(BLACK)) set_castle_kingside(WHITE); + if (pos.can_castle_queenside(BLACK)) set_castle_queenside(WHITE); initialKFile = pos.initialKFile; initialKRFile = pos.initialKRFile; @@ -1871,7 +1897,7 @@ bool Position::is_ok(int* failedStep) const { if (failedStep) *failedStep = 1; // Side to move OK? - if (!color_is_ok(side_to_move())) + if (side_to_move() != WHITE && side_to_move() != BLACK) return false; // Are the king squares in the position correct? @@ -1885,10 +1911,10 @@ bool Position::is_ok(int* failedStep) const { // Castle files OK? if (failedStep) (*failedStep)++; - if (!file_is_ok(initialKRFile)) + if (!square_is_ok(make_square(initialKRFile, RANK_1))) return false; - if (!file_is_ok(initialQRFile)) + if (!square_is_ok(make_square(initialQRFile, RANK_1))) return false; // Do both sides have exactly one king? @@ -1991,25 +2017,26 @@ bool Position::is_ok(int* failedStep) const { if (failedStep) (*failedStep)++; if (debugPieceList) - { for (Color c = WHITE; c <= BLACK; c++) for (PieceType pt = PAWN; pt <= KING; pt++) for (int i = 0; i < pieceCount[c][pt]; i++) { - if (piece_on(piece_list(c, pt, i)) != piece_of_color_and_type(c, pt)) + if (piece_on(piece_list(c, pt, i)) != make_piece(c, pt)) return false; if (index[piece_list(c, pt, i)] != i) return false; } - } if (failedStep) (*failedStep)++; - if (debugCastleSquares) { - for (Color c = WHITE; c <= BLACK; c++) { - if (can_castle_kingside(c) && piece_on(initial_kr_square(c)) != piece_of_color_and_type(c, ROOK)) + if (debugCastleSquares) + { + for (Color c = WHITE; c <= BLACK; c++) + { + if (can_castle_kingside(c) && piece_on(initial_kr_square(c)) != make_piece(c, ROOK)) return false; - if (can_castle_queenside(c) && piece_on(initial_qr_square(c)) != piece_of_color_and_type(c, ROOK)) + + if (can_castle_queenside(c) && piece_on(initial_qr_square(c)) != make_piece(c, ROOK)) return false; } if (castleRightsMask[initial_kr_square(WHITE)] != (ALL_CASTLES ^ WHITE_OO))