X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=e51c1ba70296a7149460da078c1032b1582a3b88;hp=aa06db08c106ae9f5c1689e2e3b110671f21a25c;hb=90ce24b11ea080f923e1b5d37470e2ca98f4d98b;hpb=13b4444d9e3a3ac5a71f256145904d13754f9e7f diff --git a/src/position.cpp b/src/position.cpp index aa06db08..e51c1ba7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -36,7 +36,7 @@ using std::string; namespace Zobrist { - Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + Key psq[PIECE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; Key side; @@ -109,10 +109,9 @@ void Position::init() { PRNG rng(1070372); - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[c][pt][s] = rng.rand(); + for (Piece pc : Pieces) + for (Square s = SQ_A1; s <= SQ_H8; ++s) + Zobrist::psq[pc][s] = rng.rand(); for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rng.rand(); @@ -178,7 +177,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::memset(this, 0, sizeof(Position)); std::memset(si, 0, sizeof(StateInfo)); - std::fill_n(&pieceList[0][0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); + std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; ss >> std::noskipws; @@ -194,7 +193,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th else if ((idx = PieceToChar.find(token)) != string::npos) { - put_piece(color_of(Piece(idx)), type_of(Piece(idx)), sq); + put_piece(Piece(idx), sq); ++sq; } } @@ -292,8 +291,8 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_check_info(StateInfo* si) const { - si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE)); - si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK)); + si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), si->pinnersForKing[WHITE]); + si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinnersForKing[BLACK]); Square ksq = square(~sideToMove); @@ -324,8 +323,8 @@ void Position::set_state(StateInfo* si) const { { Square s = pop_lsb(&b); Piece pc = piece_on(s); - si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s]; - si->psq += PSQT::psq[color_of(pc)][type_of(pc)][s]; + si->key ^= Zobrist::psq[pc][s]; + si->psq += PSQT::psq[pc][s]; } if (si->epSquare != SQ_NONE) @@ -339,17 +338,17 @@ void Position::set_state(StateInfo* si) const { for (Bitboard b = pieces(PAWN); b; ) { Square s = pop_lsb(&b); - si->pawnKey ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; + si->pawnKey ^= Zobrist::psq[piece_on(s)][s]; } - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt) - si->materialKey ^= Zobrist::psq[c][pt][cnt]; + for (Piece pc : Pieces) + { + if (type_of(pc) != PAWN && type_of(pc) != KING) + si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc]; - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt) - si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt]; + for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) + si->materialKey ^= Zobrist::psq[pc][cnt]; + } } @@ -416,24 +415,25 @@ Phase Position::game_phase() const { } -/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) that -/// are blocking attacks on the square 's' from 'sliders'. A piece blocks a slider -/// if removing that piece from the board would result in a position where square 's' -/// is attacked. For example, a king-attack blocking piece can be either a pinned or -/// a discovered check piece, according if its color is the opposite or the same of -/// the color of the slider. +/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) +/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a +/// slider if removing that piece from the board would result in a position where +/// square 's' is attacked. For example, a king-attack blocking piece can be either +/// a pinned or a discovered check piece, according if its color is the opposite +/// or the same of the color of the slider. The pinners bitboard get filled with +/// real and potential pinners. -Bitboard Position::slider_blockers(Bitboard sliders, Square s) const { +Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const { - Bitboard b, pinners, result = 0; + Bitboard b, p, result = 0; // Pinners are sliders that attack 's' when a pinned piece is removed - pinners = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK)) - | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + pinners = p = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK)) + | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; - while (pinners) + while (p) { - b = between_bb(s, pop_lsb(&pinners)) & pieces(); + b = between_bb(s, pop_lsb(&p)) & pieces(); if (!more_than_one(b)) result |= b; @@ -657,23 +657,24 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); + Piece pc = piece_on(from); + Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); - assert(color_of(piece_on(from)) == us); - assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == (type_of(m) != CASTLING ? them : us)); - assert(captured != KING); + assert(color_of(pc) == us); + assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); + assert(type_of(captured) != KING); if (type_of(m) == CASTLING) { - assert(pt == KING); + assert(pc == make_piece(us, KING)); + assert(captured == make_piece(us, ROOK)); Square rfrom, rto; do_castling(us, from, to, rfrom, rto); - captured = NO_PIECE_TYPE; - st->psq += PSQT::psq[us][ROOK][rto] - PSQT::psq[us][ROOK][rfrom]; - k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom]; + k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + captured = NO_PIECE; } if (captured) @@ -682,13 +683,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. - if (captured == PAWN) + if (type_of(captured) == PAWN) { if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); - assert(pt == PAWN); + assert(pc == make_piece(us, PAWN)); assert(to == st->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == NO_PIECE); @@ -697,28 +698,28 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { board[capsq] = NO_PIECE; // Not done by remove_piece() } - st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; + st->pawnKey ^= Zobrist::psq[captured][capsq]; } else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists - remove_piece(them, captured, capsq); + remove_piece(captured, capsq); // Update material hash key and prefetch access to materialTable - k ^= Zobrist::psq[them][captured][capsq]; - st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]]; + k ^= Zobrist::psq[captured][capsq]; + st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; prefetch(thisThread->materialTable[st->materialKey]); // Update incremental scores - st->psq -= PSQT::psq[them][captured][capsq]; + st->psq -= PSQT::psq[captured][capsq]; // Reset rule 50 counter st->rule50 = 0; } // Update hash key - k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; + k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; // Reset en passant square if (st->epSquare != SQ_NONE) @@ -737,10 +738,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) - move_piece(us, pt, from, to); + move_piece(pc, from, to); // If the moving piece is a pawn do some special extra work - if (pt == PAWN) + if (type_of(pc) == PAWN) { // Set en-passant square if the moved pawn can be captured if ( (int(to) ^ int(from)) == 16 @@ -752,29 +753,29 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else if (type_of(m) == PROMOTION) { - PieceType promotion = promotion_type(m); + Piece promotion = make_piece(us, promotion_type(m)); assert(relative_rank(us, to) == RANK_8); - assert(promotion >= KNIGHT && promotion <= QUEEN); + assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); - remove_piece(us, PAWN, to); - put_piece(us, promotion, to); + remove_piece(pc, to); + put_piece(promotion, to); // Update hash keys - k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; - st->pawnKey ^= Zobrist::psq[us][PAWN][to]; - st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1] - ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; + st->pawnKey ^= Zobrist::psq[pc][to]; + st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] + ^ Zobrist::psq[pc][pieceCount[pc]]; // Update incremental score - st->psq += PSQT::psq[us][promotion][to] - PSQT::psq[us][PAWN][to]; + st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to]; // Update material st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable - st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; + st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; prefetch(thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter @@ -782,10 +783,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } // Update incremental scores - st->psq += PSQT::psq[us][pt][to] - PSQT::psq[us][pt][from]; + st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; // Set capture piece - st->capturedType = captured; + st->capturedPiece = captured; // Update the key with the final value st->key = k; @@ -814,20 +815,20 @@ void Position::undo_move(Move m) { Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(to)); + Piece pc = piece_on(to); assert(empty(from) || type_of(m) == CASTLING); - assert(st->capturedType != KING); + assert(type_of(st->capturedPiece) != KING); if (type_of(m) == PROMOTION) { assert(relative_rank(us, to) == RANK_8); - assert(pt == promotion_type(m)); - assert(pt >= KNIGHT && pt <= QUEEN); + assert(type_of(pc) == promotion_type(m)); + assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); - remove_piece(us, pt, to); - put_piece(us, PAWN, to); - pt = PAWN; + remove_piece(pc, to); + pc = make_piece(us, PAWN); + put_piece(pc, to); } if (type_of(m) == CASTLING) @@ -837,9 +838,9 @@ void Position::undo_move(Move m) { } else { - move_piece(us, pt, to, from); // Put the piece back at the source square + move_piece(pc, to, from); // Put the piece back at the source square - if (st->capturedType) + if (st->capturedPiece) { Square capsq = to; @@ -847,14 +848,14 @@ void Position::undo_move(Move m) { { capsq -= pawn_push(us); - assert(pt == PAWN); + assert(type_of(pc) == PAWN); assert(to == st->previous->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(capsq) == NO_PIECE); - assert(st->capturedType == PAWN); + assert(st->capturedPiece == make_piece(~us, PAWN)); } - put_piece(~us, st->capturedType, capsq); // Restore the captured piece + put_piece(st->capturedPiece, capsq); // Restore the captured piece } } @@ -867,7 +868,7 @@ void Position::undo_move(Move m) { /// Position::do_castling() is a helper used to do/undo a castling move. This -/// is a bit tricky, especially in Chess960. +/// is a bit tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { @@ -877,11 +878,11 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); // Remove both pieces first since squares could overlap in Chess960 - remove_piece(us, KING, Do ? from : to); - remove_piece(us, ROOK, Do ? rfrom : rto); + remove_piece(make_piece(us, KING), Do ? from : to); + remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us - put_piece(us, KING, Do ? to : from); - put_piece(us, ROOK, Do ? rto : rfrom); + put_piece(make_piece(us, KING), Do ? to : from); + put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } @@ -931,17 +932,16 @@ void Position::undo_null_move() { Key Position::key_after(Move m) const { - Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(piece_on(to)); + Piece pc = piece_on(from); + Piece captured = piece_on(to); Key k = st->key ^ Zobrist::side; if (captured) - k ^= Zobrist::psq[~us][captured][to]; + k ^= Zobrist::psq[captured][to]; - return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from]; + return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; } @@ -1000,6 +1000,18 @@ Value Position::see(Move m) const { if (!stmAttackers) return swapList[0]; + // Don't allow pinned pieces to attack as long all pinners (this includes also + // potential ones) are on their original square. When a pinner moves to the + // exchange-square or get captured on it, we fall back to standard SEE behaviour. + else if ( (stmAttackers & st->blockersForKing[stm]) + && ((st->pinnersForKing[stm] & (occupied ^ (occupied & to))) == st->pinnersForKing[stm])) + { + // Pinned pieces can't attack so remove them from attackers + stmAttackers ^= (stmAttackers & st->blockersForKing[stm]); + if (!stmAttackers) + return swapList[0]; + } + // The destination square is defended, which makes things rather more // difficult to compute. We proceed by building up a "swap list" containing // the material gain or loss at each stop in a sequence of captures to the @@ -1018,6 +1030,11 @@ Value Position::see(Move m) const { captured = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); stm = ~stm; stmAttackers = attackers & pieces(stm); + if ( stmAttackers + && (stmAttackers & st->blockersForKing[stm]) + && ((st->pinnersForKing[stm] & (occupied ^ (occupied & to))) == st->pinnersForKing[stm])) + stmAttackers ^= (stmAttackers & st->blockersForKing[stm]); + ++slIndex; } while (stmAttackers && (captured != KING || (--slIndex, false))); // Stop before a king capture @@ -1136,17 +1153,15 @@ bool Position::pos_is_ok(int* failedStep) const { } if (step == Lists) - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - if (pieceCount[c][pt] != popcount(pieces(c, pt))) - return false; + for (Piece pc : Pieces) + { + if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))) + return false; - for (int i = 0; i < pieceCount[c][pt]; ++i) - if ( board[pieceList[c][pt][i]] != make_piece(c, pt) - || index[pieceList[c][pt][i]] != i) - return false; - } + for (int i = 0; i < pieceCount[pc]; ++i) + if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) + return false; + } if (step == Castling) for (Color c = WHITE; c <= BLACK; ++c)