Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 pmbaty 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_ */