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.  * search.hpp
  21.  *
  22.  *  Created on: Feb 25, 2012
  23.  *      Author: petero
  24.  */
  25.  
  26. #ifndef SEARCH_HPP_
  27. #define SEARCH_HPP_
  28.  
  29. #include "constants.hpp"
  30. #include "position.hpp"
  31. #include "killerTable.hpp"
  32. #include "history.hpp"
  33. #include "transpositionTable.hpp"
  34. #include "evaluate.hpp"
  35. #include "treeLogger.hpp"
  36. #include "moveGen.hpp"
  37. #include "searchUtil.hpp"
  38. #include "parallel.hpp"
  39. #include "util/histogram.hpp"
  40.  
  41. #include <limits>
  42. #include <memory>
  43.  
  44.  
  45. class SearchTest;
  46. class ChessTool;
  47. class PosGenerator;
  48.  
  49. /** Implements the nega-scout search algorithm. */
  50. class Search {
  51.     friend class SearchTest;
  52.     friend class ChessTool;
  53.     friend class PosGenerator;
  54. public:
  55.     /** Help tables used by the search. */
  56.     struct SearchTables {
  57.         SearchTables(TranspositionTable& tt0, KillerTable& kt0, History& ht0,
  58.                      Evaluate::EvalHashTables& et0);
  59.         TranspositionTable& tt;
  60.         KillerTable& kt;
  61.         History& ht;
  62.         Evaluate::EvalHashTables& et;
  63.     };
  64.  
  65.     /** Constructor. */
  66.     Search(const Position& pos, const std::vector<U64>& posHashList,
  67.            int posHashListSize, SearchTables& st,
  68.            ParallelData& pd, const std::shared_ptr<SplitPoint>& rootSp,
  69.            TreeLogger& logFile);
  70.  
  71.     Search(const Search& other) = delete;
  72.     Search& operator=(const Search& other) = delete;
  73.  
  74.     /** Interface for reporting search information during search. */
  75.     class Listener {
  76.     public:
  77.         virtual void notifyDepth(int depth) = 0;
  78.         virtual void notifyCurrMove(const Move& m, int moveNr) = 0;
  79.         virtual void notifyPV(int depth, int score, int time, U64 nodes, int nps,
  80.                               bool isMate, bool upperBound, bool lowerBound,
  81.                               const std::vector<Move>& pv, int multiPVIndex,
  82.                               U64 tbHits) = 0;
  83.         virtual void notifyStats(U64 nodes, int nps, U64 tbHits, int time) = 0;
  84.     };
  85.  
  86.     void setListener(const std::shared_ptr<Listener>& listener);
  87.  
  88.     /** Exception thrown to stop the search. */
  89.     class StopSearch : public std::exception {
  90.     };
  91.  
  92.     class StopHandler {
  93.     public:
  94.         virtual bool shouldStop() = 0;
  95.     };
  96.  
  97.     void setStopHandler(const std::shared_ptr<StopHandler>& stopHandler);
  98.  
  99.     /** Set which thread is owning this Search object. */
  100.     void setThreadNo(int tNo);
  101.  
  102.     void timeLimit(int minTimeLimit, int maxTimeLimit);
  103.  
  104.     void setStrength(int strength, U64 randomSeed);
  105.  
  106.     /** Set minimum depth for TB probes. */
  107.     void setMinProbeDepth(int depth);
  108.  
  109.     Move iterativeDeepening(const MoveList& scMovesIn,
  110.                             int maxDepth, U64 initialMaxNodes, bool verbose,
  111.                             int maxPV = 1, bool onlyExact = false,
  112.                             int minProbeDepth = 0);
  113.  
  114.     /**
  115.      * Main recursive search algorithm.
  116.      * @return Score for the side to make a move, in position given by "pos".
  117.      */
  118.     template <bool smp, bool tb>
  119.     int negaScout(int alpha, int beta, int ply, int depth, int recaptureSquare,
  120.                   const bool inCheck);
  121.     int negaScout(bool smp, bool tb,
  122.                   int alpha, int beta, int ply, int depth, int recaptureSquare,
  123.                   const bool inCheck);
  124.     int negaScout(int alpha, int beta, int ply, int depth, int recaptureSquare,
  125.                   const bool inCheck);
  126.  
  127.     /** Compute extension depth for a move. */
  128.     int getMoveExtend(const Move& m, int recaptureSquare);
  129.  
  130.     static bool canClaimDraw50(const Position& pos);
  131.  
  132.     static bool canClaimDrawRep(const Position& pos, const std::vector<U64>& posHashList,
  133.                                 int posHashListSize, int posHashFirstNew);
  134.  
  135.     /**
  136.      * Compute scores for each move in a move list, using SEE, killer and history information.
  137.      * @param moves  List of moves to score.
  138.      */
  139.     void scoreMoveList(MoveList& moves, int ply, int startIdx = 0);
  140.  
  141.     /** Set search tree information for a given ply. */
  142.     void setSearchTreeInfo(int ply, const SearchTreeInfo& sti,
  143.                            const Move& currMove, int currMoveNo, int lmr,
  144.                            U64 rootNodeIdx);
  145.  
  146.     /** Get total number of nodes searched by this thread. */
  147.     S64 getTotalNodesThisThread() const;
  148.  
  149.     /** Get number of TB hits for this thread. */
  150.     S64 getTbHitsThisThread() const;
  151.  
  152. private:
  153.     void init(const Position& pos0, const std::vector<U64>& posHashList0,
  154.               int posHashListSize0);
  155.  
  156.     /** Information used for move ordering at root and for PV reporting. */
  157.     struct MoveInfo {
  158.         Move move;
  159.         U64 nodes;
  160.         int depth, alpha, beta;
  161.         std::vector<Move> pv;
  162.         MoveInfo(const Move& m, int n)
  163.             : move(m), nodes(n), depth(0), alpha(0), beta(0) {}
  164.  
  165.         int score() const { return move.score(); }
  166.  
  167.         struct SortByScore {
  168.             bool operator()(const MoveInfo& mi1, const MoveInfo& mi2) const {
  169.                 return mi1.move.score() > mi2.move.score();
  170.             }
  171.         };
  172.         struct SortByNodes {
  173.             bool operator()(const MoveInfo& mi1, const MoveInfo& mi2) {
  174.                 return mi1.nodes > mi2.nodes;
  175.             }
  176.         };
  177.     };
  178.  
  179.     /** Store depth, alpha, beta, score and pv in scMoves[mi]. */
  180.     void storeSearchResult(std::vector<MoveInfo>& scMoves, int mi, int depth,
  181.                            int alpha, int beta, int score);
  182.  
  183.     /** Report PV information to listener. */
  184.     void notifyPV(const std::vector<MoveInfo>& moveInfo, int mi, int maxPV);
  185.     void notifyPV(const MoveInfo& info, int multiPVIndex);
  186.  
  187.     /** Report search statistics to listener. */
  188.     void notifyStats();
  189.  
  190.     /** Get total number of nodes searched by all threads. */
  191.     S64 getTotalNodes() const;
  192.  
  193.     /** Get total number of TB hits for all threads. */
  194.     S64 getTbHits() const;
  195.  
  196.     /** Determine which root moves to search, taking low strength and
  197.      *  missing TB files into account. */
  198.     void getRootMoves(const MoveList& rootMovesIn,
  199.                       std::vector<MoveInfo>& rootMovesOut,
  200.                       int maxDepth);
  201.  
  202.     /** Return true if move should be skipped in order to make engine play weaker. */
  203.     bool weakPlaySkipMove(const Position& pos, const Move& m, int ply) const;
  204.  
  205.     static bool passedPawnPush(const Position& pos, const Move& m);
  206.  
  207.     /** Quiescence search. Only non-losing captures are searched. */
  208.     int quiesce(int alpha, int beta, int ply, int depth, const bool inCheck);
  209.  
  210.     /**
  211.      * Static exchange evaluation function.
  212.      * @return SEE score for m. Positive value is good for the side that makes the first move.
  213.      */
  214.     int SEE(const Move& m);
  215.  
  216.     /** Return >0, 0, <0, depending on the sign of SEE(m). */
  217.     int signSEE(const Move& m);
  218.  
  219.     /** Return true if SEE(m) < 0. */
  220.     bool negSEE(const Move& m);
  221.  
  222.     /** Score move list according to most valuable victim / least valuable attacker. */
  223.     void scoreMoveListMvvLva(MoveList& moves) const;
  224.  
  225.     /** Find move with highest score and move it to the front of the list. */
  226.     static void selectBest(MoveList& moves, int startIdx);
  227.  
  228.     /** If hashMove exists in the move list, move the hash move to the front of the list. */
  229.     static bool selectHashMove(MoveList& moves, const Move& hashMove);
  230.  
  231.     void initNodeStats();
  232.  
  233.     class DefaultStopHandler : public StopHandler {
  234.     public:
  235.         DefaultStopHandler(Search& sc0) : sc(sc0) { }
  236.         bool shouldStop() { return sc.shouldStop(); }
  237.     private:
  238.         Search& sc;
  239.     };
  240.  
  241.     /** Return true if the search should be stopped immediately. */
  242.     bool shouldStop();
  243.  
  244.  
  245.  
  246.     Position pos;
  247.     Evaluate eval;
  248.     KillerTable& kt;
  249.     History& ht;
  250.     std::vector<U64> posHashList; // List of hashes for previous positions up to the last "zeroing" move.
  251.     int posHashListSize;          // Number of used entries in posHashList
  252.     int posHashFirstNew;          // First entry in posHashList that has not been played OTB.
  253.     TranspositionTable& tt;
  254.     ParallelData& pd;
  255.     std::vector<std::shared_ptr<SplitPoint>> spVec;
  256.     std::vector<std::shared_ptr<SplitPoint>> pending;
  257.     int threadNo;
  258.     bool mainNumaNode; // True if this thread runs on the NUMA node holding the transposition table
  259.     TreeLogger& logFile;
  260.  
  261.     std::shared_ptr<Listener> listener;
  262.     std::shared_ptr<StopHandler> stopHandler;
  263.     Move emptyMove;
  264.  
  265.     static const int MAX_SEARCH_DEPTH = 100;
  266.     SearchTreeInfo searchTreeInfo[MAX_SEARCH_DEPTH * 2];
  267.  
  268.     // Time management
  269.     S64 tStart;                // Time when search started
  270.     RelaxedShared<S64> minTimeMillis; // Minimum recommended thinking time
  271.     RelaxedShared<S64> maxTimeMillis; // Maximum allowed thinking time
  272.     bool searchNeedMoreTime;   // True if negaScout should use up to maxTimeMillis time.
  273.     S64 maxNodes;              // Maximum number of nodes to search (approximately)
  274.     int minProbeDepth;         // Minimum depth to probe endgame tablebases.
  275.     int nodesToGo;             // Number of nodes until next time check
  276.     RelaxedShared<int> nodesBetweenTimeCheck; // How often to check remaining time
  277.  
  278.     // Reduced strength variables
  279.     int strength;              // Strength (0-1000)
  280.     bool weak;                 // True if strength < 1000
  281.     U64 randomSeed;
  282.  
  283.     // Search statistics stuff
  284.     Histogram<0,20> nodesByPly, nodesByDepth;
  285.     S64 totalNodes;
  286.     S64 tbHits;
  287.     S64 tLastStats;        // Time when notifyStats was last called
  288.     bool verbose;
  289.  
  290.     int q0Eval; // Static eval score at first level of quiescence search
  291. };
  292.  
  293. inline
  294. Search::SearchTables::SearchTables(TranspositionTable& tt0, KillerTable& kt0, History& ht0,
  295.                                    Evaluate::EvalHashTables& et0)
  296.     : tt(tt0), kt(kt0), ht(ht0), et(et0) {
  297. }
  298.  
  299. inline void
  300. Search::setListener(const std::shared_ptr<Listener>& listener) {
  301.     this->listener = listener;
  302. }
  303.  
  304. inline void
  305. Search::setStopHandler(const std::shared_ptr<StopHandler>& stopHandler) {
  306.     this->stopHandler = stopHandler;
  307. }
  308.  
  309. inline bool
  310. Search::canClaimDrawRep(const Position& pos, const std::vector<U64>& posHashList,
  311.                        int posHashListSize, int posHashFirstNew) {
  312.     int reps = 0;
  313.     int stop = std::max(0, posHashListSize - pos.getHalfMoveClock());
  314.     for (int i = posHashListSize - 4; i >= stop; i -= 2) {
  315.         if (pos.zobristHash() == posHashList[i]) {
  316.             reps++;
  317.             if ((i >= posHashFirstNew) || (reps >= 2))
  318.                 return true;
  319.         }
  320.     }
  321.     return false;
  322. }
  323.  
  324. inline bool
  325. Search::passedPawnPush(const Position& pos, const Move& m) {
  326.     int p = pos.getPiece(m.from());
  327.     if (pos.isWhiteMove()) {
  328.         if (p != Piece::WPAWN)
  329.             return false;
  330.         if ((BitBoard::wPawnBlockerMask[m.to()] & pos.pieceTypeBB(Piece::BPAWN)) != 0)
  331.             return false;
  332.         return m.to() >= 40;
  333.     } else {
  334.         if (p != Piece::BPAWN)
  335.             return false;
  336.         if ((BitBoard::bPawnBlockerMask[m.to()] & pos.pieceTypeBB(Piece::WPAWN)) != 0)
  337.             return false;
  338.         return m.to() <= 23;
  339.     }
  340. }
  341.  
  342. inline int
  343. Search::signSEE(const Move& m) {
  344.     int p0 = ::pieceValue[pos.getPiece(m.from())];
  345.     int p1 = ::pieceValue[pos.getPiece(m.to())];
  346.     if (p0 < p1)
  347.         return 1;
  348.     return SEE(m);
  349. }
  350.  
  351. inline bool
  352. Search::negSEE(const Move& m) {
  353.     int p0 = ::pieceValue[pos.getPiece(m.from())];
  354.     int p1 = ::pieceValue[pos.getPiece(m.to())];
  355.     if (p1 >= p0)
  356.         return false;
  357.     return SEE(m) < 0;
  358. }
  359.  
  360. inline void
  361. Search::scoreMoveListMvvLva(MoveList& moves) const {
  362.     for (int i = 0; i < moves.size; i++) {
  363.         Move& m = moves[i];
  364.         int v = pos.getPiece(m.to());
  365.         int a = pos.getPiece(m.from());
  366.         m.setScore(Evaluate::pieceValueOrder[v] * 8 - Evaluate::pieceValueOrder[a]);
  367.     }
  368. }
  369.  
  370. inline void
  371. Search::selectBest(MoveList& moves, int startIdx) {
  372.     int bestIdx = startIdx;
  373.     int bestScore = moves[bestIdx].score();
  374.     for (int i = startIdx + 1; i < moves.size; i++) {
  375.         int sc = moves[i].score();
  376.         if (sc > bestScore) {
  377.             bestIdx = i;
  378.             bestScore = sc;
  379.         }
  380.     }
  381.     std::swap(moves[bestIdx], moves[startIdx]);
  382. }
  383.  
  384. inline void
  385. Search::setSearchTreeInfo(int ply, const SearchTreeInfo& sti, const Move& currMove,
  386.                           int currMoveNo, int lmr, U64 rootNodeIdx) {
  387.     searchTreeInfo[ply] = sti;
  388.     searchTreeInfo[ply].currentMove = currMove;
  389.     searchTreeInfo[ply].currentMoveNo = currMoveNo;
  390.     searchTreeInfo[ply].lmr = lmr;
  391.     searchTreeInfo[ply].nodeIdx = rootNodeIdx;
  392. }
  393.  
  394. inline int
  395. Search::negaScout(bool smp, bool tb,
  396.                   int alpha, int beta, int ply, int depth, int recaptureSquare,
  397.                   const bool inCheck) {
  398.     using namespace SearchConst;
  399.     int minDepth = pd.wq.getMinSplitDepth() * plyScale;
  400.     if (threadNo == 0)
  401.         minDepth = (minDepth + MIN_SMP_DEPTH * plyScale) / 2;
  402.     if (smp && (depth >= minDepth) &&
  403.                ((int)spVec.size() < MAX_SP_PER_THREAD)) {
  404.         bool tb2 = tb && depth >= minProbeDepth;
  405.         if (tb2)
  406.             return negaScout<true,true>(alpha, beta, ply, depth, recaptureSquare, inCheck);
  407.         else
  408.             return negaScout<true,false>(alpha, beta, ply, depth, recaptureSquare, inCheck);
  409.     } else {
  410.         bool tb2 = tb && depth >= minProbeDepth;
  411.         if (tb2)
  412.             return negaScout<false,true>(alpha, beta, ply, depth, recaptureSquare, inCheck);
  413.         else
  414.             return negaScout<false,false>(alpha, beta, ply, depth, recaptureSquare, inCheck);
  415.     }
  416. }
  417.  
  418. inline int
  419. Search::negaScout(int alpha, int beta, int ply, int depth, int recaptureSquare,
  420.                   const bool inCheck) {
  421.     return negaScout<false,false>(alpha, beta, ply, depth, recaptureSquare, inCheck);
  422. }
  423.  
  424. inline bool
  425. Search::canClaimDraw50(const Position& pos) {
  426.     return (pos.getHalfMoveClock() >= 100);
  427. }
  428.  
  429. inline S64
  430. Search::getTotalNodes() const {
  431.     return totalNodes + pd.getNumSearchedNodes();
  432. }
  433.  
  434. inline S64
  435. Search::getTotalNodesThisThread() const {
  436.     return totalNodes;
  437. }
  438.  
  439. inline S64
  440. Search::getTbHits() const {
  441.     return tbHits + pd.getTbHits();
  442. }
  443.  
  444. inline S64
  445. Search::getTbHitsThisThread() const {
  446.     return tbHits;
  447. }
  448.  
  449. inline void
  450. Search::setMinProbeDepth(int depth) {
  451.     minProbeDepth = depth * SearchConst::plyScale;
  452. }
  453.  
  454. #endif /* SEARCH_HPP_ */
  455.