X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=8f70b5dfd3ab3af4c644072396622c75257ce381;hp=e3bf4aee3e17863c55ca862ff15607443d395512;hb=b21a5e2f0638a55daeaa98ba95afc6c016ea0b6e;hpb=54f1c383d36f461a740eeaa93856b408e8d3faa3 diff --git a/src/position.cpp b/src/position.cpp index e3bf4aee..8f70b5df 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -29,6 +28,7 @@ #include "position.h" #include "psqtab.h" #include "rkiss.h" +#include "thread.h" #include "tt.h" #include "ucioption.h" @@ -76,33 +76,8 @@ namespace { // Bonus for having the side to move (modified by Joona Kiiski) const Score TempoValue = make_score(48, 22); - 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 "); } @@ -191,17 +166,19 @@ void Position::from_fen(const string& fen, bool isChess960) { char token; int hmc, fmn; - std::istringstream ss(fen); + size_t p; Square sq = SQ_A8; + std::istringstream ss(fen); clear(); + ss >> std::noskipws; // 1. Piece placement field - while (ss.get(token) && token != ' ') + while ((ss >> token) && !isspace(token)) { - if (pieceLetters.find(token) != pieceLetters.end()) + if ((p = PieceToChar.find(token)) != string::npos) { - put_piece(pieceLetters[token], sq); + put_piece(Piece(p), sq); sq++; } else if (isdigit(token)) @@ -213,23 +190,23 @@ void Position::from_fen(const string& fen, bool isChess960) { } // 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 != ' ') + while ((ss >> token) && !isspace(token)) if (!set_castling_rights(token)) goto incorrect_fen; // 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'))) { st->epSquare = make_square(file_from_char(col), rank_from_char(row)); @@ -240,7 +217,7 @@ void Position::from_fen(const string& fen, bool isChess960) { } // 5. Halfmove clock - if (ss >> hmc) + if (ss >> std::skipws >> hmc) st->rule50 = hmc; // 6. Fullmove number @@ -293,7 +270,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; } @@ -303,7 +280,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; } @@ -313,12 +290,12 @@ 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; } } @@ -336,10 +313,12 @@ const string Position::to_fen() const { string fen; Square sq; - char emptyCnt = '0'; + char emptyCnt; 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); @@ -351,16 +330,13 @@ const string Position::to_fen() const { fen += emptyCnt; emptyCnt = '0'; } - fen += pieceLetters.from_piece(piece_on(sq)); + fen += PieceToChar[piece_on(sq)]; } else emptyCnt++; } if (emptyCnt != '0') - { fen += emptyCnt; - emptyCnt = '0'; - } } fen += (sideToMove == WHITE ? " w " : " b "); @@ -412,7 +388,7 @@ void Position::print(Move move) const { piece = PIECE_NONE_DARK_SQ; char c = (color_of_piece_on(sq) == BLACK ? '=' : ' '); - cout << c << pieceLetters.from_piece(piece) << c << '|'; + cout << c << PieceToChar[piece] << c << '|'; } } cout << dottedLine << "Fen is: " << to_fen() << "\nKey is: " << st->key << endl; @@ -489,6 +465,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. @@ -569,18 +555,18 @@ 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); @@ -599,16 +585,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)) == make_piece(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. @@ -618,55 +599,32 @@ 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(); - Square from = move_from(m); - Square to = move_to(m); - - // 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); - - Bitboard target = checkers(); - Square checksq = pop_1st_bit(&target); - - if (target) // double check ? - 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); -} + MoveStack mlist[MAX_MOVES]; + MoveStack *cur, *last; -/// Position::move_is_legal() takes a position and a (not necessarily pseudo-legal) -/// move and tests whether the move is legal. This version is not very fast and -/// should be used only in non time-critical paths. + last = in_check() ? generate(*this, mlist) + : generate(*this, mlist); -bool Position::move_is_legal(const Move m) const { - - MoveStack mlist[MOVES_MAX]; - MoveStack *cur, *last = generate(*this, mlist); - - for (cur = mlist; cur != last; cur++) + for (cur = mlist; cur != last; cur++) if (cur->move == m) - return pl_move_is_legal(m, pinned_pieces(sideToMove)); + return true; return false; } -/// Fast version of Position::move_is_legal() that takes a position a move and -/// a bitboard of pinned pieces as input, and tests whether the move is legal. +/// 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_legal(const Move m, Bitboard pinned) const { +bool Position::move_is_pl(const Move m) const { assert(is_ok()); - assert(pinned == pinned_pieces(sideToMove)); Color us = sideToMove; Color them = opposite_color(sideToMove); @@ -676,11 +634,15 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const { // Use a slower but simpler function for uncommon cases if (move_is_special(m)) - return move_is_legal(m); + return move_is_pl_slow(m); + + // Is not a promotion, so promotion piece must be empty + if (promotion_piece_type(m) - 2 != PIECE_TYPE_NONE) + return false; // If the from square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. - if (color_of_piece(pc) != us) + if (pc == PIECE_NONE || color_of_piece(pc) != us) return false; // The destination square cannot be occupied by a friendly piece @@ -710,16 +672,20 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const { 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; - break; + if (color_of_piece_on(to) != them) + return false; + + // From and to files must be one file apart, avoids a7h5 + if (abs(square_file(from) - square_file(to)) != 1) + return false; + break; case DELTA_N: case DELTA_S: // Pawn push. The destination square must be empty. - if (!square_is_empty(to)) - return false; - break; + if (!square_is_empty(to)) + return false; + break; case DELTA_NN: // Double white pawn push. The destination square must be on the fourth @@ -729,17 +695,17 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const { || !square_is_empty(to) || !square_is_empty(from + DELTA_N)) return false; - break; + 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; + if ( square_rank(to) != RANK_5 + || !square_is_empty(to) + || !square_is_empty(from + DELTA_S)) + return false; + break; default: return false; @@ -748,19 +714,44 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const { else if (!bit_is_set(attacks_from(pc, from), to)) return false; - // The move is pseudo-legal, check if it is also legal - return is_check() ? pl_move_is_evasion(m, pinned) : pl_move_is_legal(m, pinned); + 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; } -/// Position::move_is_check() tests whether a pseudo-legal move is a check +/// Position::move_gives_check() tests whether a pseudo-legal move is a check -bool Position::move_is_check(Move m) const { +bool Position::move_gives_check(Move m) const { - return move_is_check(m, CheckInfo(*this)); + return move_gives_check(m, CheckInfo(*this)); } -bool Position::move_is_check(Move m, const CheckInfo& ci) const { +bool Position::move_gives_check(Move m, const CheckInfo& ci) const { assert(is_ok()); assert(move_is_ok(m)); @@ -797,7 +788,7 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const { { 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); @@ -852,9 +843,8 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const { } -/// 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. +/// 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) { @@ -871,18 +861,19 @@ void Position::do_setup_move(Move m) { startPosPlyCounter++; // Our StateInfo newSt is about going out of scope so copy - // its content inside pos before it disappears. + // 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) { @@ -958,13 +949,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]; } @@ -1007,7 +997,7 @@ 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); @@ -1047,7 +1037,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI } // Prefetch pawn and material hash tables - prefetchTables(st->pawnKey, st->materialKey, threadID); + Threads[threadID].pawnTable.prefetch(st->pawnKey); + Threads[threadID].materialTable.prefetch(st->materialKey); // Update incremental scores st->value += pst_delta(piece, from, to); @@ -1286,7 +1277,7 @@ void Position::undo_move(Move m) { if (pm) // promotion ? { - PieceType promotion = move_promotion_piece(m); + PieceType promotion = promotion_piece_type(m); pt = PAWN; assert(promotion >= KNIGHT && promotion <= QUEEN); @@ -1428,7 +1419,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. @@ -1465,7 +1456,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; @@ -1488,12 +1479,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)); @@ -1507,25 +1492,28 @@ int Position::see_sign(Move m) const { if (midgame_value_of_piece_on(to) >= midgame_value_of_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 @@ -1544,12 +1532,7 @@ 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)); @@ -1769,7 +1752,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? @@ -1782,21 +1765,26 @@ bool Position::is_draw() const { 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(*this, moves) == moves; + MoveStack moves[MAX_MOVES]; + return in_check() && generate(*this, moves) == moves; } @@ -1840,13 +1828,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::flip() { -void Position::flipped_copy(const Position& pos) { + assert(is_ok()); - assert(pos.is_ok()); + // Make a copy of current position before to start changing + const Position pos(*this, threadID); clear(); threadID = pos.thread(); @@ -1860,10 +1850,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;