X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=8f70b5dfd3ab3af4c644072396622c75257ce381;hp=ae28805c4c6bfd4cf093d52c74bcf456ab23d22a;hb=b21a5e2f0638a55daeaa98ba95afc6c016ea0b6e;hpb=b08ba446f655ba6292cb0b5a1012e790e4ad8d7d diff --git a/src/position.cpp b/src/position.cpp index ae28805c..8f70b5df 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]; @@ -89,35 +76,8 @@ 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 "); } @@ -175,14 +135,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 @@ -206,67 +166,58 @@ 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_from_char(col), rank_from_char(row)); - 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. Halfmove clock - if (ss >> hmc) + if (ss >> std::skipws >> hmc) st->rule50 = hmc; // 6. Fullmove number @@ -281,7 +232,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(); @@ -319,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; } @@ -329,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; } @@ -339,16 +290,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; } @@ -361,44 +313,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 += '-'; @@ -413,14 +368,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) { @@ -435,17 +382,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; } @@ -519,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. @@ -531,7 +487,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]; } } @@ -544,7 +500,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]; } } @@ -599,26 +555,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); @@ -629,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)) == 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. @@ -648,46 +599,165 @@ 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); + + // Use a slower but simpler function for uncommon cases + if (move_is_special(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 (pc == PIECE_NONE || 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; - // 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); + // 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; - Bitboard target = checkers(); - Square checksq = pop_1st_bit(&target); + // From and to files must be one file apart, avoids a7h5 + if (abs(square_file(from) - square_file(to)) != 1) + return false; + break; - if (target) // double check ? + 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; - // 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); + 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)); 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)); + assert(piece_on(ci.ksq) == make_piece(opposite_color(side_to_move()), KING)); Square from = move_from(m); Square to = move_to(m); @@ -718,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); @@ -773,11 +843,12 @@ 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) { -void Position::do_setup_move(Move m, StateInfo& newSt) { + StateInfo newSt; do_move(m, newSt); @@ -788,22 +859,28 @@ void Position::do_setup_move(Move m, StateInfo& newSt) { // 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; @@ -819,8 +896,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; @@ -857,7 +933,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) @@ -873,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]; } @@ -909,7 +984,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) @@ -923,14 +997,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]++; @@ -962,6 +1036,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); @@ -1024,7 +1102,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; } @@ -1088,8 +1166,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 @@ -1118,8 +1196,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; @@ -1195,15 +1273,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); @@ -1228,7 +1306,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 @@ -1250,7 +1328,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]++; @@ -1297,8 +1375,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); @@ -1318,8 +1396,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; @@ -1341,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. @@ -1378,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; @@ -1401,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)); @@ -1420,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 @@ -1457,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)); @@ -1682,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? @@ -1691,68 +1761,30 @@ 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; -} - - -/// 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_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; + MoveStack moves[MAX_MOVES]; + return in_check() && generate(*this, moves) == moves; } @@ -1796,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(); @@ -1816,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; @@ -1999,25 +2033,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))