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
 * transpositionTable.hpp
21
 *
22
 *  Created on: Feb 25, 2012
23
 *      Author: petero
24
 */
25
 
26
#ifndef TRANSPOSITIONTABLE_HPP_
27
#define TRANSPOSITIONTABLE_HPP_
28
 
29
#include "util/util.hpp"
30
#include "move.hpp"
31
#include "constants.hpp"
32
#include "util/alignedAlloc.hpp"
33
 
34
#include <vector>
35
 
36
class Position;
37
 
38
/**
39
 * Implements the main transposition table using Cuckoo hashing.
40
 */
41
class TranspositionTable {
42
public:
43
    /** In-memory representation of TT entry. Uses std::atomic for thread safety,
44
     * but accessed using memory_order_relaxed for maximum performance. */
45
    struct TTEntryStorage {
46
        std::atomic<U64> key;
47
        std::atomic<U64> data;
48
        TTEntryStorage();
49
        TTEntryStorage(const TTEntryStorage& a);
50
    };
51
    static_assert(sizeof(TTEntryStorage) == 16, "TTEntryStorage size wrong");
52
 
53
    /** A local copy of a transposition table entry. */
54
    class TTEntry {
55
    public:
56
        /** Set type to T_EMPTY. */
57
        void clear();
58
 
59
        /** Store in transposition table, encoded for thread safety. */
60
        void store(TTEntryStorage& ent);
61
 
62
        /** Load from transposition table, decode the thread safety encoding. */
63
        void load(const TTEntryStorage& ent);
64
 
65
        /** Return true if this object is more valuable than the other, false otherwise. */
66
        bool betterThan(const TTEntry& other, int currGen) const;
67
 
68
        U64 getKey() const;
69
        void setKey(U64 k);
70
 
71
        void getMove(Move& m) const;
72
 
73
        void setMove(const Move& m);
74
 
75
        /** Get the score from the hash entry and convert from "mate in x" to "mate at ply". */
76
        int getScore(int ply) const;
77
 
78
        /** Convert score from "mate at ply" to "mate in x" and store in hash entry. */
79
        void setScore(int score, int ply);
80
 
81
        /** Return true if entry is good enough to cut off this branch in the search tree. */
82
        bool isCutOff(int alpha, int beta, int ply, int depth) const;
83
 
84
        int getDepth() const;
85
        void setDepth(int d);
86
        int getGeneration() const;
87
        void setGeneration(int g);
88
        int getType() const;
89
        void setType(int t);
90
        int getEvalScore() const;
91
        void setEvalScore(int s);
92
 
93
    private:
94
        U64 key;        //  0 64 key         Zobrist hash key
95
        U64 data;       //  0 16 move        from + (to<<6) + (promote<<12)
96
                        // 16 16 score       Score from search
97
                        // 32 10 depth       Search depth
98
                        // 42  4 generation  Increase when OTB position changes
99
                        // 46  2 type        exact score, lower bound, upper bound
100
                        // 48 16 evalScore   Score from static evaluation
101
 
102
        void setBits(int first, int size, unsigned int value);
103
        unsigned int getBits(int first, int size) const;
104
    };
105
 
106
    /** Constructor. Creates an empty transposition table with numEntries slots. */
107
    TranspositionTable(int log2Size);
108
 
109
    void reSize(int log2Size);
110
 
111
    /** Insert an entry in the hash table. */
112
    void insert(U64 key, const Move& sm, int type, int ply, int depth, int evalScore);
113
 
114
    /** Retrieve an entry from the hash table corresponding to position with zobrist key "key". */
115
    void probe(U64 key, TTEntry& result);
116
 
117
    /** Prefetch cache line. */
118
    void prefetch(U64 key);
119
 
120
    /**
121
     * Increase hash table generation. This means that subsequent inserts will be considered
122
     * more valuable than the entries currently present in the hash table.
123
     */
124
    void nextGeneration();
125
 
126
    /** Clear the transposition table. */
127
    void clear();
128
 
129
    /** Extract a list of PV moves, starting from "rootPos" and first move "mFirst". */
130
    void extractPVMoves(const Position& rootPos, const Move& mFirst, std::vector<Move>& pv);
131
 
132
    /** Extract the PV starting from posIn, using hash entries, both exact scores and bounds. */
133
    std::string extractPV(const Position& posIn);
134
 
135
    /** Print hash table statistics. */
136
    void printStats() const;
137
 
138
private:
139
    /** Get position in hash table given zobrist key. */
140
    size_t getIndex(U64 key) const;
141
 
142
    /** Get part of zobrist key to store in hash table. */
143
    static U64 getStoredKey(U64 key);
144
 
145
 
146
    vector_aligned<TTEntryStorage> table;
147
    U8 generation;
148
};
149
 
