diff --git a/lib/clock.cpp b/lib/clock.cpp index 3bcc91b..9e90f2b 100644 --- a/lib/clock.cpp +++ b/lib/clock.cpp @@ -35,6 +35,7 @@ Clock::Clock(QObject *parent) m_blackTime(-1), m_blackIncrement(-1), m_moveTime(-1), + m_extraBudgetedTime(0), m_infinite(false), m_isExtended(false), m_deadline(0), @@ -205,7 +206,9 @@ void Clock::calculateDeadline(bool isPartial) const qint64 t = time(m_onTheClock); const qint64 inc = increment(m_onTheClock); const qint64 maximum = t - overhead; - const qint64 ideal = qRound((t / expectedHalfMovesTillEOG() + inc) * SearchSettings::openingTimeFactor); + const qint64 idealBase = (t / expectedHalfMovesTillEOG() + inc); + const qint64 idealBasePlusExtra = idealBase + qFloor(idealBase * m_extraBudgetedTime); + const qint64 ideal = qRound(idealBasePlusExtra * SearchSettings::openingTimeFactor); // Calculate the actual deadline qint64 deadline = 5000; diff --git a/lib/clock.h b/lib/clock.h index 1a339f4..71ed688 100644 --- a/lib/clock.h +++ b/lib/clock.h @@ -54,6 +54,9 @@ class Clock : public QObject { qint64 deadline() const { return m_deadline; } qint64 timeToDeadline() const; + float extraBudgetedTime() const { return m_extraBudgetedTime; } + void setExtraBudgetedTime(float t) { m_extraBudgetedTime = t; } + void setMaterialScore(int score) { m_materialScore = score; } void setHalfMoveNumber(int half) { m_halfMoveNumber = half; } @@ -85,6 +88,7 @@ private Q_SLOTS: qint64 m_blackIncrement; qint64 m_moveTime; + float m_extraBudgetedTime; bool m_infinite; bool m_isExtended; diff --git a/lib/searchengine.cpp b/lib/searchengine.cpp index 1875ee8..4f603fd 100644 --- a/lib/searchengine.cpp +++ b/lib/searchengine.cpp @@ -437,7 +437,7 @@ void SearchWorker::search() // Fill out the tree bool hardExit = fillOutTree(); if (hardExit) - emit requestStop(m_searchId); + emit requestStop(m_searchId, false /*isEarlyExit*/); } // Notify stop @@ -544,7 +544,7 @@ void SearchWorker::processWorkerInfo(const WorkerInfo &info) emit sendInfo(m_currentInfo, isPartial); if (!SearchSettings::featuresOff.testFlag(SearchSettings::EarlyExit) && shouldEarlyExit) - emit requestStop(m_searchId); + emit requestStop(m_searchId, true /*isEarlyExit*/); } WorkerThread::WorkerThread() @@ -588,10 +588,17 @@ SearchEngine::~SearchEngine() m_startedWorker = false; } +quint32 SearchEngine::estimatedNodes() const +{ + return m_worker->worker->estimatedNodes(); +} + void SearchEngine::setEstimatedNodes(quint32 nodes) { + if (!m_startedWorker) + return; + Q_ASSERT(!m_stop); - Q_ASSERT(m_startedWorker); Q_ASSERT(m_worker && m_worker->worker); m_worker->worker->setEstimatedNodes(nodes); } @@ -682,7 +689,7 @@ void SearchEngine::startSearch() } if (onlyLegalMove) { - requestStop(); + requestStop(true /*isEarlyExit*/); } else { Q_ASSERT(m_worker); m_worker->startWorker(m_tree, m_searchId); @@ -725,14 +732,14 @@ void SearchEngine::receivedSearchInfo(const SearchInfo &info, bool isPartial) emit sendInfo(info, isPartial); } -void SearchEngine::receivedRequestStop(int searchId) +void SearchEngine::receivedRequestStop(int searchId, bool isEarlyExit) { // It is possible this could have been queued before we were asked to stop // so ignore if so if (m_stop || searchId != m_searchId) return; - emit requestStop(); + emit requestStop(isEarlyExit); } void SearchEngine::printTree(const QVector &node, int depth, bool printPotentials) const { diff --git a/lib/searchengine.h b/lib/searchengine.h index 5eec773..5046775 100644 --- a/lib/searchengine.h +++ b/lib/searchengine.h @@ -78,6 +78,7 @@ class SearchWorker : public QObject { // These are thread safe void stopSearch(); + quint32 estimatedNodes() const { return m_estimatedNodes; } void setEstimatedNodes(quint32 nodes) { m_estimatedNodes = nodes; } @@ -87,7 +88,7 @@ public Q_SLOTS: Q_SIGNALS: void sendInfo(const SearchInfo &info, bool isPartial); void searchWorkerStopped(); - void requestStop(int searchId); + void requestStop(int searchId, bool); private Q_SLOTS: void search(); @@ -136,6 +137,7 @@ class SearchEngine : public QObject SearchEngine(QObject *parent = nullptr); ~SearchEngine() override; + quint32 estimatedNodes() const; void setEstimatedNodes(quint32 nodes); bool isStopped() const { return m_stop; } Tree *tree() const { return m_tree; } @@ -146,14 +148,14 @@ public Q_SLOTS: void stopSearch(); void searchWorkerStopped(); void receivedSearchInfo(const SearchInfo &info, bool isPartial); - void receivedRequestStop(int searchId); + void receivedRequestStop(int searchId, bool); void printTree(const QVector &node, int depth, bool printPotentials) const; void startPonder() {} void stopPonder() {} Q_SIGNALS: void sendInfo(const SearchInfo &info, bool isPartial); - void requestStop(); + void requestStop(bool); private: void resetSearch(const Search &search); diff --git a/lib/uciengine.cpp b/lib/uciengine.cpp index d193fdc..1cf8ee3 100644 --- a/lib/uciengine.cpp +++ b/lib/uciengine.cpp @@ -270,7 +270,7 @@ UciEngine::UciEngine(QObject *parent, const QString &debugFile) { m_searchEngine = new SearchEngine(this); connect(m_searchEngine, &SearchEngine::sendInfo, this, &UciEngine::sendInfo); - connect(m_searchEngine, &SearchEngine::requestStop, this, &UciEngine::stop); + connect(m_searchEngine, &SearchEngine::requestStop, this, &UciEngine::stopRequested); connect(m_clock, &Clock::timeout, this, &UciEngine::sendBestMove); } @@ -470,15 +470,15 @@ void UciEngine::sendBestMove() stopTheClock(); + const qint64 extraBudgetedTime = qMax(qint64(0), m_clock->timeToDeadline()); + m_clock->setExtraBudgetedTime(extraBudgetedTime / float(m_clock->deadline()) / float(SearchSettings::openingTimeFactor)); #if defined(DEBUG_TIME) - qint64 t = m_clock->timeToDeadline(); - if (t < 0) { + { QString out; QTextStream stream(&out); stream << "info" - << " deadline " << m_clock->deadline() - << " timeBudgetExceeded " << qAbs(t) - << endl; + << " extraBudgetedTime " << extraBudgetedTime << " as percent" << m_clock->extraBudgetedTime() + << endl; output(out); } #endif @@ -654,6 +654,7 @@ void UciEngine::uciNewGame() //qDebug() << "uciNewGame"; m_gameInitialized = true; + m_clock->setExtraBudgetedTime(0.f); m_searchEngine->reset(); Cache::globalInstance()->reset(); SearchSettings::debugInfo = Options::globalInstance()->option("DebugInfo").value() == "true"; @@ -682,6 +683,26 @@ void UciEngine::stop() sendBestMove(); } +void UciEngine::stopRequested(bool earlyExit) +{ +#if defined(DEBUG_TIME) + if (earlyExit) { + QString out; + QTextStream stream(&out); + stream << "info" + << " stopRequested estimatedNodes" << m_searchEngine->estimatedNodes() + << " rawnps " << m_averageInfo.rawnps + << endl; + output(out); + } +#else + Q_UNUSED(earlyExit); +#endif + + //qDebug() << "stop"; + stop(); +} + void UciEngine::quit() { //qDebug() << "quit"; @@ -808,12 +829,21 @@ void UciEngine::go(const Search& s) m_clock->setMaterialScore(p.materialScore(Chess::White) + p.materialScore(Chess::Black)); m_clock->setHalfMoveNumber(currentGame.halfMoveNumber()); m_clock->resetExtension(); - m_clock->startDeadline(p.activeArmy()); m_timeAtLastProgress = 0; m_depthTargeted = s.depth; m_nodesTargeted = s.nodes; m_lastInfo = SearchInfo(); + // Actually start the clock + m_clock->startDeadline(p.activeArmy()); +#if defined(DEBUG_TIME) + QString out; + QTextStream stream(&out); + stream << "info" + << " clock deadline " << m_clock->deadline() << " extra time " << m_clock->extraBudgetedTime() + << endl; + output(out); +#endif startSearch(); } diff --git a/lib/uciengine.h b/lib/uciengine.h index a9a4aec..9fc4fea 100644 --- a/lib/uciengine.h +++ b/lib/uciengine.h @@ -125,6 +125,7 @@ public Q_SLOTS: void uciNewGame(); void ponderHit(); void stop(); + void stopRequested(bool); void quit(); void readyRead(const QString &line); void installIOHandler(IOHandler *io) { m_ioHandler = io; }