X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=a069cf404be6d012a7ac129c4386b0c3a1721586;hp=8ab712b46d1f81a0fe6cd25865f39a1ea57e3c93;hb=47ee6d9fa4091aa05f6fedb62a2bb652b0422f08;hpb=37398d945624773287bfa5e56fff9f6adadaec83 diff --git a/src/position.cpp b/src/position.cpp index 8ab712b4..a069cf40 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2009 Marco Costalba + Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,13 +44,11 @@ using std::string; //// Variables //// -int Position::castleRightsMask[64]; - Key Position::zobrist[2][8][64]; Key Position::zobEp[64]; Key Position::zobCastle[16]; -Key Position::zobMaterial[2][8][16]; Key Position::zobSideToMove; +Key Position::zobExclusion; Score Position::PieceSquareTable[16][64]; @@ -65,7 +63,7 @@ CheckInfo::CheckInfo(const Position& pos) { Color them = opposite_color(us); ksq = pos.king_square(them); - dc = pos.discovered_check_candidates(us); + dcCandidates = pos.discovered_check_candidates(us); checkSq[PAWN] = pos.attacks_from(ksq, them); checkSq[KNIGHT] = pos.attacks_from(ksq); @@ -75,12 +73,36 @@ CheckInfo::CheckInfo(const Position& pos) { checkSq[KING] = EmptyBoardBB; } -Position::Position(const Position& pos) { - copy(pos); + +/// Position c'tors. Here we always create a copy of the original position +/// or the FEN string, we want the new born Position object do not depend +/// on any external data so we detach state pointer from the source one. + +Position::Position(int th) : threadID(th) {} + +Position::Position(const Position& pos, int th) { + + memcpy(this, &pos, sizeof(Position)); + detach(); // Always detach() in copy c'tor to avoid surprises + threadID = th; } -Position::Position(const string& fen) { +Position::Position(const string& fen, int th) { + from_fen(fen); + threadID = th; +} + + +/// Position::detach() copies the content of the current state and castling +/// masks inside the position itself. This is needed when the st pointee could +/// become stale, as example because the caller is about to going out of scope. + +void Position::detach() { + + startState = *st; + st = &startState; + st->previous = NULL; // as a safe guard } @@ -142,72 +164,77 @@ void Position::from_fen(const string& fen) { } i++; - while(strchr("KQkqabcdefghABCDEFGH-", fen[i])) { - if (fen[i] == '-') - { - i++; - break; - } - else if(fen[i] == 'K') allow_oo(WHITE); - else if(fen[i] == 'Q') allow_ooo(WHITE); - else if(fen[i] == 'k') allow_oo(BLACK); - else if(fen[i] == 'q') allow_ooo(BLACK); - else if(fen[i] >= 'A' && fen[i] <= 'H') { - File rookFile, kingFile = FILE_NONE; - for(Square square = SQ_B1; square <= SQ_G1; square++) - if(piece_on(square) == WK) - kingFile = square_file(square); - if(kingFile == FILE_NONE) { - std::cout << "Error in FEN at character " << i << std::endl; - return; - } - initialKFile = kingFile; - rookFile = File(fen[i] - 'A') + FILE_A; - if(rookFile < initialKFile) { - allow_ooo(WHITE); - initialQRFile = rookFile; - } - else { - allow_oo(WHITE); - initialKRFile = rookFile; + while (strchr("KQkqabcdefghABCDEFGH-", fen[i])) { + if (fen[i] == '-') + { + i++; + break; } - } - else if(fen[i] >= 'a' && fen[i] <= 'h') { - File rookFile, kingFile = FILE_NONE; - for(Square square = SQ_B8; square <= SQ_G8; square++) - if(piece_on(square) == BK) - kingFile = square_file(square); - if(kingFile == FILE_NONE) { - std::cout << "Error in FEN at character " << i << std::endl; - return; + else if (fen[i] == 'K') allow_oo(WHITE); + else if (fen[i] == 'Q') allow_ooo(WHITE); + else if (fen[i] == 'k') allow_oo(BLACK); + else if (fen[i] == 'q') allow_ooo(BLACK); + else if (fen[i] >= 'A' && fen[i] <= 'H') { + File rookFile, kingFile = FILE_NONE; + for (Square square = SQ_B1; square <= SQ_G1; square++) + if (piece_on(square) == WK) + kingFile = square_file(square); + if (kingFile == FILE_NONE) { + std::cout << "Error in FEN at character " << i << std::endl; + return; + } + initialKFile = kingFile; + rookFile = File(fen[i] - 'A') + FILE_A; + if (rookFile < initialKFile) { + allow_ooo(WHITE); + initialQRFile = rookFile; + } + else { + allow_oo(WHITE); + initialKRFile = rookFile; + } } - initialKFile = kingFile; - rookFile = File(fen[i] - 'a') + FILE_A; - if(rookFile < initialKFile) { - allow_ooo(BLACK); - initialQRFile = rookFile; + else if (fen[i] >= 'a' && fen[i] <= 'h') { + File rookFile, kingFile = FILE_NONE; + for (Square square = SQ_B8; square <= SQ_G8; square++) + if (piece_on(square) == BK) + kingFile = square_file(square); + if (kingFile == FILE_NONE) { + std::cout << "Error in FEN at character " << i << std::endl; + return; + } + initialKFile = kingFile; + rookFile = File(fen[i] - 'a') + FILE_A; + if (rookFile < initialKFile) { + allow_ooo(BLACK); + initialQRFile = rookFile; + } + else { + allow_oo(BLACK); + initialKRFile = rookFile; + } } else { - allow_oo(BLACK); - initialKRFile = rookFile; + std::cout << "Error in FEN at character " << i << std::endl; + return; } - } - else { - std::cout << "Error in FEN at character " << i << std::endl; - return; - } - i++; + i++; } // Skip blanks while (fen[i] == ' ') i++; - // En passant square + // En passant square -- ignore if no capture is possible if ( i <= fen.length() - 2 && (fen[i] >= 'a' && fen[i] <= 'h') && (fen[i+1] == '3' || fen[i+1] == '6')) - st->epSquare = square_from_string(fen.substr(i, 2)); + { + Square fenEpSquare = square_from_string(fen.substr(i, 2)); + Color them = opposite_color(sideToMove); + if (attacks_from(fenEpSquare, them) & this->pieces(PAWN, sideToMove)) + st->epSquare = square_from_string(fen.substr(i, 2)); + } // Various initialisation for (Square sq = SQ_A1; sq <= SQ_H8; sq++) @@ -265,10 +292,24 @@ const string Position::to_fen() const { fen += (sideToMove == WHITE ? "w " : "b "); if (st->castleRights != NO_CASTLES) { - if (can_castle_kingside(WHITE)) fen += 'K'; - if (can_castle_queenside(WHITE)) fen += 'Q'; - if (can_castle_kingside(BLACK)) fen += 'k'; - if (can_castle_queenside(BLACK)) fen += 'q'; + if (initialKFile == FILE_E && initialQRFile == FILE_A && initialKRFile == FILE_H) + { + if (can_castle_kingside(WHITE)) fen += 'K'; + if (can_castle_queenside(WHITE)) fen += 'Q'; + if (can_castle_kingside(BLACK)) fen += 'k'; + if (can_castle_queenside(BLACK)) fen += 'q'; + } + else + { + if (can_castle_kingside(WHITE)) + fen += char(toupper(file_to_char(initialKRFile))); + if (can_castle_queenside(WHITE)) + fen += char(toupper(file_to_char(initialQRFile))); + if (can_castle_kingside(BLACK)) + fen += file_to_char(initialKRFile); + if (can_castle_queenside(BLACK)) + fen += file_to_char(initialQRFile); + } } else fen += '-'; @@ -299,8 +340,9 @@ void Position::print(Move m) const { std::cout << std::endl; if (m != MOVE_NONE) { + Position p(*this, thread()); string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : ""); - std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl; + std::cout << "Move is: " << col << move_to_san(p, m) << std::endl; } for (Rank rank = RANK_8; rank >= RANK_1; rank--) { @@ -325,34 +367,24 @@ void Position::print(Move m) const { } -/// Position::copy() creates a copy of the input position. - -void Position::copy(const Position& pos) { - - memcpy(this, &pos, sizeof(Position)); - saveState(); // detach and copy state info -} - - /// Position:hidden_checkers<>() 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 pieces of the given color /// candidate for a discovery check against the enemy king. -/// Note that checkersBB bitboard must be already updated. +/// Bitboard checkersBB must be already updated when looking for pinners. template Bitboard Position::hidden_checkers(Color c) const { - Bitboard pinners, result = EmptyBoardBB; + Bitboard result = EmptyBoardBB; + Bitboard pinners = pieces_of_color(FindPinned ? opposite_color(c) : c); // Pinned pieces protect our king, dicovery checks attack // the enemy king. Square ksq = king_square(FindPinned ? c : opposite_color(c)); - // Pinners are sliders, not checkers, that give check when - // candidate pinned is removed. - pinners = (pieces(ROOK, QUEEN, FindPinned ? opposite_color(c) : c) & RookPseudoAttacks[ksq]) - | (pieces(BISHOP, QUEEN, FindPinned ? opposite_color(c) : c) & BishopPseudoAttacks[ksq]); + // Pinners are sliders, not checkers, that give check when candidate pinned is removed + pinners &= (pieces(ROOK, QUEEN) & RookPseudoAttacks[ksq]) | (pieces(BISHOP, QUEEN) & BishopPseudoAttacks[ksq]); if (FindPinned && pinners) pinners &= ~st->checkersBB; @@ -373,7 +405,8 @@ Bitboard Position::hidden_checkers(Color c) const { /// Position:pinned_pieces() returns a bitboard of all pinned (against the -/// king) pieces for the given color. +/// king) pieces for the given color. Note that checkersBB bitboard must +/// be already updated. Bitboard Position::pinned_pieces(Color c) const { @@ -383,7 +416,8 @@ Bitboard Position::pinned_pieces(Color c) const { /// Position:discovered_check_candidates() returns a bitboard containing all /// pieces for the given side which are candidates for giving a discovered -/// check. +/// check. Contrary to pinned_pieces() here there is no need of checkersBB +/// to be already updated. Bitboard Position::discovered_check_candidates(Color c) const { @@ -552,162 +586,98 @@ bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const bool Position::move_is_check(Move m) const { - Bitboard dc = discovered_check_candidates(side_to_move()); - return move_is_check(m, dc); + return move_is_check(m, CheckInfo(*this)); } -bool Position::move_is_check(Move m, Bitboard dcCandidates) const { +bool Position::move_is_check(Move m, const CheckInfo& ci) const { assert(is_ok()); assert(move_is_ok(m)); - assert(dcCandidates == discovered_check_candidates(side_to_move())); + 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)); - 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); + PieceType pt = type_of_piece_on(from); - assert(color_of_piece_on(from) == us); - assert(piece_on(ksq) == piece_of_color_and_type(them, KING)); + // Direct check ? + if (bit_is_set(ci.checkSq[pt], to)) + return true; - // Proceed according to the type of the moving piece - switch (type_of_piece_on(from)) + // Discovery check ? + if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from)) { - case PAWN: - - if (bit_is_set(attacks_from(ksq, them), to)) // Normal check? + // For pawn and king moves we need to verify also direction + if ( (pt != PAWN && pt != KING) + ||(direction_between_squares(from, ci.ksq) != direction_between_squares(to, ci.ksq))) return true; + } - if ( dcCandidates // Discovered check? - && bit_is_set(dcCandidates, from) - && (direction_between_squares(from, ksq) != direction_between_squares(to, ksq))) - return true; - - if (move_is_promotion(m)) // Promotion with check? - { - Bitboard b = occupied_squares(); - clear_bit(&b, from); - - switch (move_promotion_piece(m)) - { - case KNIGHT: - return bit_is_set(attacks_from(to), ksq); - case BISHOP: - return bit_is_set(bishop_attacks_bb(to, b), ksq); - case ROOK: - return bit_is_set(rook_attacks_bb(to, b), ksq); - case QUEEN: - return bit_is_set(queen_attacks_bb(to, b), ksq); - default: - assert(false); - } - } - // En passant capture with check? We have already handled the case - // of direct checks and ordinary discovered check, the only case we - // need to handle is the unusual case of a discovered check through the - // captured pawn. - else if (move_is_ep(m)) - { - Square capsq = make_square(square_file(to), square_rank(from)); - Bitboard b = occupied_squares(); - clear_bit(&b, from); - clear_bit(&b, capsq); - set_bit(&b, to); - return (rook_attacks_bb(ksq, b) & pieces(ROOK, QUEEN, us)) - ||(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, us)); - } + // Can we skip the ugly special cases ? + if (!move_is_special(m)) return false; - // Test discovered check and normal check according to piece type - case KNIGHT: - return (dcCandidates && bit_is_set(dcCandidates, from)) - || bit_is_set(attacks_from(ksq), to); - - case BISHOP: - return (dcCandidates && bit_is_set(dcCandidates, from)) - || (direction_is_diagonal(ksq, to) && bit_is_set(attacks_from(ksq), to)); - - case ROOK: - return (dcCandidates && bit_is_set(dcCandidates, from)) - || (direction_is_straight(ksq, to) && bit_is_set(attacks_from(ksq), to)); - - case QUEEN: - // Discovered checks are impossible! - assert(!bit_is_set(dcCandidates, from)); - return ( (direction_is_straight(ksq, to) && bit_is_set(attacks_from(ksq), to)) - || (direction_is_diagonal(ksq, to) && bit_is_set(attacks_from(ksq), to))); - - case KING: - // Discovered check? - if ( bit_is_set(dcCandidates, from) - && (direction_between_squares(from, ksq) != direction_between_squares(to, ksq))) - return true; + Color us = side_to_move(); + Bitboard b = occupied_squares(); - // Castling with check? - if (move_is_castle(m)) - { - Square kfrom, kto, rfrom, rto; - Bitboard b = occupied_squares(); - kfrom = from; - rfrom = to; + // Promotion with check ? + if (move_is_promotion(m)) + { + clear_bit(&b, from); - if (rfrom > kfrom) - { - kto = relative_square(us, SQ_G1); - rto = relative_square(us, SQ_F1); - } else { - kto = relative_square(us, SQ_C1); - rto = relative_square(us, SQ_D1); - } - clear_bit(&b, kfrom); - clear_bit(&b, rfrom); - set_bit(&b, rto); - set_bit(&b, kto); - return bit_is_set(rook_attacks_bb(rto, b), ksq); + switch (move_promotion_piece(m)) + { + case KNIGHT: + return bit_is_set(attacks_from(to), ci.ksq); + case BISHOP: + return bit_is_set(bishop_attacks_bb(to, b), ci.ksq); + case ROOK: + return bit_is_set(rook_attacks_bb(to, b), ci.ksq); + case QUEEN: + return bit_is_set(queen_attacks_bb(to, b), ci.ksq); + default: + assert(false); } - return false; - - default: // NO_PIECE_TYPE - break; } - assert(false); - return false; -} - - -/// Position::update_checkers() udpates chekers info given the move. It is called -/// in do_move() and is faster then find_checkers(). - -template -inline void Position::update_checkers(Bitboard* pCheckersBB, Square ksq, Square from, - Square to, Bitboard dcCandidates) { - const bool Bishop = (Piece == QUEEN || Piece == BISHOP); - const bool Rook = (Piece == QUEEN || Piece == ROOK); - const bool Slider = Bishop || Rook; - - // Direct checks - if ( ( (Bishop && bit_is_set(BishopPseudoAttacks[ksq], to)) - || (Rook && bit_is_set(RookPseudoAttacks[ksq], to))) - && bit_is_set(attacks_from(ksq), to)) // slow, try to early skip - set_bit(pCheckersBB, to); - - else if ( Piece != KING - && !Slider - && bit_is_set(Piece == PAWN ? attacks_from(ksq, opposite_color(sideToMove)) - : attacks_from(ksq), to)) - set_bit(pCheckersBB, to); + // En passant capture with check? We have already handled the case + // of direct checks and ordinary discovered check, the only case we + // need to handle is the unusual case of a discovered check through the + // captured pawn. + if (move_is_ep(m)) + { + Square capsq = make_square(square_file(to), square_rank(from)); + clear_bit(&b, from); + clear_bit(&b, capsq); + set_bit(&b, to); + return (rook_attacks_bb(ci.ksq, b) & pieces(ROOK, QUEEN, us)) + ||(bishop_attacks_bb(ci.ksq, b) & pieces(BISHOP, QUEEN, us)); + } - // Discovery checks - if (Piece != QUEEN && bit_is_set(dcCandidates, from)) + // Castling with check ? + if (move_is_castle(m)) { - if (Piece != ROOK) - (*pCheckersBB) |= (attacks_from(ksq) & pieces(ROOK, QUEEN, side_to_move())); + Square kfrom, kto, rfrom, rto; + kfrom = from; + rfrom = to; - if (Piece != BISHOP) - (*pCheckersBB) |= (attacks_from(ksq) & pieces(BISHOP, QUEEN, side_to_move())); + if (rfrom > kfrom) + { + kto = relative_square(us, SQ_G1); + rto = relative_square(us, SQ_F1); + } else { + kto = relative_square(us, SQ_C1); + rto = relative_square(us, SQ_D1); + } + clear_bit(&b, kfrom); + clear_bit(&b, rfrom); + set_bit(&b, rto); + set_bit(&b, kto); + return bit_is_set(rook_attacks_bb(rto, b), ci.ksq); } + + return false; } @@ -717,24 +687,25 @@ inline void Position::update_checkers(Bitboard* pCheckersBB, Square ksq, Square void Position::do_move(Move m, StateInfo& newSt) { - do_move(m, newSt, discovered_check_candidates(side_to_move())); + CheckInfo ci(*this); + do_move(m, newSt, ci, move_is_check(m, ci)); } -void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { +void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { assert(is_ok()); assert(move_is_ok(m)); - Bitboard key = st->key; + Key key = st->key; // Copy some fields of old state to our new StateInfo object except the // ones which are recalculated from scratch anyway, then switch our state // pointer to point to the new, ready to be updated, state. struct ReducedStateInfo { Key pawnKey, materialKey; - int castleRights, rule50, pliesFromNull; + int castleRights, rule50, gamePly, pliesFromNull; Square epSquare; - Value value; + Score value; Value npMaterial[2]; }; @@ -744,8 +715,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { // Save the current key to the history[] array, in order to be able to // detect repetition draws. - history[gamePly] = key; - gamePly++; + history[st->gamePly++] = key; // Update side to move key ^= zobSideToMove; @@ -802,7 +772,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { } // Prefetch TT access as soon as we know key is updated - TT.prefetch(key); + prefetch((char*)TT.first_entry(key)); // Move the piece Bitboard move_bb = make_move_bb(from, to); @@ -837,6 +807,46 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { key ^= zobEp[st->epSquare]; } } + + if (pm) // promotion ? + { + PieceType promotion = move_promotion_piece(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); + + // Update piece counts + pieceCount[us][promotion]++; + pieceCount[us][PAWN]--; + + // Update material key + st->materialKey ^= zobrist[us][PAWN][pieceCount[us][PAWN]]; + st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]-1]; + + // Update piece lists, move the last pawn at index[to] position + // and shrink the list. Add a new promotion piece to the list. + Square lastPawnSquare = pieceList[us][PAWN][pieceCount[us][PAWN]]; + index[lastPawnSquare] = index[to]; + pieceList[us][PAWN][index[lastPawnSquare]] = lastPawnSquare; + pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE; + index[to] = pieceCount[us][promotion] - 1; + pieceList[us][promotion][index[to]] = to; + + // Partially revert hash keys update + key ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to]; + st->pawnKey ^= zobrist[us][PAWN][to]; + + // Partially revert and update incremental scores + st->value -= pst(us, PAWN, to); + st->value += pst(us, promotion, to); + + // Update material + st->npMaterial[us] += piece_value_midgame(promotion); + } } // Update incremental scores @@ -845,65 +855,31 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { // Set capture piece st->capture = capture; - if (pm) // promotion ? - { - PieceType promotion = move_promotion_piece(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); - - // Update material key - st->materialKey ^= zobMaterial[us][PAWN][pieceCount[us][PAWN]]; - st->materialKey ^= zobMaterial[us][promotion][pieceCount[us][promotion]+1]; - - // Update piece counts - pieceCount[us][PAWN]--; - pieceCount[us][promotion]++; - - // Update piece lists, move the last pawn at index[to] position - // and shrink the list. Add a new promotion piece to the list. - Square lastPawnSquare = pieceList[us][PAWN][pieceCount[us][PAWN]]; - index[lastPawnSquare] = index[to]; - pieceList[us][PAWN][index[lastPawnSquare]] = lastPawnSquare; - pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE; - index[to] = pieceCount[us][promotion] - 1; - pieceList[us][promotion][index[to]] = to; - - // Partially revert hash keys update - key ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to]; - st->pawnKey ^= zobrist[us][PAWN][to]; - - // Partially revert and update incremental scores - st->value -= pst(us, PAWN, to); - st->value += pst(us, promotion, to); - - // Update material - st->npMaterial[us] += piece_value_midgame(promotion); - } - // Update the key with the final value st->key = key; // Update checkers bitboard, piece must be already moved - if (ep | pm) - st->checkersBB = attackers_to(king_square(them)) & pieces_of_color(us); - else + st->checkersBB = EmptyBoardBB; + + if (moveIsCheck) { - st->checkersBB = EmptyBoardBB; - Square ksq = king_square(them); - switch (pt) + if (ep | pm) + st->checkersBB = attackers_to(king_square(them)) & pieces_of_color(us); + else { - case PAWN: update_checkers(&(st->checkersBB), ksq, from, to, dcCandidates); break; - case KNIGHT: update_checkers(&(st->checkersBB), ksq, from, to, dcCandidates); break; - case BISHOP: update_checkers(&(st->checkersBB), ksq, from, to, dcCandidates); break; - case ROOK: update_checkers(&(st->checkersBB), ksq, from, to, dcCandidates); break; - case QUEEN: update_checkers(&(st->checkersBB), ksq, from, to, dcCandidates); break; - case KING: update_checkers(&(st->checkersBB), ksq, from, to, dcCandidates); break; - default: assert(false); break; + // Direct checks + if (bit_is_set(ci.checkSq[pt], to)) + st->checkersBB = SetMaskBB[to]; + + // Discovery checks + if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from)) + { + if (pt != ROOK) + st->checkersBB |= (attacks_from(ci.ksq) & pieces(ROOK, QUEEN, us)); + + if (pt != BISHOP) + st->checkersBB |= (attacks_from(ci.ksq) & pieces(BISHOP, QUEEN, us)); + } } } @@ -918,23 +894,31 @@ void Position::do_move(Move m, StateInfo& newSt, 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(Bitboard& key, PieceType capture, Color them, Square to, bool ep) { +void Position::do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep) { assert(capture != KING); Square capsq = to; - if (ep) // en passant ? + // If the captured piece was a pawn, update pawn hash key, + // otherwise update non-pawn material. + if (capture == PAWN) { - capsq = (them == BLACK)? (to - DELTA_N) : (to - DELTA_S); + if (ep) // en passant ? + { + capsq = (them == BLACK)? (to - DELTA_N) : (to - DELTA_S); - assert(to == st->epSquare); - assert(relative_rank(opposite_color(them), to) == RANK_6); - assert(piece_on(to) == EMPTY); - assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN)); + assert(to == st->epSquare); + assert(relative_rank(opposite_color(them), to) == RANK_6); + assert(piece_on(to) == EMPTY); + assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN)); - board[capsq] = EMPTY; + board[capsq] = EMPTY; + } + st->pawnKey ^= zobrist[them][PAWN][capsq]; } + else + st->npMaterial[them] -= piece_value_midgame(capture); // Remove captured piece clear_bit(&(byColorBB[them]), capsq); @@ -947,19 +931,12 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ // Update incremental scores st->value -= pst(them, capture, capsq); - // If the captured piece was a pawn, update pawn hash key, - // otherwise update non-pawn material. - if (capture == PAWN) - st->pawnKey ^= zobrist[them][PAWN][capsq]; - else - st->npMaterial[them] -= piece_value_midgame(capture); - - // Update material hash key - st->materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]]; - // Update piece count pieceCount[them][capture]--; + // Update material hash key + st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]]; + // Update piece list, move the last piece at index[capsq] position // // WARNING: This is a not perfectly revresible operation. When we @@ -1026,7 +1003,7 @@ void Position::do_castle_move(Move m) { set_bit(&(byColorBB[us]), rto); set_bit(&(byTypeBB[ROOK]), rto); 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); @@ -1083,7 +1060,6 @@ void Position::undo_move(Move m) { assert(is_ok()); assert(move_is_ok(m)); - gamePly--; sideToMove = opposite_color(sideToMove); if (move_is_castle(m)) @@ -1133,7 +1109,6 @@ void Position::undo_move(Move m) { pieceList[us][PAWN][index[to]] = to; } - // Put the piece back at the source square Bitboard move_bb = make_move_bb(to, from); do_move_bb(&(byColorBB[us]), move_bb); @@ -1211,7 +1186,7 @@ void Position::undo_castle_move(Move m) { 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); clear_bit(&(byTypeBB[KING]), kto); @@ -1219,7 +1194,7 @@ void Position::undo_castle_move(Move m) { clear_bit(&(byColorBB[us]), rto); clear_bit(&(byTypeBB[ROOK]), rto); clear_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares - + // Put pieces on source squares: set_bit(&(byColorBB[us]), kfrom); set_bit(&(byTypeBB[KING]), kfrom); @@ -1268,21 +1243,20 @@ void Position::do_null_move(StateInfo& backupSt) { // Save the current key to the history[] array, in order to be able to // detect repetition draws. - history[gamePly] = st->key; + history[st->gamePly++] = st->key; // Update the necessary information if (st->epSquare != SQ_NONE) st->key ^= zobEp[st->epSquare]; st->key ^= zobSideToMove; - TT.prefetch(st->key); + prefetch((char*)TT.first_entry(st->key)); sideToMove = opposite_color(sideToMove); st->epSquare = SQ_NONE; st->rule50++; st->pliesFromNull = 0; st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue; - gamePly++; } @@ -1304,7 +1278,7 @@ void Position::undo_null_move() { // Update the necessary information sideToMove = opposite_color(sideToMove); st->rule50--; - gamePly--; + st->gamePly--; } @@ -1479,19 +1453,6 @@ int Position::see(Square from, Square to) const { } -/// Position::saveState() copies the content of the current state -/// inside startState and makes st point to it. This is needed -/// when the st pointee could become stale, as example because -/// the caller is about to going out of scope. - -void Position::saveState() { - - startState = *st; - st = &startState; - st->previous = NULL; // as a safe guard -} - - /// Position::clear() erases the position object to a pristine state, with an /// empty board, white to move, and no castling rights. @@ -1514,7 +1475,6 @@ void Position::clear() { pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; sideToMove = WHITE; - gamePly = 0; initialKFile = FILE_E; initialKRFile = FILE_H; initialQRFile = FILE_A; @@ -1529,7 +1489,7 @@ void Position::clear() { void Position::reset_game_ply() { - gamePly = 0; + st->gamePly = 0; } @@ -1610,7 +1570,7 @@ Key Position::compute_pawn_key() const { for (Color c = WHITE; c <= BLACK; c++) { b = pieces(PAWN, c); - while(b) + while (b) { s = pop_1st_bit(&b); result ^= zobrist[c][PAWN][s]; @@ -1633,8 +1593,8 @@ Key Position::compute_material_key() const { for (PieceType pt = PAWN; pt <= QUEEN; pt++) { int count = piece_count(c, pt); - for (int i = 0; i <= count; i++) - result ^= zobMaterial[c][pt][i]; + for (int i = 0; i < count; i++) + result ^= zobrist[c][pt][i]; } return result; } @@ -1654,7 +1614,7 @@ Score Position::compute_value() const { for (PieceType pt = PAWN; pt <= KING; pt++) { b = pieces(pt, c); - while(b) + while (b) { s = pop_1st_bit(&b); assert(piece_on(s) == piece_of_color_and_type(c, pt)); @@ -1693,6 +1653,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. +// FIXME: Currently we are not handling 50 move rule correctly when in check bool Position::is_draw() const { @@ -1706,8 +1667,8 @@ bool Position::is_draw() const { return true; // Draw by repetition? - for (int i = 2; i < Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2) - if (history[gamePly - i] == st->key) + 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; @@ -1786,14 +1747,7 @@ void Position::init_zobrist() { zobCastle[i] = genrand_int64(); zobSideToMove = genrand_int64(); - - for (int i = 0; i < 2; i++) - for (int j = 0; j < 8; j++) - for (int k = 0; k < 16; k++) - zobMaterial[i][j][k] = (k > 0)? Key(genrand_int64()) : Key(0LL); - - for (int i = 0; i < 16; i++) - zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL); + zobExclusion = genrand_int64(); } @@ -1829,6 +1783,7 @@ void Position::flipped_copy(const Position& pos) { assert(pos.is_ok()); clear(); + threadID = pos.thread(); // Board for (Square s = SQ_A1; s <= SQ_H8; s++) @@ -1898,6 +1853,7 @@ bool Position::is_ok(int* failedStep) const { static const bool debugNonPawnMaterial = false; static const bool debugPieceCounts = false; static const bool debugPieceList = false; + static const bool debugCastleSquares = false; if (failedStep) *failedStep = 1; @@ -2023,9 +1979,9 @@ 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++) + 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)) return false; @@ -2034,6 +1990,25 @@ bool Position::is_ok(int* failedStep) const { 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)) + return false; + if (can_castle_queenside(c) && piece_on(initial_qr_square(c)) != piece_of_color_and_type(c, ROOK)) + return false; + } + if (castleRightsMask[initial_kr_square(WHITE)] != (ALL_CASTLES ^ WHITE_OO)) + return false; + if (castleRightsMask[initial_qr_square(WHITE)] != (ALL_CASTLES ^ WHITE_OOO)) + return false; + if (castleRightsMask[initial_kr_square(BLACK)] != (ALL_CASTLES ^ BLACK_OO)) + return false; + if (castleRightsMask[initial_qr_square(BLACK)] != (ALL_CASTLES ^ BLACK_OOO)) + return false; + } + if (failedStep) *failedStep = 0; return true; }