150
 
151
inline
152
TranspositionTable::TTEntryStorage::TTEntryStorage() {
153
    key.store(0, std::memory_order_relaxed);
154
    data.store(0, std::memory_order_relaxed);
155
}
156
 
157
inline
158
TranspositionTable::TTEntryStorage::TTEntryStorage(const TTEntryStorage& a) {
159
    key.store(a.key.load(std::memory_order_relaxed), std::memory_order_relaxed);
160
    data.store(a.data.load(std::memory_order_relaxed), std::memory_order_relaxed);
161
}
162
 
163
 
164
inline void
165
TranspositionTable::TTEntry::clear() {
166
    key = 0;
167
    data = 0;
168
    static_assert(TType::T_EMPTY == 0, "type not set to T_EMPTY");
169
}
170
 
171
inline void
172
TranspositionTable::TTEntry::store(TTEntryStorage& ent) {
173
    ent.key.store(key ^ data, std::memory_order_relaxed);
174
    ent.data.store(data, std::memory_order_relaxed);
175
}
176
 
177
inline void
178
TranspositionTable::TTEntry::load(const TTEntryStorage& ent) {
179
    key = ent.key.load(std::memory_order_relaxed);
180
    data = ent.data.load(std::memory_order_relaxed);
181
    key ^= data;
182
}
183
 
184
inline bool
185
TranspositionTable::TTEntry::betterThan(const TTEntry& other, int currGen) const {
186
    if ((getGeneration() == currGen) != (other.getGeneration() == currGen))
187
        return getGeneration() == currGen;   // Old entries are less valuable
188
    if ((getType() == TType::T_EXACT) != (other.getType() == TType::T_EXACT))
189
        return getType() == TType::T_EXACT;         // Exact score more valuable than lower/upper bound
190
    if (getDepth() != other.getDepth())
191
        return getDepth() > other.getDepth();     // Larger depth is more valuable
192
    return false;   // Otherwise, pretty much equally valuable
193
}
194
 
195
inline U64
196
TranspositionTable::TTEntry::getKey() const {
197
    return key;
198
}
199
 
200
inline void
201
TranspositionTable::TTEntry::setKey(U64 k) {
202
    key = k;
203
}
204
 
205
inline void
206
TranspositionTable::TTEntry::getMove(Move& m) const {
207
    int move = getBits(0, 16);
208
    m.setMove(move & 63, (move >> 6) & 63, (move >> 12) & 15, m.score());
209
}
210
 
211
inline void
212
TranspositionTable::TTEntry::setMove(const Move& m) {
213
    int move = (short)(m.from() + (m.to() << 6) + (m.promoteTo() << 12));
214
    setBits(0, 16, move);
215
}
216
 
217
inline int
218
TranspositionTable::TTEntry::getScore(int ply) const {
219
    int sc = (S16)getBits(16, 16);
220
    if (SearchConst::isWinScore(sc))
221
        sc -= ply;
222
    else if (SearchConst::isLoseScore(sc))
223
        sc += ply;
224
    return sc;
225
}
226
 
227
inline void
228
TranspositionTable::TTEntry::setScore(int score, int ply) {
229
    if (SearchConst::isWinScore(score))
230
        score += ply;
231
    else if (SearchConst::isLoseScore(score))
232
        score -= ply;
233
    setBits(16, 16, score);
234
}
235
 
