X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=a999b4c6f0e5ee5a67981184e8bd2e89cf52cb90;hp=5e0924830740a81a4b09262ac433b9f425d41fbd;hb=5f142ec2098531bac1780f67579dedbd12d18dcc;hpb=74f1efee263aadcceb1df716ebd87776b932238a diff --git a/src/position.cpp b/src/position.cpp index 5e092483..a999b4c6 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 }; @@ -114,7 +118,7 @@ void Position::from_fen(const std::string &fen) { } sideToMove = (fen[i] == 'w' ? WHITE : BLACK); - // Castling rights: + // Castling rights i++; if (fen[i] != ' ') { @@ -124,8 +128,10 @@ void Position::from_fen(const std::string &fen) { i++; while(strchr("KQkqabcdefghABCDEFGH-", fen[i])) { - if(fen[i] == '-') { - i++; break; + if (fen[i] == '-') + { + i++; + break; } else if(fen[i] == 'K') allow_oo(WHITE); else if(fen[i] == 'Q') allow_ooo(WHITE); @@ -242,7 +248,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 +269,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; } @@ -301,29 +323,42 @@ void Position::copy(const Position &pos) { /// king) pieces for the given color. Bitboard Position::pinned_pieces(Color c) const { + if (pinned[c] != ~EmptyBoardBB) + return pinned[c]; + + Bitboard p1, p2; Square ksq = king_square(c); - return hidden_checks(c, ksq) | hidden_checks(c, ksq); + pinned[c] = hidden_checks(c, ksq, p1) | hidden_checks(c, ksq, p2); + pinners[c] = p1 | p2; + return pinned[c]; } +Bitboard Position::pinned_pieces(Color c, Bitboard& p) const { -/// Position:discovered_check_candidates() returns a bitboard containing all -/// pieces for the given side which are candidates for giving a discovered -/// check. The code is almost the same as the function for finding pinned -/// pieces. + if (pinned[c] == ~EmptyBoardBB) + pinned_pieces(c); + + p = pinners[c]; + return pinned[c]; +} Bitboard Position::discovered_check_candidates(Color c) const { + if (dcCandidates[c] != ~EmptyBoardBB) + return dcCandidates[c]; + + Bitboard dummy; Square ksq = king_square(opposite_color(c)); - return hidden_checks(c, ksq) | hidden_checks(c, ksq); + dcCandidates[c] = hidden_checks(c, ksq, dummy) | hidden_checks(c, ksq, dummy); + return dcCandidates[c]; } - /// Position:hidden_checks<>() returns a bitboard of all pinned (against the /// king) pieces for the given color and for the given pinner type. Or, when /// template parameter FindPinned is false, the pinned pieces of opposite color /// that are, indeed, the pieces candidate for a discovery check. template -Bitboard Position::hidden_checks(Color c, Square ksq) const { +Bitboard Position::hidden_checks(Color c, Square ksq, Bitboard& pinners) const { Square s; Bitboard sliders, result = EmptyBoardBB; @@ -340,7 +375,7 @@ Bitboard Position::hidden_checks(Color c, Square ksq) const { // Pinners are sliders, not checkers, that give check when // candidate pinned are removed. - Bitboard pinners = (FindPinned ? sliders & ~checkersBB : sliders); + pinners = (FindPinned ? sliders & ~checkersBB : sliders); if (Piece == ROOK) pinners &= rook_attacks_bb(ksq, occupied_squares() ^ candidate_pinned); @@ -349,26 +384,17 @@ Bitboard Position::hidden_checks(Color c, Square ksq) const { // Finally for each pinner find the corresponding pinned piece (if same color of king) // or discovery checker (if opposite color) among the candidates. - while (pinners) + Bitboard p = pinners; + while (p) { - s = pop_1st_bit(&pinners); + s = pop_1st_bit(&p); result |= (squares_between(s, ksq) & candidate_pinned); } } - return result; -} - - -/// Position::square_is_attacked() checks whether the given side attacks the -/// given square. - -bool Position::square_is_attacked(Square s, Color c) const { + else + pinners = EmptyBoardBB; - 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)); + return result; } @@ -387,21 +413,15 @@ 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. -bool Position::piece_attacks_square(Square f, Square t) const { +bool Position::piece_attacks_square(Piece p, Square f, Square t) const { assert(square_is_ok(f)); assert(square_is_ok(t)); - switch (piece_on(f)) + switch (p) { case WP: return pawn_attacks_square(WHITE, f, t); case BP: return pawn_attacks_square(BLACK, f, t); @@ -410,14 +430,14 @@ 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; } /// Position::move_attacks_square() tests whether a move from the current -/// position attacks a given square. Only attacks by the moving piece are -/// considered; the function does not handle X-ray attacks. +/// position attacks a given square. bool Position::move_attacks_square(Move m, Square s) const { @@ -428,30 +448,33 @@ bool Position::move_attacks_square(Move m, Square s) const { assert(square_is_occupied(f)); - switch (piece_on(f)) - { - case WP: return pawn_attacks_square(WHITE, t, s); - case BP: return pawn_attacks_square(BLACK, t, s); - case WN: case BN: return piece_attacks_square(t, s); - case WB: case BB: return piece_attacks_square(t, s); - 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: assert(false); - } - return false; + if (piece_attacks_square(piece_on(f), t, s)) + return true; + + // Move the piece and scan for X-ray attacks behind it + Bitboard occ = occupied_squares(); + Color us = color_of_piece_on(f); + clear_bit(&occ, f); + set_bit(&occ, t); + Bitboard xray = ( (rook_attacks_bb(s, occ) & rooks_and_queens()) + |(bishop_attacks_bb(s, occ) & bishops_and_queens())) & pieces_of_color(us); + + // If we have attacks we need to verify that are caused by our move + // and are not already existent ones. + return xray && (xray ^ (xray & piece_attacks(s))); } /// Position::find_checkers() computes the checkersBB bitboard, which /// contains a nonzero bit for each checking piece (0, 1 or 2). It /// currently works by calling Position::attacks_to, which is probably -/// inefficient. Consider rewriting this function to use the last move +/// inefficient. Consider rewriting this function to use the last move /// played, like in non-bitboard versions of Glaurung. void Position::find_checkers() { - checkersBB = attacks_to(king_square(side_to_move()),opposite_color(side_to_move())); + Color us = side_to_move(); + checkersBB = attacks_to(king_square(us), opposite_color(us)); } @@ -468,9 +491,6 @@ bool Position::pl_move_is_legal(Move m) const { bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { - Color us, them; - Square ksq, from; - assert(is_ok()); assert(move_is_ok(m)); assert(pinned == pinned_pieces(side_to_move())); @@ -484,13 +504,13 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { if (move_is_castle(m)) return true; - us = side_to_move(); - them = opposite_color(us); - from = move_from(m); - ksq = king_square(us); + Color us = side_to_move(); + Color them = opposite_color(us); + Square from = move_from(m); + Square ksq = king_square(us); assert(color_of_piece_on(from) == us); - assert(piece_on(ksq) == king_of_color(us)); + assert(piece_on(ksq) == piece_of_color_and_type(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 @@ -502,8 +522,8 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { Bitboard b = occupied_squares(); assert(to == ep_square()); - assert(piece_on(from) == pawn_of_color(us)); - assert(piece_on(capsq) == pawn_of_color(them)); + 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(to) == EMPTY); clear_bit(&b, from); @@ -521,11 +541,8 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { // 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. - if ( !bit_is_set(pinned, from) - || (direction_between_squares(from, ksq) == direction_between_squares(move_to(m), ksq))) - return true; - - return false; + return ( !bit_is_set(pinned, from) + || (direction_between_squares(from, ksq) == direction_between_squares(move_to(m), ksq))); } @@ -543,21 +560,18 @@ bool Position::move_is_check(Move m) const { bool Position::move_is_check(Move m, Bitboard dcCandidates) const { - Color us, them; - Square ksq, from, to; - assert(is_ok()); assert(move_is_ok(m)); assert(dcCandidates == discovered_check_candidates(side_to_move())); - us = side_to_move(); - them = opposite_color(us); - from = move_from(m); - to = move_to(m); - ksq = king_square(them); + Color us = side_to_move(); + Color them = opposite_color(us); + Square from = move_from(m); + Square to = move_to(m); + Square ksq = king_square(them); assert(color_of_piece_on(from) == us); - assert(piece_on(ksq) == king_of_color(them)); + assert(piece_on(ksq) == piece_of_color_and_type(them, KING)); // Proceed according to the type of the moving piece switch (type_of_piece_on(from)) @@ -653,8 +667,8 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const { } return false; - default: - assert(false); + default: // NO_PIECE_TYPE + break; } assert(false); return false; @@ -662,12 +676,16 @@ 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 { - return color_of_piece_on(move_to(m)) == opposite_color(side_to_move()) - || move_is_ep(m); + assert(m != MOVE_NONE); + + return ( !square_is_empty(move_to(m)) + && (color_of_piece_on(move_to(m)) != color_of_piece_on(move_from(m))) + ) + || move_is_ep(m); } @@ -681,16 +699,23 @@ bool Position::move_is_capture(Move m) const { void Position::backup(UndoInfo& u) const { u.castleRights = castleRights; - u.epSquare = epSquare; - u.checkersBB = checkersBB; - u.key = key; - u.pawnKey = pawnKey; - u.materialKey = materialKey; - u.rule50 = rule50; - u.lastMove = lastMove; - u.capture = NO_PIECE_TYPE; - u.mgValue = mgValue; - u.egValue = egValue; + u.epSquare = epSquare; + u.checkersBB = checkersBB; + u.key = key; + u.pawnKey = pawnKey; + u.materialKey = materialKey; + u.rule50 = rule50; + u.lastMove = lastMove; + u.mgValue = mgValue; + u.egValue = egValue; + u.capture = NO_PIECE_TYPE; + + for (Color c = WHITE; c <= BLACK; c++) + { + u.pinners[c] = pinners[c]; + u.pinned[c] = pinned[c]; + u.dcCandidates[c] = dcCandidates[c]; + } } @@ -700,25 +725,52 @@ void Position::backup(UndoInfo& u) const { void Position::restore(const UndoInfo& u) { castleRights = u.castleRights; - epSquare = u.epSquare; - checkersBB = u.checkersBB; - key = u.key; - pawnKey = u.pawnKey; - materialKey = u.materialKey; - rule50 = u.rule50; - lastMove = u.lastMove; + epSquare = u.epSquare; + checkersBB = u.checkersBB; + key = u.key; + pawnKey = u.pawnKey; + materialKey = u.materialKey; + rule50 = u.rule50; + lastMove = u.lastMove; + mgValue = u.mgValue; + egValue = u.egValue; // u.capture is restored in undo_move() - mgValue = u.mgValue; - egValue = u.egValue; + + for (Color c = WHITE; c <= BLACK; c++) + { + pinners[c] = u.pinners[c]; + pinned[c] = u.pinned[c]; + dcCandidates[c] = u.dcCandidates[c]; + } +} + + +/// Position::update_checkers() is a private method to udpate chekers info + +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. +/// 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. /// There are two versions of this function, one which takes only the move and /// the UndoInfo as input, and one which takes a third parameter, a bitboard of -/// discovered check candidates. The second version is faster, because knowing +/// discovered check candidates. The second version is faster, because knowing /// the discovered check candidates makes it easier to update the checkersBB /// member variable in the position object. @@ -727,7 +779,7 @@ void Position::do_move(Move m, UndoInfo& u) { do_move(m, u, discovered_check_candidates(side_to_move())); } -void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { +void Position::do_move(Move m, UndoInfo& u, Bitboard dc) { assert(is_ok()); assert(move_is_ok(m)); @@ -744,6 +796,10 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { // case of non-reversible moves is taken care of later. rule50++; + // Reset pinned bitboard and its friends + for (Color c = WHITE; c <= BLACK; c++) + pinners[c] = pinned[c] = dcCandidates[c] = ~EmptyBoardBB; + if (move_is_castle(m)) do_castle_move(m); else if (move_promotion(m)) @@ -765,42 +821,8 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { if (capture) { - assert(capture != KING); - - // Remove captured piece - clear_bit(&(byColorBB[them]), to); - clear_bit(&(byTypeBB[capture]), to); - - // Update hash key - key ^= zobrist[them][capture][to]; - - // If the captured piece was a pawn, update pawn hash key - if (capture == PAWN) - pawnKey ^= zobrist[them][PAWN][to]; - - // Update incremental scores - mgValue -= mg_pst(them, capture, to); - egValue -= eg_pst(them, capture, to); - - // Update material - if (capture != PAWN) - npMaterial[them] -= piece_value_midgame(capture); - - // Update material hash key - materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]]; - - // Update piece count - pieceCount[them][capture]--; - - // Update piece list - pieceList[them][capture][index[to]] = pieceList[them][capture][pieceCount[them][capture]]; - index[pieceList[them][capture][index[to]]] = index[to]; - - // Remember the captured piece, in order to be able to undo the move correctly - u.capture = capture; - - // Reset rule 50 counter - rule50 = 0; + u.capture = capture; + do_capture_move(m, capture, them, to); } // Move the piece @@ -826,33 +848,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]; @@ -863,59 +886,18 @@ 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) { - case PAWN: - if (bit_is_set(pawn_attacks(them, 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))); - 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))); - 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)); - 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)); - break; - - case QUEEN: - if (bit_is_set(piece_attacks(ksq), to)) - set_bit(&checkersBB, to); - break; - - case KING: - if (bit_is_set(dcCandidates, from)) - checkersBB |= ( (piece_attacks(ksq) & rooks_and_queens(us)) - |(piece_attacks(ksq) & bishops_and_queens(us))); - break; - - default: - assert(false); - break; + case PAWN: update_checkers(&checkersBB, ksq, from, to, dc); break; + case KNIGHT: update_checkers(&checkersBB, ksq, from, to, dc); break; + case BISHOP: update_checkers(&checkersBB, ksq, from, to, dc); break; + case ROOK: update_checkers(&checkersBB, ksq, from, to, dc); break; + case QUEEN: update_checkers(&checkersBB, ksq, from, to, dc); break; + case KING: update_checkers(&checkersBB, ksq, from, to, dc); break; + default: assert(false); break; } } @@ -931,8 +913,51 @@ void Position::do_move(Move m, UndoInfo& u, Bitboard dcCandidates) { } +/// Position::do_capture_move() is a private method used to update captured +/// piece info. It is called from the main Position::do_move function. + +void Position::do_capture_move(Move m, PieceType capture, Color them, Square to) { + + assert(capture != KING); + + // Remove captured piece + clear_bit(&(byColorBB[them]), to); + clear_bit(&(byTypeBB[capture]), to); + + // Update hash key + key ^= zobrist[them][capture][to]; + + // If the captured piece was a pawn, update pawn hash key + if (capture == PAWN) + pawnKey ^= zobrist[them][PAWN][to]; + + // Update incremental scores + mgValue -= mg_pst(them, capture, to); + egValue -= eg_pst(them, capture, to); + + assert(!move_promotion(m) || capture != PAWN); + + // Update material + if (capture != PAWN) + npMaterial[them] -= piece_value_midgame(capture); + + // Update material hash key + materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]]; + + // Update piece count + pieceCount[them][capture]--; + + // Update piece list + pieceList[them][capture][index[to]] = pieceList[them][capture][pieceCount[them][capture]]; + index[pieceList[them][capture][index[to]]] = index[to]; + + // Reset rule 50 counter + rule50 = 0; +} + + /// Position::do_castle_move() is a private method used to make a castling -/// move. It is called from the main Position::do_move function. Note that +/// move. It is called from the main Position::do_move function. Note that /// castling moves are encoded as "king captures friendly rook" moves, for /// instance white short castling in a non-Chess960 game is encoded as e1h1. @@ -950,8 +975,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) == king_of_color(us)); - assert(piece_on(rfrom) == rook_of_color(us)); + assert(piece_on(kfrom) == piece_of_color_and_type(us, KING)); + assert(piece_on(rfrom) == piece_of_color_and_type(us, ROOK)); // Find destination squares for king and rook if (rfrom > kfrom) // O-O @@ -981,8 +1006,8 @@ void Position::do_castle_move(Move m) { // Update board array board[kfrom] = board[rfrom] = EMPTY; - board[kto] = king_of_color(us); - board[rto] = rook_of_color(us); + board[kto] = piece_of_color_and_type(us, KING); + board[rto] = piece_of_color_and_type(us, ROOK); // Update king square kingSquare[us] = kto; @@ -1009,7 +1034,7 @@ void Position::do_castle_move(Move m) { key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto]; // Clear en passant square - if(epSquare != SQ_NONE) + if (epSquare != SQ_NONE) { key ^= zobEp[epSquare]; epSquare = SQ_NONE; @@ -1049,43 +1074,15 @@ void Position::do_promotion_move(Move m, UndoInfo &u) { to = move_to(m); assert(relative_rank(us, to) == RANK_8); - assert(piece_on(from) == pawn_of_color(us)); + assert(piece_on(from) == piece_of_color_and_type(us, PAWN)); assert(color_of_piece_on(to) == them || square_is_empty(to)); capture = type_of_piece_on(to); if (capture) { - assert(capture != KING); - - // Remove captured piece - clear_bit(&(byColorBB[them]), to); - clear_bit(&(byTypeBB[capture]), to); - - // Update hash key - key ^= zobrist[them][capture][to]; - - // Update incremental scores - mgValue -= mg_pst(them, capture, to); - egValue -= eg_pst(them, capture, to); - - // Update material. Because our move is a promotion, we know that the - // captured piece is not a pawn. - assert(capture != PAWN); - npMaterial[them] -= piece_value_midgame(capture); - - // Update material hash key - materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]]; - - // Update piece count - pieceCount[them][capture]--; - - // Update piece list - pieceList[them][capture][index[to]] = pieceList[them][capture][pieceCount[them][capture]]; - index[pieceList[them][capture][index[to]]] = index[to]; - - // Remember the captured piece, in order to be able to undo the move correctly u.capture = capture; + do_capture_move(m, capture, them, to); } // Remove pawn @@ -1174,8 +1171,8 @@ void Position::do_ep_move(Move m) { assert(to == epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == EMPTY); - assert(piece_on(from) == pawn_of_color(us)); - assert(piece_on(capsq) == pawn_of_color(them)); + assert(piece_on(from) == piece_of_color_and_type(us, PAWN)); + assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN)); // Remove captured piece clear_bit(&(byColorBB[them]), capsq); @@ -1235,8 +1232,8 @@ void Position::do_ep_move(Move m) { } -/// Position::undo_move() unmakes a move. When it returns, the position should -/// be restored to exactly the same state as before the move was made. It is +/// Position::undo_move() unmakes a move. When it returns, the position should +/// be restored to exactly the same state as before the move was made. It is /// important that Position::undo_move is called with the same move and UndoInfo /// object as the earlier call to Position::do_move. @@ -1352,8 +1349,8 @@ void Position::undo_castle_move(Move m) { rto = relative_square(us, SQ_D1); } - assert(piece_on(kto) == king_of_color(us)); - assert(piece_on(rto) == rook_of_color(us)); + assert(piece_on(kto) == piece_of_color_and_type(us, KING)); + assert(piece_on(rto) == piece_of_color_and_type(us, ROOK)); // Remove pieces from destination squares clear_bit(&(byColorBB[us]), kto); @@ -1373,8 +1370,8 @@ void Position::undo_castle_move(Move m) { // Update board board[rto] = board[kto] = EMPTY; - board[rfrom] = rook_of_color(us); - board[kfrom] = king_of_color(us); + board[rfrom] = piece_of_color_and_type(us, ROOK); + board[kfrom] = piece_of_color_and_type(us, KING); // Update king square kingSquare[us] = kfrom; @@ -1425,7 +1422,7 @@ void Position::undo_promotion_move(Move m, const UndoInfo &u) { set_bit(&(byColorBB[us]), from); set_bit(&(byTypeBB[PAWN]), from); set_bit(&(byTypeBB[0]), from); // HACK: byTypeBB[0] == occupied squares - board[from] = pawn_of_color(us); + board[from] = piece_of_color_and_type(us, PAWN); // Update material npMaterial[us] -= piece_value_midgame(promotion); @@ -1490,7 +1487,7 @@ void Position::undo_ep_move(Move m) { assert(to == ep_square()); assert(relative_rank(us, to) == RANK_6); - assert(piece_on(to) == pawn_of_color(us)); + assert(piece_on(to) == piece_of_color_and_type(us, PAWN)); assert(piece_on(from) == EMPTY); assert(piece_on(capsq) == EMPTY); @@ -1498,7 +1495,7 @@ void Position::undo_ep_move(Move m) { set_bit(&(byColorBB[them]), capsq); set_bit(&(byTypeBB[PAWN]), capsq); set_bit(&(byTypeBB[0]), capsq); - board[capsq] = pawn_of_color(them); + board[capsq] = piece_of_color_and_type(them, PAWN); // Remove moving piece from destination square clear_bit(&(byColorBB[us]), to); @@ -1510,7 +1507,7 @@ void Position::undo_ep_move(Move m) { set_bit(&(byColorBB[us]), from); set_bit(&(byTypeBB[PAWN]), from); set_bit(&(byTypeBB[0]), from); - board[from] = pawn_of_color(us); + board[from] = piece_of_color_and_type(us, PAWN); // Update piece list: pieceList[us][PAWN][index[to]] = from; @@ -1526,7 +1523,7 @@ void Position::undo_ep_move(Move m) { /// Position::do_null_move makes() a "null move": It switches the side to move /// and updates the hash key without executing any move on the board. -void Position::do_null_move(UndoInfo &u) { +void Position::do_null_move(UndoInfo& u) { assert(is_ok()); assert(!is_check()); @@ -1584,11 +1581,17 @@ 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. +/// Position::see() is a static exchange evaluator: It tries to estimate the +/// 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 captures. + +int Position::see(Square to) const { + + assert(square_is_ok(to)); + return see(SQ_NONE, to); +} int Position::see(Move m) const { @@ -1598,18 +1601,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 @@ -1619,15 +1626,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 en passant moves + if (epSquare == 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]; @@ -1713,6 +1754,8 @@ void Position::clear() { } checkersBB = EmptyBoardBB; + for (Color c = WHITE; c <= BLACK; c++) + pinners[c] = pinned[c] = dcCandidates[c] = ~EmptyBoardBB; lastMove = MOVE_NONE; @@ -1727,7 +1770,7 @@ void Position::clear() { } -/// Position::reset_game_ply() simply sets gamePly to 0. It is used from the +/// 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 @@ -1804,7 +1847,7 @@ Key Position::compute_key() const { } -/// Position::compute_pawn_key() computes the hash key of the position. The +/// Position::compute_pawn_key() computes the hash key of the position. The /// hash key is usually updated incrementally as moves are made and unmade, /// the compute_pawn_key() function is only used when a new position is set /// up, and to verify the correctness of the pawn hash key when running in @@ -1850,7 +1893,7 @@ Key Position::compute_material_key() const { /// Position::compute_mg_value() and Position::compute_eg_value() compute the -/// incremental scores for the middle game and the endgame. These functions +/// incremental scores for the middle game and the endgame. These functions /// are used to initialize the incremental scores when a new position is set /// up, and to verify that the scores are correctly updated by do_move /// and undo_move when the program is running in debug mode. @@ -1899,7 +1942,7 @@ Value Position::compute_eg_value() const { /// Position::compute_non_pawn_material() computes the total non-pawn middle -/// game material score for the given side. Material scores are updated +/// game material score for the given side. Material scores are updated /// incrementally during the search, this function is only used while /// initializing a new Position object. @@ -1926,12 +1969,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; @@ -1939,7 +1981,7 @@ bool Position::is_mate() { /// Position::is_draw() tests whether the position is drawn by material, -/// repetition, or the 50 moves rule. It does not detect stalemates, this +/// repetition, or the 50 moves rule. It does not detect stalemates, this /// must be done by the search. bool Position::is_draw() const { @@ -2065,7 +2107,7 @@ 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, +/// 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) { @@ -2176,7 +2218,7 @@ bool Position::is_ok(int* failedStep) const { if (type_of_piece_on(s) == KING) kingCount[color_of_piece_on(s)]++; - if(kingCount[0] != 1 || kingCount[1] != 1) + if (kingCount[0] != 1 || kingCount[1] != 1) return false; } @@ -2256,10 +2298,10 @@ bool Position::is_ok(int* failedStep) const { if (failedStep) (*failedStep)++; if (debugNonPawnMaterial) { - if(npMaterial[WHITE] != compute_non_pawn_material(WHITE)) + if (npMaterial[WHITE] != compute_non_pawn_material(WHITE)) return false; - if(npMaterial[BLACK] != compute_non_pawn_material(BLACK)) + if (npMaterial[BLACK] != compute_non_pawn_material(BLACK)) return false; }