From: Marco Costalba Date: Sun, 17 Apr 2016 19:31:19 +0000 (+0200) Subject: Fix incorrect draw detection X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=94e41274bba2d8a2f2d58aaa711df5872309d66c;hp=ec6aab01366ba1d2de27084d3cc7415a31aa5a24 Fix incorrect draw detection In this position we should have draw for repetition: position fen rnbqkbnr/2pppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 moves g1f3 g8f6 f3g1 go infinite But latest patch broke it. Actually we had two(!) very subtle bugs, the first is that Position::set() clears the passed state and in particular 'previous' member, so that on passing setupStates, 'previous' pointer was reset. Second bug is even more subtle: SetupStates was based on std::vector as container, but when vector grows, std::vector copies all its contents to a new location invalidating all references to its entries. Because all StateInfo records are linked by 'previous' pointer, this made pointers go stale upon adding more element to setupStates. So revert to use a std::deque that ensures references are preserved when pushing back new elements. No functional change. --- diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 2978f346..95125404 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -148,7 +148,7 @@ void benchmark(const Position& current, istream& is) { for (size_t i = 0; i < fens.size(); ++i) { - StateListPtr states(new std::vector(1)); + StateListPtr states(new std::deque(1)); pos.set(fens[i], Options["UCI_Chess960"], &states->back(), Threads.main()); cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; diff --git a/src/position.h b/src/position.h index d44ed009..c3ba5ac8 100644 --- a/src/position.h +++ b/src/position.h @@ -23,6 +23,7 @@ #include #include // For offsetof() +#include #include // For std::unique_ptr #include #include @@ -77,7 +78,8 @@ struct StateInfo { StateInfo* previous; }; -typedef std::unique_ptr> StateListPtr; +// In a std::deque references to elements are unaffected upon resizing +typedef std::unique_ptr> StateListPtr; /// Position class stores information regarding the board representation as diff --git a/src/thread.cpp b/src/thread.cpp index dc4ec05e..4dc7d9e9 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -190,6 +190,8 @@ void ThreadPool::start_thinking(const Position& pos, StateListPtr& states, if (states.get()) setupStates = std::move(states); // Ownership transfer, states is now empty + StateInfo tmp = setupStates->back(); + for (Thread* th : Threads) { th->maxPly = 0; @@ -198,5 +200,7 @@ void ThreadPool::start_thinking(const Position& pos, StateListPtr& states, th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } + setupStates->back() = tmp; // Restore st->previous, cleared by Position::set() + main()->start_searching(); } diff --git a/src/uci.cpp b/src/uci.cpp index cfd253b1..a9811a9a 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -42,7 +42,7 @@ namespace { // A list to keep track of the position states along the setup moves (from the // start position to the position just before the search starts). Needed by // 'draw by repetition' detection. - StateListPtr States(new std::vector(1)); + StateListPtr States(new std::deque(1)); // position() is called when engine receives the "position" UCI command. @@ -68,7 +68,7 @@ namespace { else return; - States = StateListPtr(new std::vector(1)); + States = StateListPtr(new std::deque(1)); pos.set(fen, Options["UCI_Chess960"], &States->back(), Threads.main()); // Parse move list (if any)