236
inline bool
237
TranspositionTable::TTEntry::isCutOff(int alpha, int beta, int ply, int depth) const {
238
    using namespace SearchConst;
239
    const int score = getScore(ply);
240
    const int plyToMate = MATE0 - std::abs(getScore(0));
241
    const int eDepth = getDepth();
242
    const int eType = getType();
243
    if ((eDepth >= depth) || (eDepth >= plyToMate*plyScale)) {
244
        if ( (eType == TType::T_EXACT) ||
245
            ((eType == TType::T_GE) && (score >= beta)) ||
246
            ((eType == TType::T_LE) && (score <= alpha)))
247
            return true;
248
    }
249
    return false;
250
}
251
 
252
inline int
253
TranspositionTable::TTEntry::getDepth() const {
254
    return getBits(32, 10);
255
}
256
 
257
inline void
258
TranspositionTable::TTEntry::setDepth(int d) {
259
    setBits(32, 10, d);
260
}
261
 
262
inline int
263
TranspositionTable::TTEntry::getGeneration() const {
264
    return getBits(42, 4);
265
}
266
 
267
inline void
268
TranspositionTable::TTEntry::setGeneration(int g) {
269
    setBits(42, 4, g);
270
}
271
 
272
inline int
273
TranspositionTable::TTEntry::getType() const {
274
    return getBits(46, 2);
275
}
276
 
277
inline void
278
TranspositionTable::TTEntry::setType(int t) {
279
    setBits(46, 2, t);
280
}
281
 
282
inline int
283
TranspositionTable::TTEntry::getEvalScore() const {
284
    return (S16)getBits(48, 16);
285
}
286
 
287
inline void
288
TranspositionTable::TTEntry::setEvalScore(int s) {
289
    setBits(48, 16, s);
290
}
291
 
292
inline void
293
TranspositionTable::TTEntry::setBits(int first, int size, unsigned int value) {
294
    U64 mask = ((1ULL << size) - 1) << first;
295
    data = (data & ~mask) | (((U64)value << first) & mask);
296
}
297
 
298
inline unsigned int
299
TranspositionTable::TTEntry::getBits(int first, int size) const {
300
    U64 sizeMask = ((1ULL << size) - 1);
301
    return (unsigned int)((data >> first) & sizeMask);
302
}
303
 
304
 
305
inline size_t
306
TranspositionTable::getIndex(U64 key) const {
307
    return (size_t)(key & (table.size() - 1));
308
}
309
 
310
inline U64
311
TranspositionTable::getStoredKey(U64 key) {
312
    return key;
313
}
314
 
315
inline TranspositionTable::TranspositionTable(int log2Size) {
316
    reSize(log2Size);
317
}
318
 
319
inline void
320
TranspositionTable::probe(U64 key, TTEntry& result) {
321
    size_t idx0 = getIndex(key);
322
    U64 key2 = getStoredKey(key);
323
    TTEntry ent;
324
    ent.load(table[idx0]);
325
    if (ent.getKey() == key2) {
326
        if (ent.getGeneration() != generation) {
327
            ent.setGeneration(generation);
328
            ent.store(table[idx0]);
329
        }
330
        result = ent;
331
        return;
332
    }
333
    size_t idx1 = idx0 ^ 1;
334
    ent.load(table[idx1]);
335
    if (ent.getKey() == key2) {
336
        if (ent.getGeneration() != generation) {
337
            ent.setGeneration(generation);
338
            ent.store(table[idx1]);
339
        }
340
        result = ent;
341
        return;
342
    }
343
    result.setType(TType::T_EMPTY);
344
}
345
 
346
inline void
347
TranspositionTable::prefetch(U64 key) {
348
#ifdef HAS_PREFETCH
349
    size_t idx0 = getIndex(key);
350
    __builtin_prefetch(&table[idx0]);
351
#endif
352
}
353
 
354
inline void
355
TranspositionTable::nextGeneration() {
356
    generation = (generation + 1) & 15;
357
}
358
 
359
inline void
360
TranspositionTable::clear() {
361
    TTEntry ent;
362
    ent.clear();
363
    for (size_t i = 0; i < table.size(); i++)
364
        ent.store(table[i]);
365
}
366
 
367
#endif /* TRANSPOSITIONTABLE_HPP_ */