X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=83a83c1887eddbfa7731c1f1b537a5d995822fba;hp=71a3074608ee75fda8996486ddba03762dc4b603;hb=1d2247aea39074d8ecea4a5ce81b1cf85165538e;hpb=038235ba3541087e2db969a0a4bdfd5fc2a42b44 diff --git a/src/position.cpp b/src/position.cpp index 71a30746..83a83c18 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -31,6 +31,7 @@ #include "movepick.h" #include "position.h" #include "psqtab.h" +#include "san.h" #include "ucioption.h" @@ -38,6 +39,8 @@ //// Variables //// +extern SearchStack EmptySearchStack; + int Position::castleRightsMask[64]; Key Position::zobrist[2][8][64]; @@ -49,6 +52,7 @@ Key Position::zobSideToMove; Value Position::MgPieceSquareTable[16][64]; Value Position::EgPieceSquareTable[16][64]; +static bool RequestPending = false; //// //// Functions @@ -56,11 +60,11 @@ Value Position::EgPieceSquareTable[16][64]; /// Constructors -Position::Position(const Position &pos) { +Position::Position(const Position& pos) { copy(pos); } -Position::Position(const std::string &fen) { +Position::Position(const std::string& fen) { from_fen(fen); } @@ -69,7 +73,7 @@ Position::Position(const std::string &fen) { /// 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 std::string &fen) { +void Position::from_fen(const std::string& fen) { static const std::string pieceLetters = "KQRBNPkqrbnp"; static const Piece pieces[] = { WK, WQ, WR, WB, WN, WP, BK, BQ, BR, BB, BN, BP }; @@ -242,7 +246,7 @@ const std::string Position::to_fen() const { fen += (rank > RANK_1 ? '/' : ' '); } - fen += (sideToMove == WHITE ? 'w' : 'b') + ' '; + fen += (sideToMove == WHITE ? "w " : "b "); if (castleRights != NO_CASTLES) { if (can_castle_kingside(WHITE)) fen += 'K'; @@ -263,29 +267,45 @@ const std::string Position::to_fen() const { /// Position::print() prints an ASCII representation of the position to -/// the standard output. - -void Position::print() const { - char pieceStrings[][8] = - {"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ", - "| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K=" - }; - - for(Rank rank = RANK_8; rank >= RANK_1; rank--) { - std::cout << "+---+---+---+---+---+---+---+---+\n"; - for(File file = FILE_A; file <= FILE_H; file++) { - Square sq = make_square(file, rank); - Piece piece = piece_on(sq); - if(piece == EMPTY) - std::cout << ((square_color(sq) == WHITE)? "| " : "| . "); - else - std::cout << pieceStrings[piece]; - } - std::cout << "|\n"; +/// the standard output. If a move is given then also the san is print. + +void Position::print(Move m) const { + + static const std::string pieceLetters = " PNBRQK PNBRQK ."; + + // Check for reentrancy, as example when called from inside + // MovePicker that is used also here in move_to_san() + if (RequestPending) + return; + + RequestPending = true; + + std::cout << std::endl; + if (m != MOVE_NONE) + { + std::string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : ""); + std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl; + } + for (Rank rank = RANK_8; rank >= RANK_1; rank--) + { + std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; + for (File file = FILE_A; file <= FILE_H; file++) + { + Square sq = make_square(file, rank); + Piece piece = piece_on(sq); + if (piece == EMPTY && square_color(sq) == WHITE) + piece = NO_PIECE; + + char col = (color_of_piece_on(sq) == BLACK ? '=' : ' '); + std::cout << '|' << col << pieceLetters[piece] << col; + } + std::cout << '|' << std::endl; } - std::cout << "+---+---+---+---+---+---+---+---+\n"; - std::cout << to_fen() << std::endl; - std::cout << key << std::endl; + std::cout << "+---+---+---+---+---+---+---+---+" << std::endl + << "Fen is: " << to_fen() << std::endl + << "Key is: " << key << std::endl; + + RequestPending = false; } @@ -359,19 +379,6 @@ Bitboard Position::hidden_checks(Color c, Square ksq) const { } -/// Position::square_is_attacked() checks whether the given side attacks the -/// given square. - -bool Position::square_is_attacked(Square s, Color c) const { - - return (pawn_attacks(opposite_color(c), s) & pawns(c)) - || (piece_attacks(s) & knights(c)) - || (piece_attacks(s) & kings(c)) - || (piece_attacks(s) & rooks_and_queens(c)) - || (piece_attacks(s) & bishops_and_queens(c)); -} - - /// Position::attacks_to() computes a bitboard containing all pieces which /// attacks a given square. There are two versions of this function: One /// which finds attackers of both colors, and one which only finds the @@ -387,12 +394,6 @@ Bitboard Position::attacks_to(Square s) const { | (piece_attacks(s) & pieces_of_type(KING)); } -Bitboard Position::attacks_to(Square s, Color c) const { - - return attacks_to(s) & pieces_of_color(c); -} - - /// Position::piece_attacks_square() tests whether the piece on square f /// attacks square t. @@ -410,6 +411,7 @@ bool Position::piece_attacks_square(Square f, Square t) const { case WR: case BR: return piece_attacks_square(f, t); case WQ: case BQ: return piece_attacks_square(f, t); case WK: case BK: return piece_attacks_square(f, t); + default: break; } return false; } @@ -437,6 +439,7 @@ bool Position::move_attacks_square(Move m, Square s) const { case WR: case BR: return piece_attacks_square(t, s); case WQ: case BQ: return piece_attacks_square(t, s); case WK: case BK: return piece_attacks_square(t, s); + default: break; } return false; } @@ -643,6 +646,9 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const { return bit_is_set(rook_attacks_bb(rto, b), ksq); } return false; + + default: // NO_PIECE_TYPE + break; } assert(false); return false; @@ -650,10 +656,12 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const { /// Position::move_is_capture() tests whether a move from the current -/// position is a capture. +/// position is a capture. Move must not be MOVE_NONE. bool Position::move_is_capture(Move m) const { + assert(m != MOVE_NONE); + return ( !square_is_empty(move_to(m)) && (color_of_piece_on(move_to(m)) == opposite_color(side_to_move())) ) @@ -702,6 +710,22 @@ void Position::restore(const UndoInfo& u) { // u.capture is restored in undo_move() } +template +inline void Position::update_checkers(Bitboard* pCheckersBB, Square ksq, Square from, Square to, Bitboard dcCandidates) { + + if (Piece != KING && bit_is_set(piece_attacks(ksq), to)) + set_bit(pCheckersBB, to); + + if (Piece != QUEEN && bit_is_set(dcCandidates, from)) + { + if (Piece != ROOK) + (*pCheckersBB) |= (piece_attacks(ksq) & rooks_and_queens(side_to_move())); + + if (Piece != BISHOP) + (*pCheckersBB) |= (piece_attacks(ksq) & bishops_and_queens(side_to_move())); + } +} + /// Position::do_move() makes a move, and backs up all information necessary /// to undo the move to an UndoInfo object. The move is assumed to be legal. /// Pseudo-legal moves should be filtered out before this function is called. @@ -781,33 +805,34 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { if (piece == KING) kingSquare[us] = to; - // If the move was a double pawn push, set the en passant square. - // This code is a bit ugly right now, and should be cleaned up later. - // FIXME + // Reset en passant square if (epSquare != SQ_NONE) { key ^= zobEp[epSquare]; epSquare = SQ_NONE; } + + // If the moving piece was a pawn do some special extra work if (piece == PAWN) { + // Reset rule 50 draw counter + rule50 = 0; + + // Update pawn hash key + pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to]; + + // Set en passant square, only if moved pawn can be captured if (abs(int(to) - int(from)) == 16) { - if( ( us == WHITE - && (pawn_attacks(WHITE, from + DELTA_N) & pawns(BLACK))) - || ( us == BLACK - && (pawn_attacks(BLACK, from + DELTA_S) & pawns(WHITE)))) + if ( (us == WHITE && (pawn_attacks(WHITE, from + DELTA_N) & pawns(BLACK))) + || (us == BLACK && (pawn_attacks(BLACK, from + DELTA_S) & pawns(WHITE)))) { epSquare = Square((int(from) + int(to)) / 2); key ^= zobEp[epSquare]; } } - // Reset rule 50 draw counter - rule50 = 0; - - // Update pawn hash key - pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to]; } + // Update piece lists pieceList[us][piece][index[from]] = to; index[to] = index[from]; @@ -818,7 +843,7 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { castleRights &= castleRightsMask[to]; key ^= zobCastle[castleRights]; - // Update checkers bitboard + // Update checkers bitboard, piece must be already moved checkersBB = EmptyBoardBB; Square ksq = king_square(them); switch (piece) @@ -833,39 +858,23 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { break; case KNIGHT: - if (bit_is_set(piece_attacks(ksq), to)) - set_bit(&checkersBB, to); - - if (bit_is_set(dcCandidates, from)) - checkersBB |= ( (piece_attacks(ksq) & rooks_and_queens(us)) - |(piece_attacks(ksq) & bishops_and_queens(us))); + update_checkers(&checkersBB, ksq, from, to, dcCandidates); break; case BISHOP: - if (bit_is_set(piece_attacks(ksq), to)) - set_bit(&checkersBB, to); - - if (bit_is_set(dcCandidates, from)) - checkersBB |= (piece_attacks(ksq) & rooks_and_queens(us)); + update_checkers(&checkersBB, ksq, from, to, dcCandidates); break; case ROOK: - if (bit_is_set(piece_attacks(ksq), to)) - set_bit(&checkersBB, to); - - if (bit_is_set(dcCandidates, from)) - checkersBB |= (piece_attacks(ksq) & bishops_and_queens(us)); + update_checkers(&checkersBB, ksq, from, to, dcCandidates); break; case QUEEN: - if (bit_is_set(piece_attacks(ksq), to)) - set_bit(&checkersBB, to); + update_checkers(&checkersBB, ksq, from, to, dcCandidates); break; case KING: - if (bit_is_set(dcCandidates, from)) - checkersBB |= ( (piece_attacks(ksq) & rooks_and_queens(us)) - |(piece_attacks(ksq) & bishops_and_queens(us))); + update_checkers(&checkersBB, ksq, from, to, dcCandidates); break; default: @@ -885,7 +894,6 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { assert(is_ok()); } - /// Position::do_capture_move() is a private method used to update captured /// piece info. It is called from the main Position::do_move function. @@ -1113,7 +1121,7 @@ void Position::do_promotion_move(Move m, UndoInfo &u) { castleRights &= castleRightsMask[to]; key ^= zobCastle[castleRights]; - // Reset rule 50 counter + // Reset rule 50 counter rule50 = 0; // Update checkers BB @@ -1555,10 +1563,16 @@ void Position::undo_null_move(const UndoInfo &u) { /// Position::see() is a static exchange evaluator: It tries to estimate the -/// material gain or loss resulting from a move. There are two versions of -/// this function: One which takes a move as input, and one which takes a -/// 'from' and a 'to' square. The function does not yet understand promotions -/// or en passant captures. +/// material gain or loss resulting from a move. There are three versions of +/// this function: One which takes a destination square as input, one takes a +/// move, and one which takes a 'from' and a 'to' square. The function does +/// not yet understand promotions or en passant captures. + +int Position::see(Square to) const { + + assert(square_is_ok(to)); + return see(SQ_NONE, to); +} int Position::see(Move m) const { @@ -1568,18 +1582,22 @@ int Position::see(Move m) const { int Position::see(Square from, Square to) const { - // Approximate material values, with pawn = 1 + // Material values static const int seeValues[18] = { - 0, 1, 3, 3, 5, 10, 100, 0, 0, 1, 3, 3, 5, 10, 100, 0, 0, 0 + 0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, + RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0, + 0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, + RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0, + 0, 0 }; Bitboard attackers, occ, b; - assert(square_is_ok(from)); + assert(square_is_ok(from) || from == SQ_NONE); assert(square_is_ok(to)); // Initialize colors - Color us = color_of_piece_on(from); + Color us = (from != SQ_NONE ? color_of_piece_on(from) : opposite_color(color_of_piece_on(to))); Color them = opposite_color(us); // Initialize pieces @@ -1589,15 +1607,49 @@ 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. occ = occupied_squares(); - clear_bit(&occ, from); - attackers = (rook_attacks_bb(to, occ) & rooks_and_queens()) - | (bishop_attacks_bb(to, occ) & bishops_and_queens()) - | (piece_attacks(to) & knights()) - | (piece_attacks(to) & kings()) - | (pawn_attacks(WHITE, to) & pawns(BLACK)) - | (pawn_attacks(BLACK, to) & pawns(WHITE)); - - // If the opponent has no attackers, we are finished + + // Handle enpassant moves + if (ep_square() == to && type_of_piece_on(from) == PAWN) + { + assert(capture == EMPTY); + + Square capQq = (side_to_move() == WHITE)? (to - DELTA_N) : (to - DELTA_S); + capture = piece_on(capQq); + + assert(type_of_piece_on(capQq) == PAWN); + + // Remove the captured pawn + clear_bit(&occ, capQq); + } + + while (true) + { + clear_bit(&occ, from); + attackers = (rook_attacks_bb(to, occ) & rooks_and_queens()) + | (bishop_attacks_bb(to, occ) & bishops_and_queens()) + | (piece_attacks(to) & knights()) + | (piece_attacks(to) & kings()) + | (pawn_attacks(WHITE, to) & pawns(BLACK)) + | (pawn_attacks(BLACK, to) & pawns(WHITE)); + + if (from != SQ_NONE) + break; + + // If we don't have any attacker we are finished + if ((attackers & pieces_of_color(us)) == EmptyBoardBB) + return 0; + + // Locate the least valuable attacker to the destination square + // and use it to initialize from square. + PieceType pt; + for (pt = PAWN; !(attackers & pieces_of_color_and_type(us, pt)); pt++) + assert(pt < KING); + + from = first_1(attackers & pieces_of_color_and_type(us, pt)); + piece = piece_on(from); + } + + // If the opponent has no attackers we are finished if ((attackers & pieces_of_color(them)) == EmptyBoardBB) return seeValues[capture]; @@ -1896,12 +1948,11 @@ Value Position::compute_non_pawn_material(Color c) const { /// side to move is checkmated. Note that this function is currently very /// slow, and shouldn't be used frequently inside the search. -bool Position::is_mate() { +bool Position::is_mate() const { if (is_check()) { - MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE, - MOVE_NONE, MOVE_NONE, Depth(0)); + MovePicker mp = MovePicker(*this, false, MOVE_NONE, EmptySearchStack, Depth(0)); return mp.get_next_move() == MOVE_NONE; } return false;