X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=1364c7ed7d2cb9c7b3453fd02d578d9d528947e0;hp=b6c0a522e204edb3889e02d26036932d1e7da3d7;hb=03cfd94414b49489cae12abfd6c39f7fbb53515a;hpb=4626ec2890b140829e9971658ca948005b945fd4 diff --git a/src/position.cpp b/src/position.cpp index b6c0a522..1364c7ed 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,33 +44,69 @@ 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]; static bool RequestPending = false; -//// -//// Functions -//// /// Constructors +CheckInfo::CheckInfo(const Position& pos) { + + Color us = pos.side_to_move(); + Color them = opposite_color(us); + + ksq = pos.king_square(them); + dcCandidates = pos.discovered_check_candidates(us); + + checkSq[PAWN] = pos.attacks_from(ksq, them); + checkSq[KNIGHT] = pos.attacks_from(ksq); + checkSq[BISHOP] = pos.attacks_from(ksq); + checkSq[ROOK] = pos.attacks_from(ksq); + checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; + checkSq[KING] = EmptyBoardBB; +} + + +/// Position c'tors. Here we always create a slower but safer copy of +/// the original position or the FEN string, we want the new born Position +/// object do not depend on any external data. Instead if we know what we +/// are doing and we need speed we can create a position with default +/// c'tor Position() and then use just fast_copy(). + +Position::Position() {} + Position::Position(const Position& pos) { - copy(pos); + + memcpy(this, &pos, sizeof(Position)); + detach(); // Always detach() in copy c'tor to avoid surprises } Position::Position(const string& fen) { + from_fen(fen); } +/// 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 +} + + /// Position::from_fen() initializes the position object with the given 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). @@ -129,72 +165,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++) @@ -252,10 +293,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 += '-'; @@ -286,8 +341,9 @@ void Position::print(Move m) const { std::cout << std::endl; if (m != MOVE_NONE) { + Position p(*this); 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--) { @@ -312,34 +368,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; @@ -360,7 +406,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 { @@ -370,7 +417,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 { @@ -539,162 +587,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? - return true; - - if ( dcCandidates // Discovered check? - && bit_is_set(dcCandidates, from) - && (direction_between_squares(from, ksq) != direction_between_squares(to, ksq))) + // 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 (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; } @@ -704,24 +688,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 key, pawnKey, materialKey; + Key pawnKey, materialKey; int castleRights, rule50, pliesFromNull; Square epSquare; - Value value; + Score value; Value npMaterial[2]; }; @@ -758,16 +743,15 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { Piece piece = piece_on(from); PieceType pt = type_of_piece(piece); + PieceType capture = ep ? PAWN : type_of_piece_on(to); 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(!pm || relative_rank(us, to) == RANK_8); - st->capture = ep ? PAWN : type_of_piece_on(to); - - if (st->capture) - do_capture_move(key, st->capture, them, to, ep); + if (capture) + do_capture_move(key, capture, them, to, ep); // Update hash key key ^= zobrist[us][pt][from] ^ zobrist[us][pt][to]; @@ -817,7 +801,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { st->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 ((to ^ from) == 16) { if (attacks_from(from + (us == WHITE ? DELTA_N : DELTA_S), us) & pieces(PAWN, them)) { @@ -830,6 +814,9 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { // Update incremental scores st->value += pst_delta(piece, from, to); + // Set capture piece + st->capture = capture; + if (pm) // promotion ? { PieceType promotion = move_promotion_piece(m); @@ -841,13 +828,13 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { 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 ^= zobMaterial[us][PAWN][pieceCount[us][PAWN]]; - st->materialKey ^= zobMaterial[us][promotion][pieceCount[us][promotion]+1]; - - // Update piece counts - pieceCount[us][PAWN]--; - pieceCount[us][promotion]++; + st->materialKey ^= zobMaterial[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. @@ -874,27 +861,33 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) { 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)); + } } } // Finish sideToMove = opposite_color(sideToMove); - st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue; + st->value += (sideToMove == WHITE ? TempoValue : -TempoValue); assert(is_ok()); } @@ -903,7 +896,7 @@ 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); @@ -939,12 +932,12 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ 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 ^= zobMaterial[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 @@ -1011,7 +1004,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); @@ -1054,7 +1047,7 @@ void Position::do_castle_move(Move m) { // Finish sideToMove = opposite_color(sideToMove); - st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue; + st->value += (sideToMove == WHITE ? TempoValue : -TempoValue); assert(is_ok()); } @@ -1196,7 +1189,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); @@ -1204,7 +1197,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); @@ -1464,19 +1457,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. @@ -1595,7 +1575,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]; @@ -1618,7 +1598,7 @@ 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++) + for (int i = 0; i < count; i++) result ^= zobMaterial[c][pt][i]; } return result; @@ -1631,7 +1611,7 @@ Key Position::compute_material_key() const { /// updated by do_move and undo_move when the program is running in debug mode. Score Position::compute_value() const { - Score result(0, 0); + Score result = make_score(0, 0); Bitboard b; Square s; @@ -1639,7 +1619,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)); @@ -1647,7 +1627,7 @@ Score Position::compute_value() const { } } - result += (side_to_move() == WHITE)? TempoValue / 2 : -TempoValue / 2; + result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2); return result; } @@ -1678,6 +1658,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 { @@ -1691,7 +1672,7 @@ bool Position::is_draw() const { return true; // Draw by repetition? - for (int i = 2; i < Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2) + for (int i = 4; i <= Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2) if (history[gamePly - i] == st->key) return true; @@ -1774,11 +1755,13 @@ void Position::init_zobrist() { 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 k = 0; k < 8; k++) + zobMaterial[i][j][k] = Key(genrand_int64()); - for (int i = 0; i < 16; i++) + for (int i = 0; i < 8; i++) zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL); + + zobExclusion = genrand_int64(); } @@ -1796,7 +1779,7 @@ void Position::init_piece_square_tables() { for (Piece p = WP; p <= WK; p++) { i = (r == 0)? 0 : (genrand_int32() % (r*2) - r); - PieceSquareTable[p][s] = Score(MgPST[p][s] + i, EgPST[p][s] + i); + PieceSquareTable[p][s] = make_score(MgPST[p][s] + i, EgPST[p][s] + i); } for (Square s = SQ_A1; s <= SQ_H8; s++) @@ -1883,6 +1866,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; @@ -2008,9 +1992,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; @@ -2019,6 +2003,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; }