From bd87ab9ff508e826f7e8b62accc9ea4ddf477367 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 26 Jan 2013 22:07:28 +0100 Subject: [PATCH 01/30] Retire generate_king_moves() We have only one call place so inline its content. BTW, function is already declared as FORCE_INLINE. Also some small refactoring while there. No functional change. --- src/movegen.cpp | 56 ++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 3848611c..e5824d54 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -249,41 +249,38 @@ namespace { } - FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist, - Color us, Bitboard target) { - Square from = pos.king_square(us); - Bitboard b = pos.attacks_from(from) & target; - SERIALIZE(b); - return mlist; - } - - template FORCE_INLINE - MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us, - Bitboard target, const CheckInfo* ci = NULL) { + MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us, + Bitboard target, const CheckInfo* ci = NULL) { + + const bool Checks = Type == QUIET_CHECKS; mlist = (us == WHITE ? generate_pawn_moves(pos, mlist, target, ci) : generate_pawn_moves(pos, mlist, target, ci)); - mlist = generate_moves(pos, mlist, us, target, ci); - mlist = generate_moves(pos, mlist, us, target, ci); - mlist = generate_moves(pos, mlist, us, target, ci); - mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); if (Type != QUIET_CHECKS && Type != EVASIONS) - mlist = generate_king_moves(pos, mlist, us, target); + { + Square from = pos.king_square(us); + Bitboard b = pos.attacks_from(from) & target; + SERIALIZE(b); + } if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us)) { if (pos.is_chess960()) { - mlist = generate_castle(pos, mlist, us); - mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); } else { - mlist = generate_castle(pos, mlist, us); - mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); } } @@ -310,18 +307,12 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { assert(!pos.checkers()); Color us = pos.side_to_move(); - Bitboard target; - if (Type == CAPTURES) - target = pos.pieces(~us); + Bitboard target = Type == CAPTURES ? pos.pieces(~us) + : Type == QUIETS ? ~pos.pieces() + : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; - else if (Type == QUIETS) - target = ~pos.pieces(); - - else if (Type == NON_EVASIONS) - target = ~pos.pieces(us); - - return generate_all_moves(pos, mlist, us, target); + return generate_all(pos, mlist, us, target); } // Explicit template instantiations @@ -337,7 +328,6 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { assert(!pos.checkers()); - Color us = pos.side_to_move(); CheckInfo ci(pos); Bitboard dc = ci.dcCandidates; @@ -357,7 +347,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { SERIALIZE(b); } - return generate_all_moves(pos, mlist, us, ~pos.pieces(), &ci); + return generate_all(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci); } @@ -419,7 +409,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { // Generate blocking evasions or captures of the checking piece Bitboard target = between_bb(checksq, ksq) | pos.checkers(); - return generate_all_moves(pos, mlist, us, target); + return generate_all(pos, mlist, us, target); } From 52cab06fff5b84ecc252fb9d4137f8d92ab8dddf Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 27 Jan 2013 10:15:59 +0100 Subject: [PATCH 02/30] Don't prefetch if not needed Prefetch access to hash tables only in case we have changed pawn or material hash keys. No functional change. --- src/position.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index a005d5df..59935088 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -802,9 +802,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI pieceList[them][capture][index[lastSquare]] = lastSquare; pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE; - // Update hash keys + // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[them][capture][capsq]; st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; + prefetch((char*)thisThread->materialTable[st->materialKey]); // Update incremental scores st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq]; @@ -831,7 +832,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->castleRights &= ~cr; } - // Prefetch TT access as soon as we know key is updated + // Prefetch TT access as soon as we know the new hash key prefetch((char*)TT.first_entry(k)); // Move the piece @@ -894,17 +895,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->npMaterial[us] += PieceValue[MG][promotion]; } - // Update pawn hash key + // Update pawn hash key and prefetch access to pawnsTable st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; + prefetch((char*)thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter st->rule50 = 0; } - // Prefetch pawn and material hash tables - prefetch((char*)thisThread->pawnsTable[st->pawnKey]); - prefetch((char*)thisThread->materialTable[st->materialKey]); - // Update incremental scores st->psqScore += psq_delta(piece, from, to); From 76a0d3c05ac84cd2016ba304f6d5290d04450bec Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 27 Jan 2013 10:56:03 +0100 Subject: [PATCH 03/30] Get rid of some locals in do_castle_move() Rewrite the logic to get rid of kBefore and rBefore. No functional change. --- src/position.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 59935088..f3eb76f8 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1052,30 +1052,24 @@ void Position::do_castle_move(Move m) { assert(is_ok(m)); assert(type_of(m) == CASTLE); - Square kto, kfrom, rfrom, rto, kAfter, rAfter; - Color us = sideToMove; - Square kBefore = from_sq(m); - Square rBefore = to_sq(m); + Square kfrom, kto, rfrom, rto; - // Find after-castle squares for king and rook - if (rBefore > kBefore) // O-O + bool kingSide = to_sq(m) > from_sq(m); + kfrom = kto = from_sq(m); + rfrom = rto = to_sq(m); + + if (Do) { - kAfter = relative_square(us, SQ_G1); - rAfter = relative_square(us, SQ_F1); + kto = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); } - else // O-O-O + else { - kAfter = relative_square(us, SQ_C1); - rAfter = relative_square(us, SQ_D1); + kfrom = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + rfrom = relative_square(us, kingSide ? SQ_F1 : SQ_D1); } - kfrom = Do ? kBefore : kAfter; - rfrom = Do ? rBefore : rAfter; - - kto = Do ? kAfter : kBefore; - rto = Do ? rAfter : rBefore; - assert(piece_on(kfrom) == make_piece(us, KING)); assert(piece_on(rfrom) == make_piece(us, ROOK)); From 2218a5836a48a764e9b57f5996520b48e0b3d236 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 27 Jan 2013 11:45:01 +0100 Subject: [PATCH 04/30] Rewrite do_null_move() Use a more traditional approach, along the same lines of do_move(). It is true that we copy more in do_null_move(), but we save the work in undo_null_move(). Speed test shows the new code to be even a bit faster. No functional change. --- src/position.cpp | 72 +++++++++++++++++++++--------------------------- src/position.h | 3 +- src/search.cpp | 4 +-- 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index f3eb76f8..2cdcb4ae 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -953,10 +953,7 @@ void Position::undo_move(Move m) { sideToMove = ~sideToMove; if (type_of(m) == CASTLE) - { - do_castle_move(m); - return; - } + return do_castle_move(m); Color us = sideToMove; Color them = ~us; @@ -1082,11 +1079,9 @@ void Position::do_castle_move(Move m) { byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb; // Update board - Piece king = make_piece(us, KING); - Piece rook = make_piece(us, ROOK); board[kfrom] = board[rfrom] = NO_PIECE; - board[kto] = king; - board[rto] = rook; + board[kto] = make_piece(us, KING); + board[rto] = make_piece(us, ROOK); // Update piece lists pieceList[us][KING][index[kfrom]] = kto; @@ -1101,8 +1096,8 @@ void Position::do_castle_move(Move m) { st->capturedType = NO_PIECE_TYPE; // Update incremental scores - st->psqScore += psq_delta(king, kfrom, kto); - st->psqScore += psq_delta(rook, rfrom, rto); + st->psqScore += psq_delta(make_piece(us, KING), kfrom, kto); + st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto); // Update hash key st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto]; @@ -1132,47 +1127,42 @@ void Position::do_castle_move(Move m) { } -/// Position::do_null_move() is used to do/undo a "null move": It flips the side -/// to move and updates the hash key without executing any move on the board. -template -void Position::do_null_move(StateInfo& backupSt) { +/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips +/// the side to move without executing any move on the board. + +void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); - // Back up the information necessary to undo the null move to the supplied - // StateInfo object. Note that differently from normal case here backupSt - // is actually used as a backup storage not as the new state. This reduces - // the number of fields to be copied. - StateInfo* src = Do ? st : &backupSt; - StateInfo* dst = Do ? &backupSt : st; + memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here - dst->key = src->key; - dst->epSquare = src->epSquare; - dst->psqScore = src->psqScore; - dst->rule50 = src->rule50; - dst->pliesFromNull = src->pliesFromNull; + newSt.previous = st; + st = &newSt; + + if (st->epSquare != SQ_NONE) + { + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } + + st->key ^= Zobrist::side; + prefetch((char*)TT.first_entry(st->key)); + + st->rule50++; + st->pliesFromNull = 0; sideToMove = ~sideToMove; - if (Do) - { - if (st->epSquare != SQ_NONE) - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - - st->key ^= Zobrist::side; - prefetch((char*)TT.first_entry(st->key)); - - st->epSquare = SQ_NONE; - st->rule50++; - st->pliesFromNull = 0; - } - assert(pos_is_ok()); } -// Explicit template instantiations -template void Position::do_null_move(StateInfo& backupSt); -template void Position::do_null_move(StateInfo& backupSt); +void Position::undo_null_move() { + + assert(!checkers()); + + st = st->previous; + sideToMove = ~sideToMove; +} /// Position::see() is a static exchange evaluator: It tries to estimate the diff --git a/src/position.h b/src/position.h index b05d466e..235cc825 100644 --- a/src/position.h +++ b/src/position.h @@ -154,7 +154,8 @@ public: void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void undo_move(Move m); - template void do_null_move(StateInfo& st); + void do_null_move(StateInfo& st); + void undo_null_move(); // Static exchange evaluation int see(Move m) const; diff --git a/src/search.cpp b/src/search.cpp index cfc15736..15c3b15a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -667,12 +667,12 @@ namespace { if (eval - PawnValueMg > beta) R += ONE_PLY; - pos.do_null_move(st); + pos.do_null_move(st); (ss+1)->skipNullMove = true; nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : - search(pos, ss+1, -beta, -alpha, depth-R); (ss+1)->skipNullMove = false; - pos.do_null_move(st); + pos.undo_null_move(); if (nullValue >= beta) { From 483c98a69e8c6836c655d92a150d70fbe780341f Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 27 Jan 2013 18:48:27 +0100 Subject: [PATCH 05/30] Rewrite do_castle_move() And handle the castle directly in do/undo_move(). This allow to greatly simplify the code. Here the beast is the nasty Chess960 that is really tricky to get it right because could be that 'from' and 'to' squares are the same or that king's 'to' square is rook's 'from' square. Anyhow should work: verified on all Chess960 starting positions. No functional and no speed change also in Chess960. --- src/position.cpp | 175 ++++++++++++++++++----------------------------- src/position.h | 4 +- 2 files changed, 69 insertions(+), 110 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 2cdcb4ae..4045a25d 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -740,13 +740,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->rule50++; st->pliesFromNull++; - if (type_of(m) == CASTLE) - { - st->key = k; - do_castle_move(m); - return; - } - Color us = sideToMove; Color them = ~us; Square from = from_sq(m); @@ -756,9 +749,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); assert(color_of(piece) == us); - assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them); + assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); assert(capture != KING); + if (type_of(m) == CASTLE) + { + assert(piece == make_piece(us, KING)); + + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + + do_castle(from, to, rfrom, rto); + + st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto); + k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + } + if (capture) { Square capsq = to; @@ -835,19 +844,22 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI // Prefetch TT access as soon as we know the new hash key prefetch((char*)TT.first_entry(k)); - // Move the piece - Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; - byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[us] ^= from_to_bb; + // Move the piece. The tricky Chess960 castle is handled earlier + if (type_of(m) != CASTLE) + { + Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; + byTypeBB[ALL_PIECES] ^= from_to_bb; + byTypeBB[pt] ^= from_to_bb; + byColorBB[us] ^= from_to_bb; - board[to] = board[from]; - board[from] = NO_PIECE; + board[from] = NO_PIECE; + board[to] = piece; - // Update piece lists, index[from] is not updated and becomes stale. This - // works as long as index[] is accessed just by known occupied squares. - index[to] = index[from]; - pieceList[us][pt][index[to]] = to; + // Update piece lists, index[from] is not updated and becomes stale. This + // works as long as index[] is accessed just by known occupied squares. + index[to] = index[from]; + pieceList[us][pt][index[to]] = to; + } // If the moving piece is a pawn do some special extra work if (pt == PAWN) @@ -952,19 +964,14 @@ void Position::undo_move(Move m) { sideToMove = ~sideToMove; - if (type_of(m) == CASTLE) - return do_castle_move(m); - Color us = sideToMove; Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); - Piece piece = piece_on(to); - PieceType pt = type_of(piece); + PieceType pt = type_of(piece_on(to)); PieceType capture = st->capturedType; - assert(is_empty(from)); - assert(color_of(piece) == us); + assert(is_empty(from) || type_of(m) == CASTLE); assert(capture != KING); if (type_of(m) == PROMOTION) @@ -992,19 +999,32 @@ void Position::undo_move(Move m) { pt = PAWN; } - // Put the piece back at the source square - Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; - byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[us] ^= from_to_bb; + if (type_of(m) == CASTLE) + { + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + pt = KING; + do_castle(to, from, rto, rfrom); + } + else + { + // Put the piece back at the source square + Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; + byTypeBB[ALL_PIECES] ^= from_to_bb; + byTypeBB[pt] ^= from_to_bb; + byColorBB[us] ^= from_to_bb; - board[from] = board[to]; - board[to] = NO_PIECE; + board[to] = NO_PIECE; + board[from] = make_piece(us, pt); - // Update piece lists, index[to] is not updated and becomes stale. This - // works as long as index[] is accessed just by known occupied squares. - index[from] = index[to]; - pieceList[us][pt][index[from]] = from; + // Update piece lists, index[to] is not updated and becomes stale. This + // works as long as index[] is accessed just by known occupied squares. + index[from] = index[to]; + pieceList[us][pt][index[from]] = from; + } if (capture) { @@ -1039,38 +1059,12 @@ void Position::undo_move(Move m) { } -/// Position::do_castle_move() is a private method used to do/undo a castling -/// move. Note that castling moves are encoded as "king captures friendly rook" -/// moves, for instance white short castling in a non-Chess960 game is encoded -/// as e1h1. -template -void Position::do_castle_move(Move m) { +/// Position::do_castle() is a helper used to do/undo a castling move. This +/// is a bit tricky, especially in Chess960. - assert(is_ok(m)); - assert(type_of(m) == CASTLE); +void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { Color us = sideToMove; - Square kfrom, kto, rfrom, rto; - - bool kingSide = to_sq(m) > from_sq(m); - kfrom = kto = from_sq(m); - rfrom = rto = to_sq(m); - - if (Do) - { - kto = relative_square(us, kingSide ? SQ_G1 : SQ_C1); - rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); - } - else - { - kfrom = relative_square(us, kingSide ? SQ_G1 : SQ_C1); - rfrom = relative_square(us, kingSide ? SQ_F1 : SQ_D1); - } - - assert(piece_on(kfrom) == make_piece(us, KING)); - assert(piece_on(rfrom) == make_piece(us, ROOK)); - - // Move the pieces, with some care; in chess960 could be kto == rfrom Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto]; Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto]; byTypeBB[KING] ^= k_from_to_bb; @@ -1078,52 +1072,17 @@ void Position::do_castle_move(Move m) { byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb; byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb; - // Update board + // Could be from == to, so first set NO_PIECE then KING and ROOK board[kfrom] = board[rfrom] = NO_PIECE; board[kto] = make_piece(us, KING); board[rto] = make_piece(us, ROOK); - // Update piece lists - pieceList[us][KING][index[kfrom]] = kto; - pieceList[us][ROOK][index[rfrom]] = rto; - int tmp = index[rfrom]; // In Chess960 could be kto == rfrom - index[kto] = index[kfrom]; - index[rto] = tmp; - - if (Do) - { - // Reset capture field - st->capturedType = NO_PIECE_TYPE; - - // Update incremental scores - st->psqScore += psq_delta(make_piece(us, KING), kfrom, kto); - st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto); - - // Update hash key - st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto]; - st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; - - // Clear en passant square - if (st->epSquare != SQ_NONE) - { - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } - - // Update castling rights - st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]]; - st->castleRights &= ~castleRightsMask[kfrom]; - - // Update checkers BB - st->checkersBB = attackers_to(king_square(~us)) & pieces(us); - - sideToMove = ~sideToMove; - } - else - // Undo: point our state pointer back to the previous state - st = st->previous; - - assert(pos_is_ok()); + // Could be kfrom == rto, so use a 'tmp' variable + int tmp = index[kfrom]; + index[rto] = index[rfrom]; + index[kto] = tmp; + pieceList[us][KING][index[kto]] = kto; + pieceList[us][ROOK][index[rto]] = rto; } diff --git a/src/position.h b/src/position.h index 235cc825..8b475dd9 100644 --- a/src/position.h +++ b/src/position.h @@ -191,8 +191,8 @@ private: void put_piece(Piece p, Square s); void set_castle_right(Color c, Square rfrom); - // Helper template functions - template void do_castle_move(Move m); + // Helper functions + void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); template Bitboard hidden_checkers() const; // Computing hash keys from scratch (for initialization and debugging) From 58c9fbacc787cc3b654244c071169057e6895c91 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Mon, 28 Jan 2013 17:24:01 +0100 Subject: [PATCH 06/30] Revert "Extend full 3 fold detection to PvNodes" --- src/position.cpp | 24 ++++++++++++++---------- src/position.h | 2 +- src/search.cpp | 6 +++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 4045a25d..310e46e3 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1363,31 +1363,36 @@ 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 +template bool Position::is_draw() const { + // Draw by material? if ( !pieces(PAWN) && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg)) return true; + // Draw by the 50 moves rule? if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; - if (CheckRepetition) + // Draw by repetition? + if (!SkipRepetition) { - int i = 4, e = std::min(st->rule50, st->pliesFromNull), cnt; + int i = 4, e = std::min(st->rule50, st->pliesFromNull); if (i <= e) { StateInfo* stp = st->previous->previous; - for (cnt = 0; i <= e; i += 2) - { + do { stp = stp->previous->previous; - if (stp->key == st->key && (!CheckThreeFold || ++cnt >= 2)) + if (stp->key == st->key) return true; - } + + i += 2; + + } while (i <= e); } } @@ -1395,9 +1400,8 @@ bool Position::is_draw() const { } // Explicit template instantiations -template bool Position::is_draw() const; -template bool Position::is_draw() const; -template bool Position::is_draw() const; +template bool Position::is_draw() const; +template bool Position::is_draw() const; /// Position::flip() flips position with the white and black sides reversed. This diff --git a/src/position.h b/src/position.h index 8b475dd9..579b5308 100644 --- a/src/position.h +++ b/src/position.h @@ -179,7 +179,7 @@ public: Thread* this_thread() const; int64_t nodes_searched() const; void set_nodes_searched(int64_t n); - template bool is_draw() const; + template bool is_draw() const; // Position consistency check, for debugging bool pos_is_ok(int* failedStep = NULL) const; diff --git a/src/search.cpp b/src/search.cpp index 15c3b15a..cb7b30b8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -534,7 +534,7 @@ namespace { if (!RootNode) { // Step 2. Check for aborted search and immediate draw - if (Signals.stop || pos.is_draw() || ss->ply > MAX_PLY) + if (Signals.stop || pos.is_draw() || ss->ply > MAX_PLY) return DrawValue[pos.side_to_move()]; // Step 3. Mate distance pruning. Even if we mate at the next move our score @@ -1109,7 +1109,7 @@ split_point_start: // At split points actual search starts from here ss->ply = (ss-1)->ply + 1; // Check for an instant draw or maximum ply reached - if (pos.is_draw() || ss->ply > MAX_PLY) + if (pos.is_draw() || ss->ply > MAX_PLY) return DrawValue[pos.side_to_move()]; // Transposition table lookup. At PV nodes, we don't use the TT for @@ -1516,7 +1516,7 @@ void RootMove::extract_pv_from_tt(Position& pos) { && pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change && pos.pl_move_is_legal(m, pos.pinned_pieces()) && ply < MAX_PLY - && (!pos.is_draw() || ply < 2)); + && (!pos.is_draw() || ply < 2)); pv.push_back(MOVE_NONE); // Must be zero-terminating From 0901e12102cf61559e6bfbbda0a040c72048dc8c Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 2 Feb 2013 06:43:11 +0100 Subject: [PATCH 07/30] Revert "Simplify Evaluation" This reverts commit 496c7497cb81de4383 --- src/evaluate.cpp | 15 ++++++++++++++- src/search.cpp | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 984a85a6..49dc07fe 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -577,6 +577,18 @@ Value do_evaluate(const Position& pos, Value& margin) { mobility += MobilityBonus[Piece][mob]; + // Add a bonus if a slider is pinning an enemy piece + if ( (Piece == BISHOP || Piece == ROOK || Piece == QUEEN) + && (PseudoAttacks[Piece][pos.king_square(Them)] & s)) + { + b = BetweenBB[s][pos.king_square(Them)] & pos.pieces(); + + assert(b); + + if (!more_than_one(b) && (b & pos.pieces(Them))) + score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))]; + } + // Decrease score if we are attacked by an enemy pawn. Remaining part // of threat evaluation must be done later when we have full attack info. if (ei.attackedBy[Them][PAWN] & s) @@ -687,7 +699,8 @@ Value do_evaluate(const Position& pos, Value& margin) { & ~ei.attackedBy[Them][0]; if (undefendedMinors) - score += UndefendedMinorPenalty; + score += more_than_one(undefendedMinors) ? UndefendedMinorPenalty * 2 + : UndefendedMinorPenalty; // Enemy pieces not defended by a pawn and under our attack weakEnemies = pos.pieces(Them) diff --git a/src/search.cpp b/src/search.cpp index cb7b30b8..c516f3c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1343,6 +1343,7 @@ split_point_start: // At split points actual search starts from here Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt; while (b) { + // Note that here we generate illegal "double move"! if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta) return true; } From 9f94d2280110a1ba5eb9eb15b0756883037a817d Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 2 Feb 2013 06:44:20 +0100 Subject: [PATCH 08/30] Restore "fail-low of reduced" and close regression This reverts "Threat Extensions" and is the last of this revert series. In single-thread tests we should now be on par with 2.3.1 --- src/search.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c516f3c4..e8809f14 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -99,6 +99,7 @@ namespace { Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta); + bool allows_move(const Position& pos, Move first, Move second); bool prevents_move(const Position& pos, Move first, Move second); string uci_pv(const Position& pos, int depth, Value alpha, Value beta); @@ -692,9 +693,21 @@ namespace { return nullValue; } else + { // The null move failed low, which means that we may be faced with - // some kind of threat. + // some kind of threat. If the previous move was reduced, check if + // the move that refuted the null move was somehow connected to the + // move which was reduced. If a connection is found, return a fail + // low score (which will cause the reduced move to fail high in the + // parent node, which will trigger a re-search with full depth). threatMove = (ss+1)->currentMove; + + if ( depth < 5 * ONE_PLY + && (ss-1)->reduction + && threatMove != MOVE_NONE + && allows_move(pos, (ss-1)->currentMove, threatMove)) + return beta - 1; + } } // Step 9. ProbCut (is omitted in PV nodes) @@ -847,12 +860,13 @@ split_point_start: // At split points actual search starts from here && !inCheck && !dangerous && move != ttMove - && (!threatMove || !prevents_move(pos, move, threatMove)) && (bestValue > VALUE_MATED_IN_MAX_PLY || ( bestValue == -VALUE_INFINITE && alpha > VALUE_MATED_IN_MAX_PLY))) { // Move count based pruning - if (depth < 16 * ONE_PLY && moveCount >= FutilityMoveCounts[depth]) + if ( depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[depth] + && (!threatMove || !prevents_move(pos, move, threatMove))) { if (SpNode) sp->mutex.lock(); @@ -1352,6 +1366,47 @@ split_point_start: // At split points actual search starts from here } + // allows_move() tests whether the move at previous ply (first) somehow makes a + // second move possible, for instance if the moving piece is the same in both + // moves. Normally the second move is the threat move (the best move returned + // from a null search that fails low). + + bool allows_move(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move()); + assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // The piece is the same or second's destination was vacated by the first move + if (m1to == m2from || m2to == m1from) + return true; + + // Second one moves through the square vacated by first one + if (between_bb(m2from, m2to) & m1from) + return true; + + // Second's destination is defended by the first move's piece + Bitboard m1att = pos.attacks_from(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from); + if (m1att & m2to) + return true; + + // Second move gives a discovered check through the first's checking piece + if (m1att & pos.king_square(pos.side_to_move())) + { + assert(between_bb(m1to, pos.king_square(pos.side_to_move())) & m2from); + return true; + } + + return false; + } + + // prevents_move() tests whether a move (first) is able to defend against an // opponent's move (second). In this case will not be pruned. Normally the // second move is the threat move (the best move returned from a null search From 53051eefc741586f72ccbf9a765592c4ca6030bd Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 2 Feb 2013 16:04:41 +0100 Subject: [PATCH 09/30] Retire history.h And move the contents to movepick.cpp, where they are mostly used. Idea from DiscoCheck. No functional change (bench 5379503) --- src/history.h | 72 ------------------------------------------------ src/movepick.cpp | 25 ++++++++++++++--- src/movepick.h | 26 ++++++++++++++++- src/search.cpp | 5 ++-- src/thread.cpp | 4 ++- 5 files changed, 51 insertions(+), 81 deletions(-) delete mode 100644 src/history.h diff --git a/src/history.h b/src/history.h deleted file mode 100644 index c5f72f22..00000000 --- a/src/history.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2012 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#if !defined(HISTORY_H_INCLUDED) -#define HISTORY_H_INCLUDED - -#include -#include - -#include "types.h" - -/// The History class stores statistics about how often different moves -/// have been successful or unsuccessful during the current search. These -/// statistics are used for reduction and move ordering decisions. History -/// entries are stored according only to moving piece and destination square, -/// in particular two moves with different origin but same destination and -/// same piece will be considered identical. - -class History { - -public: - void clear(); - Value value(Piece p, Square to) const; - void add(Piece p, Square to, Value bonus); - Value gain(Piece p, Square to) const; - void update_gain(Piece p, Square to, Value g); - - static const Value MaxValue = Value(2000); - -private: - Value history[PIECE_NB][SQUARE_NB]; - Value maxGains[PIECE_NB][SQUARE_NB]; -}; - -inline void History::clear() { - memset(history, 0, 16 * 64 * sizeof(Value)); - memset(maxGains, 0, 16 * 64 * sizeof(Value)); -} - -inline Value History::value(Piece p, Square to) const { - return history[p][to]; -} - -inline void History::add(Piece p, Square to, Value bonus) { - if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus; -} - -inline Value History::gain(Piece p, Square to) const { - return maxGains[p][to]; -} - -inline void History::update_gain(Piece p, Square to, Value g) { - maxGains[p][to] = std::max(g, maxGains[p][to] - 1); -} - -#endif // !defined(HISTORY_H_INCLUDED) diff --git a/src/movepick.cpp b/src/movepick.cpp index 794fbde9..e2bb9741 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -52,6 +52,23 @@ namespace { } +/// History class method definitions + +void History::clear() { + memset(history, 0, sizeof(history)); + memset(gains, 0, sizeof(gains)); +} + +void History::update(Piece p, Square to, Value bonus) { + if (abs(history[p][to] + bonus) < History::Max) + history[p][to] += bonus; +} + +void History::update_gain(Piece p, Square to, Value gain) { + gains[p][to] = std::max(gain, gains[p][to] - 1); +} + + /// Constructors of the MovePicker class. As arguments we pass information /// to help it to return the presumably good moves first, to decide which /// moves to return (in the quiescence search, for instance, we only want to @@ -180,7 +197,7 @@ void MovePicker::score_noncaptures() { for (MoveStack* it = moves; it != end; ++it) { m = it->move; - it->score = H.value(pos.piece_moved(m), to_sq(m)); + it->score = H[pos.piece_moved(m)][to_sq(m)]; } } @@ -198,12 +215,12 @@ void MovePicker::score_evasions() { { m = it->move; if ((seeScore = pos.see_sign(m)) < 0) - it->score = seeScore - History::MaxValue; // Be sure we are at the bottom + it->score = seeScore - History::Max; // Be sure we are at the bottom else if (pos.is_capture(m)) it->score = PieceValue[MG][pos.piece_on(to_sq(m))] - - type_of(pos.piece_moved(m)) + History::MaxValue; + - type_of(pos.piece_moved(m)) + History::Max; else - it->score = H.value(pos.piece_moved(m), to_sq(m)); + it->score = H[pos.piece_moved(m)][to_sq(m)]; } } diff --git a/src/movepick.h b/src/movepick.h index 9459b8c2..7d6651bb 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -20,12 +20,36 @@ #if !defined MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED -#include "history.h" #include "position.h" #include "search.h" #include "types.h" +/// The History class stores statistics about how often different moves +/// have been successful or unsuccessful during the current search. These +/// statistics are used for reduction and move ordering decisions. History +/// entries are stored according only to moving piece and destination square, +/// in particular two moves with different origin but same destination and +/// same piece will be considered identical. + +class History { +public: + + static const Value Max = Value(2000); + + const Value* operator[](Piece p) const { return &history[p][0]; } + Value gain(Piece p, Square to) const { return gains[p][to]; } + + void clear(); + void update(Piece p, Square to, Value bonus); + void update_gain(Piece p, Square to, Value gain); + +private: + Value history[PIECE_NB][SQUARE_NB]; + Value gains[PIECE_NB][SQUARE_NB]; +}; + + /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a /// new pseudo legal move each time it is called, until there are no moves left, diff --git a/src/search.cpp b/src/search.cpp index e8809f14..aa5ef13c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -26,7 +26,6 @@ #include "book.h" #include "evaluate.h" -#include "history.h" #include "movegen.h" #include "movepick.h" #include "notation.h" @@ -1071,13 +1070,13 @@ split_point_start: // At split points actual search starts from here // Increase history value of the cut-off move Value bonus = Value(int(depth) * int(depth)); - H.add(pos.piece_moved(bestMove), to_sq(bestMove), bonus); + H.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); // Decrease history of all the other played non-capture moves for (int i = 0; i < playedMoveCount - 1; i++) { Move m = movesSearched[i]; - H.add(pos.piece_moved(m), to_sq(m), -bonus); + H.update(pos.piece_moved(m), to_sq(m), -bonus); } } } diff --git a/src/thread.cpp b/src/thread.cpp index 14c778f8..316ed76b 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#include // For std::count #include #include @@ -370,7 +371,8 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, RootMoves.clear(); for (MoveList ml(pos); !ml.end(); ++ml) - if (searchMoves.empty() || count(searchMoves.begin(), searchMoves.end(), ml.move())) + if ( searchMoves.empty() + || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) RootMoves.push_back(RootMove(ml.move())); main_thread()->thinking = true; From ddbe6082c47befcfe2bd2e778866c8fbda33b8e2 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 2 Feb 2013 17:13:33 +0100 Subject: [PATCH 10/30] Unify History and Gains under a single Stats class Handling of History and Gains is almost the same, with the exception of the update logic, so unify both classes under a single Stats struct. No functional change. --- src/movepick.cpp | 28 +++++----------------------- src/movepick.h | 45 ++++++++++++++++++++++++++++----------------- src/search.cpp | 20 +++++++++++--------- src/thread.cpp | 4 ++-- 4 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index e2bb9741..e6b16cc4 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -18,7 +18,6 @@ along with this program. If not, see . */ -#include #include #include "movegen.h" @@ -52,23 +51,6 @@ namespace { } -/// History class method definitions - -void History::clear() { - memset(history, 0, sizeof(history)); - memset(gains, 0, sizeof(gains)); -} - -void History::update(Piece p, Square to, Value bonus) { - if (abs(history[p][to] + bonus) < History::Max) - history[p][to] += bonus; -} - -void History::update_gain(Piece p, Square to, Value gain) { - gains[p][to] = std::max(gain, gains[p][to] - 1); -} - - /// Constructors of the MovePicker class. As arguments we pass information /// to help it to return the presumably good moves first, to decide which /// moves to return (in the quiescence search, for instance, we only want to @@ -76,7 +58,7 @@ void History::update_gain(Piece p, Square to, Value gain) { /// move ordering is at the current node. MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, - Search::Stack* s, Value beta) : pos(p), H(h), depth(d) { + Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) { assert(d > DEPTH_ZERO); @@ -109,7 +91,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, } MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, - Square sq) : pos(p), H(h), cur(moves), end(moves) { + Square sq) : pos(p), Hist(h), cur(moves), end(moves) { assert(d <= DEPTH_ZERO); @@ -141,7 +123,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, } MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt) - : pos(p), H(h), cur(moves), end(moves) { + : pos(p), Hist(h), cur(moves), end(moves) { assert(!pos.checkers()); @@ -197,7 +179,7 @@ void MovePicker::score_noncaptures() { for (MoveStack* it = moves; it != end; ++it) { m = it->move; - it->score = H[pos.piece_moved(m)][to_sq(m)]; + it->score = Hist[pos.piece_moved(m)][to_sq(m)]; } } @@ -220,7 +202,7 @@ void MovePicker::score_evasions() { it->score = PieceValue[MG][pos.piece_on(to_sq(m))] - type_of(pos.piece_moved(m)) + History::Max; else - it->score = H[pos.piece_moved(m)][to_sq(m)]; + it->score = Hist[pos.piece_moved(m)][to_sq(m)]; } } diff --git a/src/movepick.h b/src/movepick.h index 7d6651bb..3be429ed 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -20,35 +20,46 @@ #if !defined MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED +#include // For std::max +#include // For memset + #include "position.h" #include "search.h" #include "types.h" -/// The History class stores statistics about how often different moves -/// have been successful or unsuccessful during the current search. These -/// statistics are used for reduction and move ordering decisions. History -/// entries are stored according only to moving piece and destination square, -/// in particular two moves with different origin but same destination and -/// same piece will be considered identical. - -class History { -public: +/// The Stats struct stores moves statistics. According to the template parameter +/// the class can store both History and Gains type statistics. History records +/// how often different moves have been successful or unsuccessful during the +/// current search and is used for reduction and move ordering decisions. Gains +/// records the move's best evaluation gain from one ply to the next and is used +/// for pruning decisions. Entries are stored according only to moving piece and +/// destination square, in particular two moves with different origin but same +/// destination and same piece will be considered identical. +template +struct Stats { static const Value Max = Value(2000); - const Value* operator[](Piece p) const { return &history[p][0]; } - Value gain(Piece p, Square to) const { return gains[p][to]; } + const Value* operator[](Piece p) const { return &table[p][0]; } + void clear() { memset(table, 0, sizeof(table)); } - void clear(); - void update(Piece p, Square to, Value bonus); - void update_gain(Piece p, Square to, Value gain); + void update(Piece p, Square to, Value v) { + + if (Gain) + table[p][to] = std::max(v, table[p][to] - 1); + + else if (abs(table[p][to] + v) < Max) + table[p][to] += v; + } private: - Value history[PIECE_NB][SQUARE_NB]; - Value gains[PIECE_NB][SQUARE_NB]; + Value table[PIECE_NB][SQUARE_NB]; }; +typedef Stats History; +typedef Stats Gains; + /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a @@ -74,7 +85,7 @@ private: void generate_next(); const Position& pos; - const History& H; + const History& Hist; Search::Stack* ss; Depth depth; Move ttMove; diff --git a/src/search.cpp b/src/search.cpp index aa5ef13c..61932263 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -86,7 +86,8 @@ namespace { TimeManager TimeMgr; int BestMoveChanges; Value DrawValue[COLOR_NB]; - History H; + History Hist; + Gains Gain; template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); @@ -299,7 +300,8 @@ namespace { bestValue = delta = -VALUE_INFINITE; ss->currentMove = MOVE_NULL; // Hack to skip update gains TT.new_search(); - H.clear(); + Hist.clear(); + Gain.clear(); PVSize = Options["MultiPV"]; Skill skill(Options["Skill Level"]); @@ -617,7 +619,7 @@ namespace { && type_of(move) == NORMAL) { Square to = to_sq(move); - H.update_gain(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); + Gain.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); } // Step 6. Razoring (is omitted in PV nodes) @@ -727,7 +729,7 @@ namespace { assert((ss-1)->currentMove != MOVE_NONE); assert((ss-1)->currentMove != MOVE_NULL); - MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); + MovePicker mp(pos, ttMove, Hist, pos.captured_piece_type()); CheckInfo ci(pos); while ((move = mp.next_move()) != MOVE_NONE) @@ -759,7 +761,7 @@ namespace { split_point_start: // At split points actual search starts from here - MovePicker mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); + MovePicker mp(pos, ttMove, depth, Hist, ss, PvNode ? -VALUE_INFINITE : beta); CheckInfo ci(pos); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc singularExtensionNode = !RootNode @@ -878,7 +880,7 @@ split_point_start: // At split points actual search starts from here // but fixing this made program slightly weaker. Depth predictedDepth = newDepth - reduction(depth, moveCount); futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) - + H.gain(pos.piece_moved(move), to_sq(move)); + + Gain[pos.piece_moved(move)][to_sq(move)]; if (futilityValue < beta) { @@ -1070,13 +1072,13 @@ split_point_start: // At split points actual search starts from here // Increase history value of the cut-off move Value bonus = Value(int(depth) * int(depth)); - H.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); + Hist.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); // Decrease history of all the other played non-capture moves for (int i = 0; i < playedMoveCount - 1; i++) { Move m = movesSearched[i]; - H.update(pos.piece_moved(m), to_sq(m), -bonus); + Hist.update(pos.piece_moved(m), to_sq(m), -bonus); } } } @@ -1188,7 +1190,7 @@ split_point_start: // At split points actual search starts from here // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. - MovePicker mp(pos, ttMove, depth, H, to_sq((ss-1)->currentMove)); + MovePicker mp(pos, ttMove, depth, Hist, to_sq((ss-1)->currentMove)); CheckInfo ci(pos); // Loop through the moves until no moves remain or a beta cutoff occurs diff --git a/src/thread.cpp b/src/thread.cpp index 316ed76b..1a396516 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -371,8 +371,8 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, RootMoves.clear(); for (MoveList ml(pos); !ml.end(); ++ml) - if ( searchMoves.empty() - || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) + if ( searchMoves.empty() + || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) RootMoves.push_back(RootMove(ml.move())); main_thread()->thinking = true; From 5f58db8c9971d8709093ca3c97cecdc99c61303e Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 3 Feb 2013 09:01:55 +0100 Subject: [PATCH 11/30] Correctly score enpassant captures Surprisingly this rare case was not considered when scoring a capture. Also take in account that in the promotion case we gain a new piece (typically a queen) but we lose the promoting pawn. These small issues were present since Glaurung times! Found while browsing DiscoCheck sources bench: 5400063 --- src/movepick.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index e6b16cc4..4cf45ad3 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -168,7 +168,10 @@ void MovePicker::score_captures() { - type_of(pos.piece_moved(m)); if (type_of(m) == PROMOTION) - it->score += PieceValue[MG][promotion_type(m)]; + it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; + + else if (type_of(m) == ENPASSANT) + it->score += PieceValue[MG][PAWN]; } } From 1c4e6d7ea2d7fb6892ae56889109b32cc98fd79a Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 3 Feb 2013 09:16:44 +0100 Subject: [PATCH 12/30] Rename prevents_move() to refutes() Better! From DiscoCheck. No functional change. --- src/search.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 61932263..86f6fff0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -99,8 +99,8 @@ namespace { Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta); - bool allows_move(const Position& pos, Move first, Move second); - bool prevents_move(const Position& pos, Move first, Move second); + bool allows(const Position& pos, Move first, Move second); + bool refutes(const Position& pos, Move first, Move second); string uci_pv(const Position& pos, int depth, Value alpha, Value beta); struct Skill { @@ -706,7 +706,7 @@ namespace { if ( depth < 5 * ONE_PLY && (ss-1)->reduction && threatMove != MOVE_NONE - && allows_move(pos, (ss-1)->currentMove, threatMove)) + && allows(pos, (ss-1)->currentMove, threatMove)) return beta - 1; } } @@ -867,7 +867,7 @@ split_point_start: // At split points actual search starts from here // Move count based pruning if ( depth < 16 * ONE_PLY && moveCount >= FutilityMoveCounts[depth] - && (!threatMove || !prevents_move(pos, move, threatMove))) + && (!threatMove || !refutes(pos, move, threatMove))) { if (SpNode) sp->mutex.lock(); @@ -1367,12 +1367,12 @@ split_point_start: // At split points actual search starts from here } - // allows_move() tests whether the move at previous ply (first) somehow makes a - // second move possible, for instance if the moving piece is the same in both - // moves. Normally the second move is the threat move (the best move returned + // allows() tests whether the 'first' move at previous ply somehow makes the + // 'second' move possible, for instance if the moving piece is the same in + // both moves. Normally the second move is the threat (the best move returned // from a null search that fails low). - bool allows_move(const Position& pos, Move first, Move second) { + bool allows(const Position& pos, Move first, Move second) { assert(is_ok(first)); assert(is_ok(second)); @@ -1408,12 +1408,11 @@ split_point_start: // At split points actual search starts from here } - // prevents_move() tests whether a move (first) is able to defend against an - // opponent's move (second). In this case will not be pruned. Normally the - // second move is the threat move (the best move returned from a null search - // that fails low). + // refutes() tests whether a 'first' move is able to defend against a 'second' + // opponent's move. In this case will not be pruned. Normally the second move + // is the threat (the best move returned from a null search that fails low). - bool prevents_move(const Position& pos, Move first, Move second) { + bool refutes(const Position& pos, Move first, Move second) { assert(is_ok(first)); assert(is_ok(second)); From 08d615cc9500713d89bd20dd0963258932abf627 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 3 Feb 2013 09:52:39 +0100 Subject: [PATCH 13/30] Templetize score_xxx() functions So to be style-wise aligned with the corresponding generate() functions. No functional change. --- src/movepick.cpp | 45 +++++++++++++++++++++------------------------ src/movepick.h | 5 ++--- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 4cf45ad3..17516603 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -20,7 +20,6 @@ #include -#include "movegen.h" #include "movepick.h" #include "thread.h" @@ -140,12 +139,10 @@ MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType } -/// MovePicker::score_captures(), MovePicker::score_noncaptures() and -/// MovePicker::score_evasions() assign a numerical move ordering score -/// to each move in a move list. The moves with highest scores will be -/// picked first by next_move(). - -void MovePicker::score_captures() { +/// score() assign a numerical move ordering score to each move in a move list. +/// The moves with highest scores will be picked first. +template<> +void MovePicker::score() { // Winning and equal captures in the main search are ordered by MVV/LVA. // Suprisingly, this appears to perform slightly better than SEE based // move ordering. The reason is probably that in a position with a winning @@ -175,7 +172,8 @@ void MovePicker::score_captures() { } } -void MovePicker::score_noncaptures() { +template<> +void MovePicker::score() { Move m; @@ -186,21 +184,20 @@ void MovePicker::score_noncaptures() { } } -void MovePicker::score_evasions() { +template<> +void MovePicker::score() { // Try good captures ordered by MVV/LVA, then non-captures if destination square // is not under attack, ordered by history value, then bad-captures and quiet // moves with a negative SEE. This last group is ordered by the SEE score. Move m; int seeScore; - if (end < moves + 2) - return; - for (MoveStack* it = moves; it != end; ++it) { m = it->move; if ((seeScore = pos.see_sign(m)) < 0) - it->score = seeScore - History::Max; // Be sure we are at the bottom + it->score = seeScore - History::Max; // At the bottom + else if (pos.is_capture(m)) it->score = PieceValue[MG][pos.piece_on(to_sq(m))] - type_of(pos.piece_moved(m)) + History::Max; @@ -210,8 +207,8 @@ void MovePicker::score_evasions() { } -/// MovePicker::generate_next() generates, scores and sorts the next bunch of moves, -/// when there are no more moves to try for the current phase. +/// generate_next() generates, scores and sorts the next bunch of moves, when +/// there are no more moves to try for the current phase. void MovePicker::generate_next() { @@ -221,7 +218,7 @@ void MovePicker::generate_next() { case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: end = generate(pos, moves); - score_captures(); + score(); return; case KILLERS_S1: @@ -231,7 +228,7 @@ void MovePicker::generate_next() { case QUIETS_1_S1: endQuiets = end = generate(pos, moves); - score_noncaptures(); + score(); end = std::partition(cur, end, has_positive_score); sort(cur, end); return; @@ -251,7 +248,8 @@ void MovePicker::generate_next() { case EVASIONS_S2: end = generate(pos, moves); - score_evasions(); + if (end > moves + 1) + score(); return; case QUIET_CHECKS_S3: @@ -270,11 +268,10 @@ void MovePicker::generate_next() { } -/// MovePicker::next_move() is the most important method of the MovePicker class. -/// It returns a new pseudo legal move every time it is called, until there -/// are no more moves left. It picks the move with the biggest score from a list -/// of generated moves taking care not to return the tt move if has already been -/// searched previously. +/// next_move() is the most important method of the MovePicker class. It returns +/// a new pseudo legal move every time is called, until there are no more moves +/// left. It picks the move with the biggest score from a list of generated moves +/// taking care not returning the ttMove if has already been searched previously. template<> Move MovePicker::next_move() { @@ -361,6 +358,6 @@ Move MovePicker::next_move() { /// Version of next_move() to use at split point nodes where the move is grabbed /// from the split point's shared MovePicker object. This function is not thread -/// safe so should be lock protected by the caller. +/// safe so must be lock protected by the caller. template<> Move MovePicker::next_move() { return ss->sp->mp->next_move(); } diff --git a/src/movepick.h b/src/movepick.h index 3be429ed..5b40ded8 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -23,6 +23,7 @@ #include // For std::max #include // For memset +#include "movegen.h" #include "position.h" #include "search.h" #include "types.h" @@ -79,9 +80,7 @@ public: template Move next_move(); private: - void score_captures(); - void score_noncaptures(); - void score_evasions(); + template void score(); void generate_next(); const Position& pos; From 6560e4cc5b3da33c16a2a3290bbb3697f6c69b00 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 3 Feb 2013 11:14:21 +0100 Subject: [PATCH 14/30] Be clear about not LMR the ttMove Currently a ttMove is reduced with ss->reduction = DEPTH_ZERO, so it is actually not reduced (as it should be), but the trick works just becuase it happens that ttMove is the first to be tried and reduction(depth, 1) Always returns zero. So explicitly forbid reduction of ttMove in the LMR condition. This is much clear and self-documented. No functional change. --- src/search.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 86f6fff0..27669d9d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -400,6 +400,7 @@ namespace { // Sort the PV lines searched so far and update the GUI sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); + if (PVIdx + 1 == PVSize || Time::now() - SearchTime > 3000) sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; } @@ -922,8 +923,9 @@ split_point_start: // At split points actual search starts from here && !pvMove && !captureOrPromotion && !dangerous - && ss->killers[0] != move - && ss->killers[1] != move) + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1]) { ss->reduction = reduction(depth, moveCount); Depth d = std::max(newDepth - ss->reduction, ONE_PLY); From b8c5ea869ca80338f8b2fa6815fc92349b889750 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Mon, 4 Feb 2013 19:48:56 +0100 Subject: [PATCH 15/30] Some renaming in split() Naming suggested by jundery. No functional change. --- src/movepick.cpp | 2 +- src/search.cpp | 8 ++++---- src/thread.cpp | 34 +++++++++++++++++----------------- src/thread.h | 6 +++--- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 17516603..72601bb3 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -360,4 +360,4 @@ Move MovePicker::next_move() { /// from the split point's shared MovePicker object. This function is not thread /// safe so must be lock protected by the caller. template<> -Move MovePicker::next_move() { return ss->sp->mp->next_move(); } +Move MovePicker::next_move() { return ss->sp->movePicker->next_move(); } diff --git a/src/search.cpp b/src/search.cpp index 27669d9d..4adcefa9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1616,7 +1616,7 @@ void Thread::idle_loop() { // at the thread creation. So it means we are the split point's master. const SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL; - assert(!this_sp || (this_sp->master == this && searching)); + assert(!this_sp || (this_sp->masterThread == this && searching)); // If this thread is the master of a split point and all slaves have finished // their work at this split point, return from the idle loop. @@ -1700,11 +1700,11 @@ void Thread::idle_loop() { // Wake up master thread so to allow it to return from the idle loop // in case we are the last slave of the split point. if ( Threads.sleepWhileIdle - && this != sp->master + && this != sp->masterThread && !sp->slavesMask) { - assert(!sp->master->searching); - sp->master->notify_one(); + assert(!sp->masterThread->searching); + sp->masterThread->notify_one(); } // After releasing the lock we cannot access anymore any SplitPoint diff --git a/src/thread.cpp b/src/thread.cpp index 1a396516..7be8da88 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -147,7 +147,7 @@ void Thread::wait_for(volatile const bool& b) { bool Thread::cutoff_occurred() const { - for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parent) + for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint) if (sp->cutoff) return true; @@ -258,17 +258,17 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, assert(bestValue > -VALUE_INFINITE); assert(depth >= Threads.minimumSplitDepth); - Thread* master = pos.this_thread(); + Thread* thisThread = pos.this_thread(); - assert(master->searching); - assert(master->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); + assert(thisThread->searching); + assert(thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); // Pick the next available split point from the split point stack - SplitPoint& sp = master->splitPoints[master->splitPointsSize]; + SplitPoint& sp = thisThread->splitPoints[thisThread->splitPointsSize]; - sp.master = master; - sp.parent = master->activeSplitPoint; - sp.slavesMask = 1ULL << master->idx; + sp.masterThread = thisThread; + sp.parentSplitPoint = thisThread->activeSplitPoint; + sp.slavesMask = 1ULL << thisThread->idx; sp.depth = depth; sp.bestMove = *bestMove; sp.threatMove = threatMove; @@ -276,7 +276,7 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, sp.beta = beta; sp.nodeType = nodeType; sp.bestValue = bestValue; - sp.mp = ∓ + sp.movePicker = ∓ sp.moveCount = moveCount; sp.pos = &pos; sp.nodes = 0; @@ -289,13 +289,13 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, mutex.lock(); sp.mutex.lock(); - master->splitPointsSize++; - master->activeSplitPoint = &sp; + thisThread->splitPointsSize++; + thisThread->activeSplitPoint = &sp; size_t slavesCnt = 1; // Master is always included for (size_t i = 0; i < threads.size() && !Fake; ++i) - if (threads[i]->is_available_to(master) && ++slavesCnt <= maxThreadsPerSplitPoint) + if (threads[i]->is_available_to(thisThread) && ++slavesCnt <= maxThreadsPerSplitPoint) { sp.slavesMask |= 1ULL << threads[i]->idx; threads[i]->activeSplitPoint = &sp; @@ -312,11 +312,11 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, // their work at this split point. if (slavesCnt > 1 || Fake) { - master->Thread::idle_loop(); // Force a call to base class idle_loop() + thisThread->Thread::idle_loop(); // Force a call to base class idle_loop() // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. - assert(!master->searching); + assert(!thisThread->searching); } // We have returned from the idle loop, which means that all threads are @@ -325,9 +325,9 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, mutex.lock(); sp.mutex.lock(); - master->searching = true; - master->splitPointsSize--; - master->activeSplitPoint = sp.parent; + thisThread->searching = true; + thisThread->splitPointsSize--; + thisThread->activeSplitPoint = sp.parentSplitPoint; pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); *bestMove = sp.bestMove; diff --git a/src/thread.h b/src/thread.h index ad17e8b2..d24b65dc 100644 --- a/src/thread.h +++ b/src/thread.h @@ -63,15 +63,15 @@ struct SplitPoint { // Const data after split point has been setup const Position* pos; const Search::Stack* ss; - Thread* master; + Thread* masterThread; Depth depth; Value beta; int nodeType; Move threatMove; // Const pointers to shared data - MovePicker* mp; - SplitPoint* parent; + MovePicker* movePicker; + SplitPoint* parentSplitPoint; // Shared data Mutex mutex; From 91427c824280d71eaf27f39a4bfdd2188cbdb4ec Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Mon, 4 Feb 2013 22:09:52 +0100 Subject: [PATCH 16/30] Move split() under Thread Previous renaming patch suggested this reformat: when a better naming leads to a better code! No functional change. --- src/search.cpp | 4 ++-- src/thread.cpp | 58 ++++++++++++++++++++++++-------------------------- src/thread.h | 10 ++++----- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4adcefa9..49d18b50 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1030,8 +1030,8 @@ split_point_start: // At split points actual search starts from here { assert(bestValue < beta); - bestValue = Threads.split(pos, ss, alpha, beta, bestValue, &bestMove, - depth, threatMove, moveCount, mp, NT); + bestValue = thisThread->split(pos, ss, alpha, beta, bestValue, &bestMove, + depth, threatMove, moveCount, mp, NT); if (bestValue >= beta) break; } diff --git a/src/thread.cpp b/src/thread.cpp index 7be8da88..12a95271 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -249,26 +249,24 @@ bool ThreadPool::slave_available(Thread* master) const { // search() then split() returns. template -Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, - Value bestValue, Move* bestMove, Depth depth, Move threatMove, - int moveCount, MovePicker& mp, int nodeType) { +Value Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, + Value bestValue, Move* bestMove, Depth depth, Move threatMove, + int moveCount, MovePicker& mp, int nodeType) { assert(pos.pos_is_ok()); assert(bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE); assert(depth >= Threads.minimumSplitDepth); - Thread* thisThread = pos.this_thread(); - - assert(thisThread->searching); - assert(thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); + assert(searching); + assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); // Pick the next available split point from the split point stack - SplitPoint& sp = thisThread->splitPoints[thisThread->splitPointsSize]; + SplitPoint& sp = splitPoints[splitPointsSize]; - sp.masterThread = thisThread; - sp.parentSplitPoint = thisThread->activeSplitPoint; - sp.slavesMask = 1ULL << thisThread->idx; + sp.masterThread = this; + sp.parentSplitPoint = activeSplitPoint; + sp.slavesMask = 1ULL << idx; sp.depth = depth; sp.bestMove = *bestMove; sp.threatMove = threatMove; @@ -286,25 +284,25 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, // Try to allocate available threads and ask them to start searching setting // 'searching' flag. This must be done under lock protection to avoid concurrent // allocation of the same slave by another master. - mutex.lock(); + Threads.mutex.lock(); sp.mutex.lock(); - thisThread->splitPointsSize++; - thisThread->activeSplitPoint = &sp; + splitPointsSize++; + activeSplitPoint = &sp; size_t slavesCnt = 1; // Master is always included - for (size_t i = 0; i < threads.size() && !Fake; ++i) - if (threads[i]->is_available_to(thisThread) && ++slavesCnt <= maxThreadsPerSplitPoint) + for (size_t i = 0; i < Threads.size() && !Fake; ++i) + if (Threads[i].is_available_to(this) && ++slavesCnt <= Threads.maxThreadsPerSplitPoint) { - sp.slavesMask |= 1ULL << threads[i]->idx; - threads[i]->activeSplitPoint = &sp; - threads[i]->searching = true; // Slave leaves idle_loop() - threads[i]->notify_one(); // Could be sleeping + sp.slavesMask |= 1ULL << Threads[i].idx; + Threads[i].activeSplitPoint = &sp; + Threads[i].searching = true; // Slave leaves idle_loop() + Threads[i].notify_one(); // Could be sleeping } sp.mutex.unlock(); - mutex.unlock(); + Threads.mutex.unlock(); // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its 'searching' flag is set. @@ -312,34 +310,34 @@ Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, // their work at this split point. if (slavesCnt > 1 || Fake) { - thisThread->Thread::idle_loop(); // Force a call to base class idle_loop() + Thread::idle_loop(); // Force a call to base class idle_loop() // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. - assert(!thisThread->searching); + assert(!searching); } // We have returned from the idle loop, which means that all threads are // finished. Note that setting 'searching' and decreasing splitPointsSize is // done under lock protection to avoid a race with Thread::is_available_to(). - mutex.lock(); + Threads.mutex.lock(); sp.mutex.lock(); - thisThread->searching = true; - thisThread->splitPointsSize--; - thisThread->activeSplitPoint = sp.parentSplitPoint; + searching = true; + splitPointsSize--; + activeSplitPoint = sp.parentSplitPoint; pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); *bestMove = sp.bestMove; sp.mutex.unlock(); - mutex.unlock(); + Threads.mutex.unlock(); return sp.bestValue; } // Explicit template instantiations -template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker&, int); -template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker&, int); +template Value Thread::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker&, int); +template Value Thread::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker&, int); // wait_for_think_finished() waits for main thread to go to sleep then returns diff --git a/src/thread.h b/src/thread.h index d24b65dc..76a2d843 100644 --- a/src/thread.h +++ b/src/thread.h @@ -102,6 +102,10 @@ struct Thread { bool is_available_to(Thread* master) const; void wait_for(volatile const bool& b); + template + Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove, + Depth depth, Move threatMove, int moveCount, MovePicker& mp, int nodeType); + SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; Material::Table materialTable; Endgames endgames; @@ -155,19 +159,15 @@ public: void start_thinking(const Position&, const Search::LimitsType&, const std::vector&, Search::StateStackPtr&); - template - Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove, - Depth depth, Move threatMove, int moveCount, MovePicker& mp, int nodeType); - bool sleepWhileIdle; Depth minimumSplitDepth; + size_t maxThreadsPerSplitPoint; Mutex mutex; ConditionVariable sleepCondition; private: std::vector threads; TimerThread* timer; - size_t maxThreadsPerSplitPoint; }; extern ThreadPool Threads; From 1a414cd9cb274d9572c60fe4067671cd993ffb36 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Mon, 4 Feb 2013 22:38:42 +0100 Subject: [PATCH 17/30] Derive ThreadPool from std::vector Prefer sub-classing to composition in this case. No functional change. --- src/search.cpp | 16 ++++++++-------- src/thread.cpp | 38 +++++++++++++++++++++----------------- src/thread.h | 14 +++----------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 49d18b50..84c62eec 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -229,22 +229,22 @@ void Search::think() { // Reset the threads, still sleeping: will be wake up at split time for (size_t i = 0; i < Threads.size(); i++) - Threads[i].maxPly = 0; + Threads[i]->maxPly = 0; Threads.sleepWhileIdle = Options["Use Sleeping Threads"]; // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. - Threads.timer_thread()->msec = + Threads.timer->msec = Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) : Limits.nodes ? 2 * TimerResolution : 100; - Threads.timer_thread()->notify_one(); // Wake up the recurring timer + Threads.timer->notify_one(); // Wake up the recurring timer id_loop(RootPos); // Let's start searching ! - Threads.timer_thread()->msec = 0; // Stop the timer + Threads.timer->msec = 0; // Stop the timer Threads.sleepWhileIdle = true; // Send idle threads to sleep if (Options["Use Search Log"]) @@ -1513,8 +1513,8 @@ split_point_start: // At split points actual search starts from here int selDepth = 0; for (size_t i = 0; i < Threads.size(); i++) - if (Threads[i].maxPly > selDepth) - selDepth = Threads[i].maxPly; + if (Threads[i]->maxPly > selDepth) + selDepth = Threads[i]->maxPly; for (size_t i = 0; i < uciPVSize; i++) { @@ -1744,9 +1744,9 @@ void check_time() { // Loop across all split points and sum accumulated SplitPoint nodes plus // all the currently active slaves positions. for (size_t i = 0; i < Threads.size(); i++) - for (int j = 0; j < Threads[i].splitPointsSize; j++) + for (int j = 0; j < Threads[i]->splitPointsSize; j++) { - SplitPoint& sp = Threads[i].splitPoints[j]; + SplitPoint& sp = Threads[i]->splitPoints[j]; sp.mutex.lock(); diff --git a/src/thread.cpp b/src/thread.cpp index 12a95271..1c5c67ba 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -186,7 +186,7 @@ void ThreadPool::init() { sleepWhileIdle = true; timer = new TimerThread(); - threads.push_back(new MainThread()); + push_back(new MainThread()); read_uci_options(); } @@ -197,8 +197,8 @@ void ThreadPool::exit() { delete timer; // As first because check_time() accesses threads data - for (size_t i = 0; i < threads.size(); i++) - delete threads[i]; + for (iterator it = begin(); it != end(); ++it) + delete *it; } @@ -215,13 +215,13 @@ void ThreadPool::read_uci_options() { assert(requested > 0); - while (threads.size() < requested) - threads.push_back(new Thread()); + while (size() < requested) + push_back(new Thread()); - while (threads.size() > requested) + while (size() > requested) { - delete threads.back(); - threads.pop_back(); + delete back(); + pop_back(); } } @@ -231,8 +231,8 @@ void ThreadPool::read_uci_options() { bool ThreadPool::slave_available(Thread* master) const { - for (size_t i = 0; i < threads.size(); i++) - if (threads[i]->is_available_to(master)) + for (const_iterator it = begin(); it != end(); ++it) + if ((*it)->is_available_to(master)) return true; return false; @@ -290,16 +290,20 @@ Value Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, splitPointsSize++; activeSplitPoint = &sp; - size_t slavesCnt = 1; // Master is always included + size_t slavesCnt = 1; // This thread is always included - for (size_t i = 0; i < Threads.size() && !Fake; ++i) - if (Threads[i].is_available_to(this) && ++slavesCnt <= Threads.maxThreadsPerSplitPoint) + for (ThreadPool::iterator it = Threads.begin(); it != Threads.end() && !Fake; ++it) + { + Thread* slave = *it; + + if (slave->is_available_to(this) && ++slavesCnt <= Threads.maxThreadsPerSplitPoint) { - sp.slavesMask |= 1ULL << Threads[i].idx; - Threads[i].activeSplitPoint = &sp; - Threads[i].searching = true; // Slave leaves idle_loop() - Threads[i].notify_one(); // Could be sleeping + sp.slavesMask |= 1ULL << slave->idx; + slave->activeSplitPoint = &sp; + slave->searching = true; // Slave leaves idle_loop() + slave->notify_one(); // Could be sleeping } + } sp.mutex.unlock(); Threads.mutex.unlock(); diff --git a/src/thread.h b/src/thread.h index 76a2d843..786c5b67 100644 --- a/src/thread.h +++ b/src/thread.h @@ -138,21 +138,16 @@ struct TimerThread : public Thread { }; -/// ThreadPool class handles all the threads related stuff like init, starting, +/// ThreadPool struct handles all the threads related stuff like init, starting, /// parking and, the most important, launching a slave thread at a split point. /// All the access to shared thread data is done through this class. -class ThreadPool { +struct ThreadPool : public std::vector { -public: void init(); // No c'tor and d'tor, threads rely on globals that should void exit(); // be initialized and valid during the whole thread lifetime. - Thread& operator[](size_t id) { return *threads[id]; } - size_t size() const { return threads.size(); } - MainThread* main_thread() { return static_cast(threads[0]); } - TimerThread* timer_thread() { return timer; } - + MainThread* main_thread() { return static_cast((*this)[0]); } void read_uci_options(); bool slave_available(Thread* master) const; void wait_for_think_finished(); @@ -164,9 +159,6 @@ public: size_t maxThreadsPerSplitPoint; Mutex mutex; ConditionVariable sleepCondition; - -private: - std::vector threads; TimerThread* timer; }; From bf706c4a4ff06e91edf8c24685d24130146f88d4 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 5 Feb 2013 06:30:05 +0100 Subject: [PATCH 18/30] Slightly change split() API This function "returns" two values: bestValue and bestMove Instead of returning one and passing as pointer the other be consistent and pass as pointers both. No functional change. --- src/search.cpp | 4 ++-- src/thread.cpp | 22 ++++++++++------------ src/thread.h | 4 ++-- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 84c62eec..5e61b9e5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1030,8 +1030,8 @@ split_point_start: // At split points actual search starts from here { assert(bestValue < beta); - bestValue = thisThread->split(pos, ss, alpha, beta, bestValue, &bestMove, - depth, threatMove, moveCount, mp, NT); + thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, + depth, threatMove, moveCount, &mp, NT); if (bestValue >= beta) break; } diff --git a/src/thread.cpp b/src/thread.cpp index 1c5c67ba..b04b8adb 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -249,15 +249,14 @@ bool ThreadPool::slave_available(Thread* master) const { // search() then split() returns. template -Value Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, - Value bestValue, Move* bestMove, Depth depth, Move threatMove, - int moveCount, MovePicker& mp, int nodeType) { +void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue, + Move* bestMove, Depth depth, Move threatMove, int moveCount, + MovePicker* movePicker, int nodeType) { assert(pos.pos_is_ok()); - assert(bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); - assert(bestValue > -VALUE_INFINITE); + assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); + assert(*bestValue > -VALUE_INFINITE); assert(depth >= Threads.minimumSplitDepth); - assert(searching); assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); @@ -268,13 +267,13 @@ Value Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, sp.parentSplitPoint = activeSplitPoint; sp.slavesMask = 1ULL << idx; sp.depth = depth; + sp.bestValue = *bestValue; sp.bestMove = *bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; - sp.bestValue = bestValue; - sp.movePicker = ∓ + sp.movePicker = movePicker; sp.moveCount = moveCount; sp.pos = &pos; sp.nodes = 0; @@ -332,16 +331,15 @@ Value Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, activeSplitPoint = sp.parentSplitPoint; pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); *bestMove = sp.bestMove; + *bestValue = sp.bestValue; sp.mutex.unlock(); Threads.mutex.unlock(); - - return sp.bestValue; } // Explicit template instantiations -template Value Thread::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker&, int); -template Value Thread::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker&, int); +template void Thread::split(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); +template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); // wait_for_think_finished() waits for main thread to go to sleep then returns diff --git a/src/thread.h b/src/thread.h index 786c5b67..b18170df 100644 --- a/src/thread.h +++ b/src/thread.h @@ -103,8 +103,8 @@ struct Thread { void wait_for(volatile const bool& b); template - Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove, - Depth depth, Move threatMove, int moveCount, MovePicker& mp, int nodeType); + void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, + Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType); SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; Material::Table materialTable; From f69c185e02b4ab33d19cea6e143ad0beac05c7c7 Mon Sep 17 00:00:00 2001 From: jundery Date: Tue, 5 Feb 2013 10:06:37 -0700 Subject: [PATCH 19/30] Add const qualifer to check_is_dangerous No functional change. --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5e61b9e5..ec457609 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -98,7 +98,7 @@ namespace { void id_loop(Position& pos); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); - bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta); + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta); bool allows(const Position& pos, Move first, Move second); bool refutes(const Position& pos, Move first, Move second); string uci_pv(const Position& pos, int depth, Value alpha, Value beta); @@ -1335,7 +1335,7 @@ split_point_start: // At split points actual search starts from here // check_is_dangerous() tests if a checking move can be pruned in qsearch() - bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta) + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta) { Piece pc = pos.piece_moved(move); Square from = from_sq(move); From 88c3670edf26d094678eeef3a2ed1eeffaf344d4 Mon Sep 17 00:00:00 2001 From: jundery Date: Tue, 5 Feb 2013 10:02:54 -0700 Subject: [PATCH 20/30] Rename posKey stored in the transposition table [Edit: Slightly extended by me] No functional change. --- src/tt.cpp | 20 ++++++++++---------- src/tt.h | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 9dbfcb5a..a7042f9e 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -82,23 +82,23 @@ void TranspositionTable::clear() { /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// a previous search, or if the depth of t1 is bigger than the depth of t2. -void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) { +void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) { int c1, c2, c3; TTEntry *tte, *replace; - uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster + uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster - tte = replace = first_entry(posKey); + tte = replace = first_entry(key); for (int i = 0; i < ClusterSize; i++, tte++) { - if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old + if (!tte->key() || tte->key() == key32) // Empty or overwrite old { // Preserve any existing ttMove if (m == MOVE_NONE) m = tte->move(); - tte->save(posKey32, v, t, d, m, generation, statV, kingD); + tte->save(key32, v, t, d, m, generation, statV, kingD); return; } @@ -110,7 +110,7 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move if (c1 + c2 + c3 > 0) replace = tte; } - replace->save(posKey32, v, t, d, m, generation, statV, kingD); + replace->save(key32, v, t, d, m, generation, statV, kingD); } @@ -118,13 +118,13 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move /// transposition table. Returns a pointer to the TTEntry or NULL if /// position is not found. -TTEntry* TranspositionTable::probe(const Key posKey) const { +TTEntry* TranspositionTable::probe(const Key key) const { - uint32_t posKey32 = posKey >> 32; - TTEntry* tte = first_entry(posKey); + TTEntry* tte = first_entry(key); + uint32_t key32 = key >> 32; for (int i = 0; i < ClusterSize; i++, tte++) - if (tte->key() == posKey32) + if (tte->key() == key32) return tte; return NULL; diff --git a/src/tt.h b/src/tt.h index f3b0f2ad..705bac91 100644 --- a/src/tt.h +++ b/src/tt.h @@ -100,8 +100,8 @@ public: ~TranspositionTable(); void set_size(size_t mbSize); void clear(); - void store(const Key posKey, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); - TTEntry* probe(const Key posKey) const; + void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); + TTEntry* probe(const Key key) const; void new_search(); TTEntry* first_entry(const Key posKey) const; void refresh(const TTEntry* tte) const; From 14c2c1395b2d3e4e766b1ace71a486432259da73 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Wed, 6 Feb 2013 09:36:13 +0100 Subject: [PATCH 21/30] Change slave_available() API To return a pointer to the available thread instead of a bool. This allows to simplify the core loop in split(). No functional change. Signed-off-by: Marco Costalba --- src/search.cpp | 2 +- src/thread.cpp | 23 ++++++++++------------- src/thread.h | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ec457609..60b7c806 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,7 +1025,7 @@ split_point_start: // At split points actual search starts from here // Step 19. Check for splitting the search if ( !SpNode && depth >= Threads.minimumSplitDepth - && Threads.slave_available(thisThread) + && Threads.available_slave(thisThread) && thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD) { assert(bestValue < beta); diff --git a/src/thread.cpp b/src/thread.cpp index b04b8adb..3b59a05d 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -229,13 +229,13 @@ void ThreadPool::read_uci_options() { // slave_available() tries to find an idle thread which is available as a slave // for the thread 'master'. -bool ThreadPool::slave_available(Thread* master) const { +Thread* ThreadPool::available_slave(Thread* master) const { for (const_iterator it = begin(); it != end(); ++it) if ((*it)->is_available_to(master)) - return true; + return *it; - return false; + return NULL; } @@ -290,18 +290,15 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes activeSplitPoint = &sp; size_t slavesCnt = 1; // This thread is always included + Thread* slave; - for (ThreadPool::iterator it = Threads.begin(); it != Threads.end() && !Fake; ++it) + while ( (slave = Threads.available_slave(this)) != NULL + && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake) { - Thread* slave = *it; - - if (slave->is_available_to(this) && ++slavesCnt <= Threads.maxThreadsPerSplitPoint) - { - sp.slavesMask |= 1ULL << slave->idx; - slave->activeSplitPoint = &sp; - slave->searching = true; // Slave leaves idle_loop() - slave->notify_one(); // Could be sleeping - } + sp.slavesMask |= 1ULL << slave->idx; + slave->activeSplitPoint = &sp; + slave->searching = true; // Slave leaves idle_loop() + slave->notify_one(); // Could be sleeping } sp.mutex.unlock(); diff --git a/src/thread.h b/src/thread.h index b18170df..c760c20d 100644 --- a/src/thread.h +++ b/src/thread.h @@ -149,7 +149,7 @@ struct ThreadPool : public std::vector { MainThread* main_thread() { return static_cast((*this)[0]); } void read_uci_options(); - bool slave_available(Thread* master) const; + Thread* available_slave(Thread* master) const; void wait_for_think_finished(); void start_thinking(const Position&, const Search::LimitsType&, const std::vector&, Search::StateStackPtr&); From c67fb8ef04439dbd208bfcd53a89367372ef84e7 Mon Sep 17 00:00:00 2001 From: Gary Linscott Date: Sun, 3 Feb 2013 15:42:51 -0500 Subject: [PATCH 22/30] Add KBPKP endgame It is a draw if pawns are on G or B files, weaker pawn is on rank 7 and bishop can't attack the pawn. No functional change (because it is very rare and does not appear in bench) --- src/endgame.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/endgame.cpp b/src/endgame.cpp index d60ce396..ae29a71a 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -471,6 +471,29 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_DRAW; } } + + // All pawns on same B or G file? Then potential draw + if ( (pawnFile == FILE_B || pawnFile == FILE_G) + && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) + && pos.non_pawn_material(weakerSide) == 0 + && pos.piece_count(weakerSide, PAWN) >= 1) + { + // Get weaker pawn closest to opponent's queening square + Bitboard wkPawns = pos.pieces(weakerSide, PAWN); + Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns); + + Square strongerKingSq = pos.king_square(strongerSide); + Square weakerKingSq = pos.king_square(weakerSide); + Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; + + // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and + // weaker king can stop opposing opponent's king from penetrating. + if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7 + && opposite_colors(bishopSq, weakerPawnSq) + && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq)) + return SCALE_FACTOR_DRAW; + } + return SCALE_FACTOR_NONE; } From 50a7200b18e96a6f0e2c2ee7fda017ea6f2f1c1f Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Fri, 8 Feb 2013 08:49:36 +0100 Subject: [PATCH 23/30] Workaround value-initialization in MSVC The syntax splitPoints() should force the compiler to value-initialize the array and because there is no user defined c'tor it falls back on zero-initialization. Unfortunatly this is broken in MSVC compilers, because value initialization for non-POD types is not supported, so left splitPoints un-initialized and add in split() initialization of slavesPositions, that is the only member not already set at split time. This fixes an assert under MSVC when running with more than one thread. Spotted and reported by Jundery. No functional change. --- src/thread.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/thread.cpp b/src/thread.cpp index 3b59a05d..80765cde 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -19,6 +19,7 @@ #include // For std::count #include +#include // For memset #include #include "movegen.h" @@ -43,7 +44,7 @@ namespace { extern "C" { // Thread c'tor starts a newly-created thread of execution that will call // the the virtual function idle_loop(), going immediately to sleep. -Thread::Thread() : splitPoints() { +Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC searching = exit = false; maxPly = splitPointsSize = 0; @@ -280,6 +281,8 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes sp.cutoff = false; sp.ss = ss; + memset(sp.slavesPositions, 0, sizeof(sp.slavesPositions)); + // Try to allocate available threads and ask them to start searching setting // 'searching' flag. This must be done under lock protection to avoid concurrent // allocation of the same slave by another master. From 880726c13a393e70ddc358741e68cc28a546ae8c Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Fri, 8 Feb 2013 10:05:04 +0100 Subject: [PATCH 24/30] Add const qualifer to go() Obsolete renmant of when position was directly passed to the search instead of being copied for the main thread as is now. From Jundery. No functional change. --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 5721e93f..10519bb1 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -44,7 +44,7 @@ namespace { void set_option(istringstream& up); void set_position(Position& pos, istringstream& up); - void go(Position& pos, istringstream& up); + void go(const Position& pos, istringstream& up); } @@ -182,7 +182,7 @@ namespace { // the thinking time and other parameters from the input string, and starts // the search. - void go(Position& pos, istringstream& is) { + void go(const Position& pos, istringstream& is) { Search::LimitsType limits; vector searchMoves; From e5bc79fb9cf0f47c850ad0cc0b057d9f86e788cd Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Fri, 8 Feb 2013 11:04:07 +0100 Subject: [PATCH 25/30] Retire slavesPositions Save the current active position in each Thread instead of keeping a centralized array in struct SplitPoint. This allow to skip a memset() call at each split. No functional change. --- src/search.cpp | 13 +++++++------ src/thread.cpp | 7 ++++--- src/thread.h | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 60b7c806..cabd1cca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1672,9 +1672,9 @@ void Thread::idle_loop() { sp->mutex.lock(); - assert(sp->slavesPositions[idx] == NULL); + assert(activePosition == NULL); - sp->slavesPositions[idx] = &pos; + activePosition = &pos; switch (sp->nodeType) { case Root: @@ -1693,7 +1693,7 @@ void Thread::idle_loop() { assert(searching); searching = false; - sp->slavesPositions[idx] = NULL; + activePosition = NULL; sp->slavesMask &= ~(1ULL << idx); sp->nodes += pos.nodes_searched(); @@ -1742,7 +1742,7 @@ void check_time() { nodes = RootPos.nodes_searched(); // Loop across all split points and sum accumulated SplitPoint nodes plus - // all the currently active slaves positions. + // all the currently active positions nodes. for (size_t i = 0; i < Threads.size(); i++) for (int j = 0; j < Threads[i]->splitPointsSize; j++) { @@ -1754,8 +1754,9 @@ void check_time() { Bitboard sm = sp.slavesMask; while (sm) { - Position* pos = sp.slavesPositions[pop_lsb(&sm)]; - nodes += pos ? pos->nodes_searched() : 0; + Position* pos = Threads[pop_lsb(&sm)]->activePosition; + if (pos) + nodes += pos->nodes_searched(); } sp.mutex.unlock(); diff --git a/src/thread.cpp b/src/thread.cpp index 80765cde..a6886b1c 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -19,7 +19,6 @@ #include // For std::count #include -#include // For memset #include #include "movegen.h" @@ -49,6 +48,7 @@ Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC searching = exit = false; maxPly = splitPointsSize = 0; activeSplitPoint = NULL; + activePosition = NULL; idx = Threads.size(); if (!thread_create(handle, start_routine, this)) @@ -281,8 +281,6 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes sp.cutoff = false; sp.ss = ss; - memset(sp.slavesPositions, 0, sizeof(sp.slavesPositions)); - // Try to allocate available threads and ask them to start searching setting // 'searching' flag. This must be done under lock protection to avoid concurrent // allocation of the same slave by another master. @@ -291,6 +289,7 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes splitPointsSize++; activeSplitPoint = &sp; + activePosition = NULL; size_t slavesCnt = 1; // This thread is always included Thread* slave; @@ -318,6 +317,7 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. assert(!searching); + assert(!activePosition); } // We have returned from the idle loop, which means that all threads are @@ -329,6 +329,7 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes searching = true; splitPointsSize--; activeSplitPoint = sp.parentSplitPoint; + activePosition = &pos; pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); *bestMove = sp.bestMove; *bestValue = sp.bestValue; diff --git a/src/thread.h b/src/thread.h index c760c20d..8115bc71 100644 --- a/src/thread.h +++ b/src/thread.h @@ -75,7 +75,6 @@ struct SplitPoint { // Shared data Mutex mutex; - Position* slavesPositions[MAX_THREADS]; volatile uint64_t slavesMask; volatile int64_t nodes; volatile Value alpha; @@ -110,6 +109,7 @@ struct Thread { Material::Table materialTable; Endgames endgames; Pawns::Table pawnsTable; + Position* activePosition; size_t idx; int maxPly; Mutex mutex; From 3f44be9baa38663bce163e4f7c9ab00a1553529f Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 9 Feb 2013 06:40:46 +0100 Subject: [PATCH 26/30] Simplify move_to_san() Nicely simplify disambiguation code. No functional change. --- src/notation.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/notation.cpp b/src/notation.cpp index bb4a713c..b8e89b67 100644 --- a/src/notation.cpp +++ b/src/notation.cpp @@ -110,8 +110,7 @@ const string move_to_san(Position& pos, Move m) { assert(MoveList(pos).contains(m)); - Bitboard attackers; - bool ambiguousMove, ambiguousFile, ambiguousRank; + Bitboard others, b; string san; Color us = pos.side_to_move(); Square from = from_sq(m); @@ -127,31 +126,23 @@ const string move_to_san(Position& pos, Move m) { { san = PieceToChar[WHITE][pt]; // Upper case - // Disambiguation if we have more then one piece with destination 'to' - // note that for pawns is not needed because starting file is explicit. - ambiguousMove = ambiguousFile = ambiguousRank = false; + // Disambiguation if we have more then one piece of type 'pt' that can + // reach 'to' with a legal move. + others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; - attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; - - while (attackers) + while (b) { - Square sq = pop_lsb(&attackers); - - // If the move is illegal, the piece is not included in the sub-set - if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces())) - continue; - - ambiguousFile |= file_of(sq) == file_of(from); - ambiguousRank |= rank_of(sq) == rank_of(from); - ambiguousMove = true; + Move move = make_move(pop_lsb(&b), to); + if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) + others ^= from_sq(move); } - if (ambiguousMove) + if (others) { - if (!ambiguousFile) + if (!(others & file_bb(from))) san += file_to_char(file_of(from)); - else if (!ambiguousRank) + else if (!(others & rank_bb(from))) san += rank_to_char(rank_of(from)); else From fe3352665b2dfc6c339136856c782057a5c5476e Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 9 Feb 2013 08:17:03 +0100 Subject: [PATCH 27/30] Retire TTCluster and simplify TT Also some renaming while there. No functional change. --- src/search.cpp | 8 +++---- src/tt.cpp | 36 ++++++---------------------- src/tt.h | 65 +++++++++++++++++++++----------------------------- 3 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cabd1cca..091c00fe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -594,8 +594,8 @@ namespace { else if (tte) { // Never assume anything on values stored in TT - if ( (ss->staticEval = eval = tte->static_value()) == VALUE_NONE - ||(ss->evalMargin = tte->static_value_margin()) == VALUE_NONE) + if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) eval = ss->staticEval = evaluate(pos, ss->evalMargin); // Can ttValue be used as a better position evaluation? @@ -1164,8 +1164,8 @@ split_point_start: // At split points actual search starts from here if (tte) { // Never assume anything on values stored in TT - if ( (ss->staticEval = bestValue = tte->static_value()) == VALUE_NONE - ||(ss->evalMargin = tte->static_value_margin()) == VALUE_NONE) + if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); } else diff --git a/src/tt.cpp b/src/tt.cpp index a7042f9e..2445f794 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -25,33 +25,21 @@ TranspositionTable TT; // Our global transposition table -TranspositionTable::TranspositionTable() { - - size = generation = 0; - entries = NULL; -} - -TranspositionTable::~TranspositionTable() { - - delete [] entries; -} - /// TranspositionTable::set_size() sets the size of the transposition table, -/// measured in megabytes. Transposition table consists of a power of 2 number of -/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each -/// non-empty entry contains information of exactly one position. +/// measured in megabytes. Transposition table consists of a power of 2 number +/// of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::set_size(size_t mbSize) { - size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster)); + size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); if (newSize == size) return; size = newSize; delete [] entries; - entries = new (std::nothrow) TTCluster[size]; + entries = new (std::nothrow) TTEntry[size * ClusterSize]; if (!entries) { @@ -70,7 +58,7 @@ void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::clear() { - memset(entries, 0, size * sizeof(TTCluster)); + memset(entries, 0, size * sizeof(TTEntry[ClusterSize])); } @@ -90,7 +78,7 @@ void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, tte = replace = first_entry(key); - for (int i = 0; i < ClusterSize; i++, tte++) + for (unsigned i = 0; i < ClusterSize; i++, tte++) { if (!tte->key() || tte->key() == key32) // Empty or overwrite old { @@ -123,19 +111,9 @@ TTEntry* TranspositionTable::probe(const Key key) const { TTEntry* tte = first_entry(key); uint32_t key32 = key >> 32; - for (int i = 0; i < ClusterSize; i++, tte++) + for (unsigned i = 0; i < ClusterSize; i++, tte++) if (tte->key() == key32) return tte; return NULL; } - - -/// TranspositionTable::new_search() is called at the beginning of every new -/// search. It increments the "generation" variable, which is used to -/// distinguish transposition table entries from previous searches from -/// entries from the current search. - -void TranspositionTable::new_search() { - generation++; -} diff --git a/src/tt.h b/src/tt.h index 705bac91..3484f0eb 100644 --- a/src/tt.h +++ b/src/tt.h @@ -44,7 +44,7 @@ class TTEntry { public: - void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value statV, Value statM) { + void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { key32 = (uint32_t)k; move16 = (uint16_t)m; @@ -52,63 +52,52 @@ public: generation8 = (uint8_t)g; value16 = (int16_t)v; depth16 = (int16_t)d; - staticValue = (int16_t)statV; - staticMargin = (int16_t)statM; + evalValue = (int16_t)ev; + evalMargin = (int16_t)em; } void set_generation(int g) { generation8 = (uint8_t)g; } - uint32_t key() const { return key32; } - Depth depth() const { return (Depth)depth16; } - Move move() const { return (Move)move16; } - Value value() const { return (Value)value16; } - Bound type() const { return (Bound)bound; } - int generation() const { return (int)generation8; } - Value static_value() const { return (Value)staticValue; } - Value static_value_margin() const { return (Value)staticMargin; } + uint32_t key() const { return key32; } + Depth depth() const { return (Depth)depth16; } + Move move() const { return (Move)move16; } + Value value() const { return (Value)value16; } + Bound type() const { return (Bound)bound; } + int generation() const { return (int)generation8; } + Value eval_value() const { return (Value)evalValue; } + Value eval_margin() const { return (Value)evalMargin; } private: uint32_t key32; uint16_t move16; uint8_t bound, generation8; - int16_t value16, depth16, staticValue, staticMargin; + int16_t value16, depth16, evalValue, evalMargin; }; -/// This is the number of TTEntry slots for each cluster -const int ClusterSize = 4; - - -/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster -/// must not be bigger than a cache line size. In case it is less, it should -/// be padded to guarantee always aligned accesses. - -struct TTCluster { - TTEntry data[ClusterSize]; -}; - - -/// The transposition table class. This is basically just a huge array containing -/// TTCluster objects, and a few methods for writing and reading entries. +/// A TranspositionTable consists of a power of 2 number of clusters and each +/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry +/// contains information of exactly one position. Size of a cluster shall not be +/// bigger than a cache line size. In case it is less, it should be padded to +/// guarantee always aligned accesses. class TranspositionTable { - TranspositionTable(const TranspositionTable&); - TranspositionTable& operator=(const TranspositionTable&); + static const unsigned ClusterSize = 4; // A cluster is 64 Bytes public: - TranspositionTable(); - ~TranspositionTable(); + ~TranspositionTable() { delete [] entries; } + void new_search() { generation++; } + + TTEntry* probe(const Key key) const; + TTEntry* first_entry(const Key key) const; + void refresh(const TTEntry* tte) const; void set_size(size_t mbSize); void clear(); void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); - TTEntry* probe(const Key key) const; - void new_search(); - TTEntry* first_entry(const Key posKey) const; - void refresh(const TTEntry* tte) const; private: size_t size; - TTCluster* entries; + TTEntry* entries; uint8_t generation; // Size must be not bigger then TTEntry::generation8 }; @@ -119,9 +108,9 @@ extern TranspositionTable TT; /// a cluster given a position. The lowest order bits of the key are used to /// get the index of the cluster. -inline TTEntry* TranspositionTable::first_entry(const Key posKey) const { +inline TTEntry* TranspositionTable::first_entry(const Key key) const { - return entries[((uint32_t)posKey) & (size - 1)].data; + return entries + ((uint32_t)key & (size - 1)) * ClusterSize; } From c698362680d7d66801be100e20346bbbf4ec5c4f Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 9 Feb 2013 10:22:34 +0100 Subject: [PATCH 28/30] Microptimize first_entry() for 32bits Do a 32bit bitwise 'and' instead of a 64bit subtract and bitwise 'and'. This is possible because even in the biggest hash table case (8GB) the number of entries is 2^29 so storable in an unsigned int. No functional change. --- src/tt.cpp | 10 ++++++---- src/tt.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 2445f794..fa0d39de 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -32,12 +32,14 @@ TranspositionTable TT; // Our global transposition table void TranspositionTable::set_size(size_t mbSize) { - size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); + assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); - if (newSize == size) + uint32_t size = 1 << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); + + if (clusterMask == size - 1) return; - size = newSize; + clusterMask = size - 1; delete [] entries; entries = new (std::nothrow) TTEntry[size * ClusterSize]; @@ -58,7 +60,7 @@ void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::clear() { - memset(entries, 0, size * sizeof(TTEntry[ClusterSize])); + memset(entries, 0, (clusterMask + 1) * sizeof(TTEntry[ClusterSize])); } diff --git a/src/tt.h b/src/tt.h index 3484f0eb..81bfd3b2 100644 --- a/src/tt.h +++ b/src/tt.h @@ -96,7 +96,7 @@ public: void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); private: - size_t size; + uint32_t clusterMask; TTEntry* entries; uint8_t generation; // Size must be not bigger then TTEntry::generation8 }; @@ -110,7 +110,7 @@ extern TranspositionTable TT; inline TTEntry* TranspositionTable::first_entry(const Key key) const { - return entries + ((uint32_t)key & (size - 1)) * ClusterSize; + return entries + ((uint32_t)key & clusterMask) * ClusterSize; } From 0be7b8c54207a5a435ed38f0b8e42ad9a8cc9935 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 9 Feb 2013 16:22:47 +0100 Subject: [PATCH 29/30] Further simplify first_entry() We can encode the ClusterSize directly in the hashMask, this allows to skip the left shift. There is no real change, but bench number is now different because instead of using the lowest order bits of the key to index the start of the cluster, now we don't use the last two lsb bits that are always set to zero (cluster size is 4). So for instance, if 10 bits are used to index the cluster, instead of bits [9..0] now we use bits [11..2]. This changes the positions that end up in the same cluster affecting TT hits and so bench is different. Also some renaming while there. bench: 5383795 --- src/tt.cpp | 14 +++++++------- src/tt.h | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index fa0d39de..f4e55354 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -34,16 +34,16 @@ void TranspositionTable::set_size(size_t mbSize) { assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); - uint32_t size = 1 << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); + uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); - if (clusterMask == size - 1) + if (hashMask == size - ClusterSize) return; - clusterMask = size - 1; - delete [] entries; - entries = new (std::nothrow) TTEntry[size * ClusterSize]; + hashMask = size - ClusterSize; + delete [] table; + table = new (std::nothrow) TTEntry[size]; - if (!entries) + if (!table) { std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; @@ -60,7 +60,7 @@ void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::clear() { - memset(entries, 0, (clusterMask + 1) * sizeof(TTEntry[ClusterSize])); + memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); } diff --git a/src/tt.h b/src/tt.h index 81bfd3b2..2db19c89 100644 --- a/src/tt.h +++ b/src/tt.h @@ -85,7 +85,7 @@ class TranspositionTable { static const unsigned ClusterSize = 4; // A cluster is 64 Bytes public: - ~TranspositionTable() { delete [] entries; } + ~TranspositionTable() { delete [] table; } void new_search() { generation++; } TTEntry* probe(const Key key) const; @@ -96,8 +96,8 @@ public: void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); private: - uint32_t clusterMask; - TTEntry* entries; + uint32_t hashMask; + TTEntry* table; uint8_t generation; // Size must be not bigger then TTEntry::generation8 }; @@ -110,7 +110,7 @@ extern TranspositionTable TT; inline TTEntry* TranspositionTable::first_entry(const Key key) const { - return entries + ((uint32_t)key & clusterMask) * ClusterSize; + return table + ((uint32_t)key & hashMask); } From 733d0099b2a3e3ad594bb551d37c8a06c62f13db Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 10 Feb 2013 19:10:01 +0100 Subject: [PATCH 30/30] Rename and de-templetize sort() Rename to insertion_sort so to avoid confusion with std::sort, also move it to movepicker.cpp and use the bit slower std::stable_sort in search.cpp where it is used in not performance critical paths. No functional change. --- src/movepick.cpp | 18 ++++++++++++++++-- src/position.cpp | 2 +- src/search.cpp | 4 ++-- src/search.h | 3 +-- src/types.h | 17 ----------------- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 72601bb3..5c8338c0 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -35,6 +35,20 @@ namespace { STOP }; + // Our insertion sort, guaranteed to be stable, as is needed + void insertion_sort(MoveStack* begin, MoveStack* end) + { + MoveStack tmp, *p, *q; + + for (p = begin + 1; p < end; ++p) + { + tmp = *p; + for (q = p; q != begin && *(q-1) < tmp; --q) + *q = *(q-1); + *q = tmp; + } + } + // Unary predicate used by std::partition to split positive scores from remaining // ones so to sort separately the two sets, and with the second sort delayed. inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; } @@ -230,14 +244,14 @@ void MovePicker::generate_next() { endQuiets = end = generate(pos, moves); score(); end = std::partition(cur, end, has_positive_score); - sort(cur, end); + insertion_sort(cur, end); return; case QUIETS_2_S1: cur = end; end = endQuiets; if (depth >= 3 * ONE_PLY) - sort(cur, end); + insertion_sort(cur, end); return; case BAD_CAPTURES_S1: diff --git a/src/position.cpp b/src/position.cpp index 310e46e3..ad0a621d 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -802,7 +802,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI // Update piece list, move the last piece at index[capsq] position and // shrink the list. // - // WARNING: This is a not revresible operation. When we will reinsert the + // WARNING: This is a not reversible operation. When we will reinsert the // captured piece in undo_move() we will put it at the end of the list and // not in its original place, it means index[] and pieceList[] are not // guaranteed to be invariant to a do_move() + undo_move() sequence. diff --git a/src/search.cpp b/src/search.cpp index 091c00fe..74dc9e29 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -354,7 +354,7 @@ namespace { // we want to keep the same order for all the moves but the new // PV that goes to the front. Note that in case of MultiPV search // the already searched PV lines are preserved. - sort(RootMoves.begin() + PVIdx, RootMoves.end()); + std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end()); // Write PV back to transposition table in case the relevant // entries have been overwritten during the search. @@ -399,7 +399,7 @@ namespace { } // Sort the PV lines searched so far and update the GUI - sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); + std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); if (PVIdx + 1 == PVSize || Time::now() - SearchTime > 3000) sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; diff --git a/src/search.h b/src/search.h index 600d7a75..4a66b11f 100644 --- a/src/search.h +++ b/src/search.h @@ -56,12 +56,11 @@ struct Stack { /// all non-pv moves. struct RootMove { - RootMove(){} // Needed by sort() RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { pv.push_back(m); pv.push_back(MOVE_NONE); } - bool operator<(const RootMove& m) const { return score < m.score; } + bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort bool operator==(const Move& m) const { return pv[0] == m; } void extract_pv_from_tt(Position& pos); diff --git a/src/types.h b/src/types.h index b7bcb4e8..aa1f34fc 100644 --- a/src/types.h +++ b/src/types.h @@ -489,21 +489,4 @@ inline const std::string square_to_string(Square s) { return ch; } -/// Our insertion sort implementation, works with pointers and iterators and is -/// guaranteed to be stable, as is needed. -template -void sort(K begin, K end) -{ - T tmp; - K p, q; - - for (p = begin + 1; p < end; p++) - { - tmp = *p; - for (q = p; q != begin && *(q-1) < tmp; --q) - *q = *(q-1); - *q = tmp; - } -} - #endif // !defined(TYPES_H_INCLUDED)