X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=a9c98626980fa56bbf5995e2c0d186f342fddef2;hp=8b6bafe31ad309cd8c42211098dc7ed227754ed6;hb=e444e18d2bd75454e3bbd9e5667b2357a19e5388;hpb=d9113d127b491db0a427a416217d55d3d298c25e diff --git a/src/position.cpp b/src/position.cpp index 8b6bafe3..a9c98626 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -29,6 +29,7 @@ #include "position.h" #include "psqtab.h" #include "rkiss.h" +#include "thread.h" #include "tt.h" #include "ucioption.h" @@ -160,7 +161,7 @@ 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. @@ -255,7 +256,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(); @@ -368,16 +369,16 @@ const string Position::to_fen() const { 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 += '-'; @@ -622,7 +623,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const { - assert(is_check()); + assert(in_check()); Color us = side_to_move(); Square from = move_from(m); @@ -643,15 +644,124 @@ bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const return bit_is_set(target, to) && pl_move_is_legal(m, pinned); } +/// 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. -/// Position::move_is_check() tests whether a pseudo-legal move is a check +bool Position::move_is_legal(const Move m) const { -bool Position::move_is_check(Move m) const { + MoveStack mlist[MAX_MOVES]; + MoveStack *cur, *last = generate(*this, mlist); - return move_is_check(m, CheckInfo(*this)); + for (cur = mlist; cur != last; cur++) + if (cur->move == m) + return pl_move_is_legal(m, pinned_pieces(sideToMove)); + + return false; } -bool Position::move_is_check(Move m, const CheckInfo& ci) const { + +/// 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. + +bool Position::move_is_legal(const Move m, Bitboard pinned) const { + + assert(is_ok()); + assert(pinned == pinned_pieces(sideToMove)); + + Color us = sideToMove; + Color them = opposite_color(sideToMove); + Square from = move_from(m); + Square to = move_to(m); + Piece pc = piece_on(from); + + // Use a slower but simpler function for uncommon cases + if (move_is_special(m)) + return move_is_legal(m); + + // 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) + return false; + + // 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; + + // 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; + break; + + 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; + + // The move is pseudo-legal, check if it is also legal + return in_check() ? pl_move_is_evasion(m, pinned) : pl_move_is_legal(m, pinned); +} + + +/// Position::move_gives_check() tests whether a pseudo-legal move is a check + +bool Position::move_gives_check(Move m) const { + + return move_gives_check(m, CheckInfo(*this)); +} + +bool Position::move_gives_check(Move m, const CheckInfo& ci) const { assert(is_ok()); assert(move_is_ok(m)); @@ -743,9 +853,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) { @@ -762,18 +871,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) { @@ -885,7 +995,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) @@ -938,6 +1047,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); @@ -1317,7 +1430,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 +1467,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 +1490,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)); @@ -1396,25 +1503,22 @@ 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)); + assert(move_is_ok(m)); + from = move_from(m); + to = move_to(m); capturedType = type_of_piece_on(to); - - // King cannot be recaptured - if (capturedType == KING) - return seeValues[capturedType]; - occupied = occupied_squares(); // Handle en passant moves @@ -1684,51 +1788,8 @@ bool Position::is_draw() const { bool Position::is_mate() const { - MoveStack moves[MOVES_MAX]; - return is_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. - -bool Position::has_mate_threat() { - - 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(*this, mlist); - last = generate(*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; + MoveStack moves[MAX_MOVES]; + return in_check() && generate(*this, moves) == moves; } @@ -1772,13 +1833,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(); @@ -1975,7 +2038,6 @@ 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++) @@ -1986,13 +2048,15 @@ bool Position::is_ok(int* failedStep) const { if (index[piece_list(c, pt, i)] != i) return false; } - } if (failedStep) (*failedStep)++; - if (debugCastleSquares) { - for (Color c = WHITE; c <= BLACK; c++) { + 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)) != make_piece(c, ROOK)) return false; }