Subversion Repositories Games.Chess Giants

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.     Texel - A UCI chess engine.
  3.     Copyright (C) 2012-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
  4.  
  5.     This program is free software: you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation, either version 3 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. /*
  20.  * enginecontrol.cpp
  21.  *
  22.  *  Created on: Mar 4, 2012
  23.  *      Author: petero
  24.  */
  25.  
  26. #define _GLIBCXX_USE_NANOSLEEP
  27.  
  28. #include "enginecontrol.hpp"
  29. #include "util/random.hpp"
  30. #include "searchparams.hpp"
  31. #include "book.hpp"
  32. #include "textio.hpp"
  33. #include "parameters.hpp"
  34. #include "moveGen.hpp"
  35. #include "util/logger.hpp"
  36. #include "numa.hpp"
  37.  
  38. #include <iostream>
  39. #include <memory>
  40. #include <chrono>
  41.  
  42.  
  43. EngineControl::SearchListener::SearchListener(std::ostream& os0)
  44.     : os(os0)
  45. {
  46. }
  47.  
  48. void
  49. EngineControl::SearchListener::notifyDepth(int depth) {
  50. //    std::lock_guard<std::mutex> L(Logger::getLogMutex());
  51.     os << "info depth " << depth << std::endl;
  52. }
  53.  
  54. void
  55. EngineControl::SearchListener::notifyCurrMove(const Move& m, int moveNr) {
  56. //    std::lock_guard<std::mutex> L(Logger::getLogMutex());
  57.     os << "info currmove " << moveToString(m) << " currmovenumber " << moveNr << std::endl;
  58. }
  59.  
  60. void
  61. EngineControl::SearchListener::notifyPV(int depth, int score, int time, U64 nodes, int nps, bool isMate,
  62.                                         bool upperBound, bool lowerBound, const std::vector<Move>& pv,
  63.                                         int multiPVIndex, U64 tbHits) {
  64. //    std::lock_guard<std::mutex> L(Logger::getLogMutex());
  65.     std::string pvBuf;
  66.     for (size_t i = 0; i < pv.size(); i++) {
  67.         pvBuf += ' ';
  68.         pvBuf += moveToString(pv[i]);
  69.     }
  70.     std::string bound;
  71.     if (upperBound) {
  72.         bound = " upperbound";
  73.     } else if (lowerBound) {
  74.         bound = " lowerbound";
  75.     }
  76.     os << "info depth " << depth << " score " << (isMate ? "mate " : "cp ")
  77.        << score << bound << " time " << time << " nodes " << nodes
  78.        << " nps " << nps;
  79.     if (tbHits > 0)
  80.         os << " tbhits " << tbHits;
  81.     if (multiPVIndex >= 0)
  82.         os << " multipv " << (multiPVIndex + 1);
  83.     os << " pv" << pvBuf << std::endl;
  84. }
  85.  
  86. void
  87. EngineControl::SearchListener::notifyStats(U64 nodes, int nps, U64 tbHits, int time) {
  88.     os << "info nodes " << nodes << " nps " << nps;
  89.     if (tbHits > 0)
  90.         os << " tbhits " << tbHits;
  91.     os << " time " << time << std::endl;
  92. }
  93.  
  94. EngineControl::EngineControl(std::ostream& o)
  95.     : os(o),
  96.       shouldDetach(true),
  97.       tt(8),
  98.       pd(tt),
  99.       randomSeed(0)
  100. {
  101.     Numa::instance().bindThread(0);
  102.     hashParListenerId = UciParams::hash->addListener([this]() {
  103.         setupTT();
  104.     });
  105.     clearHashParListenerId = UciParams::clearHash->addListener([this]() {
  106.         tt.clear();
  107.         ht.init();
  108.     }, false);
  109.     et = Evaluate::getEvalHashTables();
  110. }
  111.  
  112. EngineControl::~EngineControl() {
  113.     UciParams::hash->removeListener(hashParListenerId);
  114.     UciParams::hash->removeListener(clearHashParListenerId);
  115. }
  116.  
  117. void
  118. EngineControl::startSearch(const Position& pos, const std::vector<Move>& moves, const SearchParams& sPar) {
  119.     stopSearch();
  120.     setupPosition(pos, moves);
  121.     computeTimeLimit(sPar);
  122.     ponder = false;
  123.     infinite = (maxTimeLimit < 0) && (maxDepth < 0) && (maxNodes < 0);
  124.     searchMoves = sPar.searchMoves;
  125.     startThread(minTimeLimit, maxTimeLimit, maxDepth, maxNodes);
  126. }
  127.  
  128. void
  129. EngineControl::startPonder(const Position& pos, const std::vector<Move>& moves, const SearchParams& sPar) {
  130.     stopSearch();
  131.     setupPosition(pos, moves);
  132.     computeTimeLimit(sPar);
  133.     ponder = true;
  134.     infinite = false;
  135.     startThread(-1, -1, -1, -1);
  136. }
  137.  
  138. void
  139. EngineControl::ponderHit() {
  140.     std::shared_ptr<Search> mySearch;
  141.     {
  142.         std::lock_guard<std::mutex> L(threadMutex);
  143.         mySearch = sc;
  144.     }
  145.     if (mySearch) {
  146.         if (onePossibleMove) {
  147.             if (minTimeLimit > 1) minTimeLimit = 1;
  148.             if (maxTimeLimit > 1) maxTimeLimit = 1;
  149.         }
  150.         mySearch->timeLimit(minTimeLimit, maxTimeLimit);
  151.     }
  152.     infinite = (maxTimeLimit < 0) && (maxDepth < 0) && (maxNodes < 0);
  153.     ponder = false;
  154. }
  155.  
  156. void
  157. EngineControl::stopSearch() {
  158.     stopThread();
  159. }
  160.  
  161. void
  162. EngineControl::newGame() {
  163.     randomSeed = Random().nextU64();
  164.     tt.clear();
  165.     ht.init();
  166. }
  167.  
  168. /**
  169.  * Compute thinking time for current search.
  170.  */
  171. void
  172. EngineControl::computeTimeLimit(const SearchParams& sPar) {
  173.     minTimeLimit = -1;
  174.     maxTimeLimit = -1;
  175.     maxDepth = -1;
  176.     maxNodes = -1;
  177.     if (sPar.infinite) {
  178.         minTimeLimit = -1;
  179.         maxTimeLimit = -1;
  180.         maxDepth = -1;
  181.     } else {
  182.         if (sPar.depth > 0)
  183.             maxDepth = sPar.depth;
  184.         if (sPar.mate > 0) {
  185.             int md = sPar.mate * 2 - 1;
  186.             maxDepth = maxDepth == -1 ? md : std::min(maxDepth, md);
  187.         }
  188.         if (sPar.nodes > 0)
  189.             maxNodes = sPar.nodes;
  190.  
  191.         if (sPar.moveTime > 0) {
  192.              minTimeLimit = maxTimeLimit = sPar.moveTime;
  193.         } else if (sPar.wTime || sPar.bTime) {
  194.             int moves = sPar.movesToGo;
  195.             if (moves == 0)
  196.                 moves = 999;
  197.             moves = std::min(moves, static_cast<int>(timeMaxRemainingMoves)); // Assume at most N more moves until end of game
  198.             bool white = pos.isWhiteMove();
  199.             int time = white ? sPar.wTime : sPar.bTime;
  200.             int inc  = white ? sPar.wInc : sPar.bInc;
  201.             const int margin = std::min(static_cast<int>(bufferTime), time * 9 / 10);
  202.             int timeLimit = (time + inc * (moves - 1) - margin) / moves;
  203.             minTimeLimit = (int)(timeLimit * minTimeUsage * 0.01);
  204.             if (UciParams::ponder->getBoolPar()) {
  205.                 const double ponderHitRate = timePonderHitRate * 0.01;
  206.                 minTimeLimit = (int)ceil(minTimeLimit / (1 - ponderHitRate));
  207.             }
  208.             maxTimeLimit = (int)(minTimeLimit * clamp(moves * 0.5, 2.5, static_cast<int>(maxTimeUsage) * 0.01));
  209.  
  210.             // Leave at least 1s on the clock, but can't use negative time
  211.             minTimeLimit = clamp(minTimeLimit, 1, time - margin);
  212.             maxTimeLimit = clamp(maxTimeLimit, 1, time - margin);
  213.         }
  214.     }
  215. }
  216.  
  217. void
  218. EngineControl::startThread(int minTimeLimit, int maxTimeLimit, int maxDepth, int maxNodes) {
  219.     Search::SearchTables st(tt, kt, ht, *et);
  220.     sc = std::make_shared<Search>(pos, posHashList, posHashListSize, st, pd, nullptr, treeLog);
  221.     sc->setListener(std::make_shared<SearchListener>(os));
  222.     sc->setStrength(UciParams::strength->getIntPar(), randomSeed);
  223.     std::shared_ptr<MoveList> moves(std::make_shared<MoveList>());
  224.     MoveGen::pseudoLegalMoves(pos, *moves);
  225.     MoveGen::removeIllegal(pos, *moves);
  226.     if (searchMoves.size() > 0)
  227.         moves->filter(searchMoves);
  228.     onePossibleMove = false;
  229.     if ((moves->size < 2) && !infinite) {
  230.         onePossibleMove = true;
  231.         if (!ponder) {
  232.             if (maxTimeLimit > 0) {
  233.                 maxTimeLimit = clamp(maxTimeLimit/100, 1, 100);
  234.                 minTimeLimit = clamp(minTimeLimit/100, 1, 100);
  235.             } else {
  236.                 if ((maxDepth < 0) || (maxDepth > 2))
  237.                     maxDepth = 2;
  238.             }
  239.         }
  240.     }
  241.     pd.addRemoveWorkers(UciParams::threads->getIntPar() - 1);
  242.     pd.wq.resetSplitDepth();
  243.     pd.startAll();
  244.     sc->timeLimit(minTimeLimit, maxTimeLimit);
  245.     tt.nextGeneration();
  246.     bool ownBook = UciParams::ownBook->getBoolPar();
  247.     bool analyseMode = UciParams::analyseMode->getBoolPar();
  248.     int maxPV = (infinite || analyseMode) ? UciParams::multiPV->getIntPar() : 1;
  249.     int minProbeDepth = UciParams::minProbeDepth->getIntPar();
  250.     if (analyseMode) {
  251.         Evaluate eval(*et);
  252.         int evScore = eval.evalPosPrint(pos) * (pos.isWhiteMove() ? 1 : -1);
  253.         std::stringstream ss;
  254.         ss.precision(2);
  255.         ss << std::fixed << (evScore / 100.0);
  256.         os << "info string Eval: " << ss.str() << std::endl;
  257.     }
  258.     auto f = [this,ownBook,analyseMode,moves,maxDepth,maxNodes,maxPV,minProbeDepth]() {
  259.         Numa::instance().bindThread(0);
  260.         Move m;
  261.         if (ownBook && !analyseMode) {
  262.             Book book(false);
  263.             book.getBookMove(pos, m);
  264.         }
  265.         if (m.isEmpty())
  266.             m = sc->iterativeDeepening(*moves, maxDepth, maxNodes, false, maxPV, false, minProbeDepth);
  267.         while (ponder || infinite) {
  268.             // We should not respond until told to do so. Just wait until
  269.             // we are allowed to respond.
  270.             std::this_thread::sleep_for(std::chrono::milliseconds(10));
  271.         }
  272.         Move ponderMove = getPonderMove(pos, m);
  273.         std::lock_guard<std::mutex> L(threadMutex);
  274.         os << "bestmove " << moveToString(m);
  275.         if (!ponderMove.isEmpty())
  276.             os << " ponder " << moveToString(ponderMove);
  277.         os << std::endl;
  278.         if (shouldDetach) {
  279.             engineThread->detach();
  280.             pd.stopAll();
  281.             pd.fhInfo.reScale();
  282.         }
  283.         engineThread.reset();
  284.         sc.reset();
  285.         for (auto& p : pendingOptions)
  286.             setOption(p.first, p.second, false);
  287.         pendingOptions.clear();
  288.     };
  289.     shouldDetach = true;
  290.     {
  291.         std::lock_guard<std::mutex> L(threadMutex);
  292.         engineThread = std::make_shared<std::thread>(f);
  293.     }
  294. }
  295.  
  296. void
  297. EngineControl::stopThread() {
  298.     std::shared_ptr<std::thread> myThread;
  299.     {
  300.         std::lock_guard<std::mutex> L(threadMutex);
  301.         myThread = engineThread;
  302.         if (myThread) {
  303.             sc->timeLimit(0, 0);
  304.             infinite = false;
  305.             ponder = false;
  306.             shouldDetach = false;
  307.         }
  308.     }
  309.     if (myThread)
  310.         myThread->join();
  311.     pd.stopAll();
  312.     pd.fhInfo.reScale();
  313. }
  314.  
  315. void
  316. EngineControl::setupTT() {
  317.     int hashSizeMB = UciParams::hash->getIntPar();
  318.     U64 nEntries = hashSizeMB > 0 ? ((U64)hashSizeMB) * (1 << 20) / sizeof(TranspositionTable::TTEntry)
  319.                                   : (U64)1024;
  320.     int logSize = 0;
  321.     while (nEntries > 1) {
  322.         logSize++;
  323.         nEntries /= 2;
  324.     }
  325.     logSize++;
  326.     while (true) {
  327.         try {
  328.             logSize--;
  329.             if (logSize <= 0)
  330.                 break;
  331.             tt.reSize(logSize);
  332.             break;
  333.         } catch (const std::bad_alloc& /*ex*/) { // Pierre-Marie Baty -- unreferenced variable
  334.         }
  335.     }
  336. }
  337.  
  338. void
  339. EngineControl::setupPosition(Position pos, const std::vector<Move>& moves) {
  340.     UndoInfo ui;
  341.     posHashList.resize(200 + moves.size());
  342.     posHashListSize = 0;
  343.     for (size_t i = 0; i < moves.size(); i++) {
  344.         const Move& m = moves[i];
  345.         posHashList[posHashListSize++] = pos.zobristHash();
  346.         pos.makeMove(m, ui);
  347.         if (pos.getHalfMoveClock() == 0)
  348.             posHashListSize = 0;
  349.     }
  350.     this->pos = pos;
  351. }
  352.  
  353. /**
  354.  * Try to find a move to ponder from the transposition table.
  355.  */
  356. Move
  357. EngineControl::getPonderMove(Position pos, const Move& m) {
  358.     Move ret;
  359.     if (m.isEmpty())
  360.         return ret;
  361.     UndoInfo ui;
  362.     pos.makeMove(m, ui);
  363.     TranspositionTable::TTEntry ent;
  364.     ent.clear();
  365.     tt.probe(pos.historyHash(), ent);
  366.     if (ent.getType() != TType::T_EMPTY) {
  367.         ent.getMove(ret);
  368.         MoveList moves;
  369.         MoveGen::pseudoLegalMoves(pos, moves);
  370.         MoveGen::removeIllegal(pos, moves);
  371.         bool contains = false;
  372.         for (int mi = 0; mi < moves.size; mi++)
  373.             if (moves[mi].equals(ret)) {
  374.                 contains = true;
  375.                 break;
  376.             }
  377.         if  (!contains)
  378.             ret = Move();
  379.     }
  380.     pos.unMakeMove(m, ui);
  381.     return ret;
  382. }
  383.  
  384. std::string
  385. EngineControl::moveToString(const Move& m) {
  386.     if (m.isEmpty())
  387.         return "0000";
  388.     std::string ret = TextIO::squareToString(m.from());
  389.     ret += TextIO::squareToString(m.to());
  390.     switch (m.promoteTo()) {
  391.     case Piece::WQUEEN:
  392.     case Piece::BQUEEN:
  393.         ret += 'q';
  394.         break;
  395.     case Piece::WROOK:
  396.     case Piece::BROOK:
  397.         ret += 'r';
  398.         break;
  399.     case Piece::WBISHOP:
  400.     case Piece::BBISHOP:
  401.         ret += 'b';
  402.         break;
  403.     case Piece::WKNIGHT:
  404.     case Piece::BKNIGHT:
  405.         ret += 'n';
  406.         break;
  407.     default:
  408.         break;
  409.     }
  410.     return ret;
  411. }
  412.  
  413. void
  414. EngineControl::printOptions(std::ostream& os) {
  415.     std::vector<std::string> parNames;
  416.     Parameters::instance().getParamNames(parNames);
  417.     for (const auto& pName : parNames) {
  418.         std::shared_ptr<Parameters::ParamBase> p = Parameters::instance().getParam(pName);
  419.         switch (p->type) {
  420.         case Parameters::CHECK: {
  421.             const Parameters::CheckParam& cp = dynamic_cast<const Parameters::CheckParam&>(*p.get());
  422.             os << "option name " << cp.name << " type check default "
  423.                << (cp.defaultValue?"true":"false") << std::endl;
  424.             break;
  425.         }
  426.         case Parameters::SPIN: {
  427.             const Parameters::SpinParam& sp = dynamic_cast<const Parameters::SpinParam&>(*p.get());
  428.             os << "option name " << sp.name << " type spin default "
  429.                << sp.getDefaultValue() << " min " << sp.getMinValue()
  430.                << " max " << sp.getMaxValue() << std::endl;
  431.             break;
  432.         }
  433.         case Parameters::COMBO: {
  434.             const Parameters::ComboParam& cp = dynamic_cast<const Parameters::ComboParam&>(*p.get());
  435.             os << "option name " << cp.name << " type combo default " << cp.defaultValue;
  436.             for (size_t i = 0; i < cp.allowedValues.size(); i++)
  437.                 os << " var " << cp.allowedValues[i];
  438.             os << std::endl;
  439.             break;
  440.         }
  441.         case Parameters::BUTTON:
  442.             os << "option name " << p->name << " type button" << std::endl;
  443.             break;
  444.         case Parameters::STRING: {
  445.             const Parameters::StringParam& sp = dynamic_cast<const Parameters::StringParam&>(*p.get());
  446.             os << "option name " << sp.name << " type string default "
  447.                << sp.defaultValue << std::endl;
  448.             break;
  449.         }
  450.         }
  451.     }
  452. }
  453.  
  454. void
  455. EngineControl::setOption(const std::string& optionName, const std::string& optionValue,
  456.                          bool deferIfBusy) {
  457.     Parameters& params = Parameters::instance();
  458.     if (deferIfBusy) {
  459.         std::lock_guard<std::mutex> L(threadMutex);
  460.         if (engineThread) {
  461.             if (params.getParam(optionName))
  462.                 pendingOptions[optionName] = optionValue;
  463.             return;
  464.         }
  465.     }
  466.     std::shared_ptr<Parameters::ParamBase> par = params.getParam(optionName);
  467.     if (par && par->type == Parameters::STRING && optionValue == "<empty>")
  468.         params.set(optionName, "");
  469.     else
  470.         params.set(optionName, optionValue);
  471. }
  472.