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-2013  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
 * treeLogger.hpp
21
 *
22
 *  Created on: Feb 25, 2012
23
 *      Author: petero
24
 */
25
 
26
#ifndef TREELOGGER_HPP_
27
#define TREELOGGER_HPP_
28
 
29
#include "util/util.hpp"
30
#include "move.hpp"
31
#include "parallel.hpp"
32
 
33
#include <vector>
34
#include <type_traits>
35
#include <utility>
36
#include <cstring>
37
#include <fstream>
38
 
39
 
40
class TreeLoggerWriter;
41
class TreeLoggerWriterDummy;
42
 
43
/** Change to TreeLoggerWriter to enable tree logging. */
44
using TreeLogger = TreeLoggerWriterDummy;
45
 
46
 
47
class Position;
48
 
49
namespace Serializer {
50
    template <typename Type>
51
    typename std::enable_if<std::is_integral<Type>::value, U8*>::type
52
    putBytes(U8* buffer, Type value) {
53
        int s = sizeof(value);
54
        memcpy(buffer, &value, s);
55
        return buffer + s;
56
    }
57
 
58
    template <int N>
59
    static U8* serialize(U8* buffer) {
60
        static_assert(N >= 0, "Buffer too small");
61
        return buffer;
62
    }
63
 
64
    /** Store a sequence of values in a byte buffer. */
65
    template <int N, typename Type0, typename... Types>
66
    static U8* serialize(U8* buffer, Type0 value0, Types... values) {
67
        const int s = sizeof(Type0);
68
        buffer = putBytes(buffer, value0);
69
        return serialize<N-s>(buffer, values...);
70
    }
71
 
72
    template <typename Type>
73
    typename std::enable_if<std::is_integral<Type>::value, const U8*>::type
74
    getBytes(const U8* buffer, Type& value) {
75
        int s = sizeof(value);
76
        memcpy(&value, buffer, s);
77
        return buffer + s;
78
    }
79
 
80
    template <int N>
81
    static const U8* deSerialize(const U8* buffer) {
82
        static_assert(N >= 0, "Buffer too small");
83
        return buffer;
84
    }
85
 
86
    /** Retrieve a sequence of values from a byte buffer. */
87
    template <int N, typename Type0, typename... Types>
88
    static const U8* deSerialize(const U8* buffer, Type0& value0, Types&... values) {
89
        const int s = sizeof(Type0);
90
        buffer = getBytes(buffer, value0);
91
        return deSerialize<N-s>(buffer, values...);
92
    }
93
}
94
 
95
/** Base class for logging search trees to file. */
96
class TreeLoggerBase {
97
    friend class TreeLoggerTest;
98
protected:
99
    /**
100
     * Rules for the log file format
101
     * - A log file contains a search tree dump for one thread.
102
     * - The file contains information for 0 or more searches.
103
     * - The log file for thread 0 contains information for a single search.
104
     * - Information for one search tree starts with a Position0 record and
105
     *   ends at the next Position0 record or at the end of the file.
106
     * - A start entry may not have a corresponding end entry. This happens
107
     *   if the search was interrupted.
108
     * - Start and end entries are properly nested (assuming the end entries exist)
109
     *   s1.index < s2.index => e1.index > e2.index
110
     */
111
 
112
    enum class EntryType : U8 {
113
        POSITION_INCOMPLETE, // First entry in file has this type when endIndex
114
                             // has not yet been computed for all StartEntries.
115
        POSITION_PART0,      // Position entry, first part.
116
        POSITION_PART1,      // Position entry, second part.
117
        POSITION_PART2,      // Position entry, third part.
118
        NODE_START,          // Start of a search node.
119
        NODE_END             // End of a search node.
120
    };
121
 
122
    static const U32 endMark = 0xffffffff;
123
 
124
    struct Position0 {
125
        U32 nextIndex;     // Index of next position, or endMark for last position.
126
        U64 word0;
127
        U64 word1;
128
 
129
        template <int N> U8* serialize(U8 buffer[N]) const {
130
            return Serializer::serialize<N>(buffer, nextIndex, word0, word1);
131
        }
132
        template <int N> void deSerialize(const U8 buffer[N]) {
133
            Serializer::deSerialize<N>(buffer, nextIndex, word0, word1);
134
        }
135
    };
136
 
137
    struct Position1 {
138
        U32 t0Index;
139
        U64 word2;
140
        U64 word3;
141
 
142
        template <int N> U8* serialize(U8 buffer[N]) const {
143
            return Serializer::serialize<N>(buffer, t0Index, word2, word3);
144
        }
145
        template <int N> void deSerialize(const U8 buffer[N]) {
146
            Serializer::deSerialize<N>(buffer, t0Index, word2, word3);
147
        }
148
    };
149
 
150
    struct Position2 {
151
        U64 word4;
152
        U8 owningThread;
153
        U8 moveNo;
154
        U32 parentIndex;    // Index in owning threads tree log
155
 
156
        template <int N> U8* serialize(U8 buffer[N]) const {
157
            return Serializer::serialize<N>(buffer, word4, owningThread, moveNo, parentIndex);
158
        }
159
        template <int N> void deSerialize(const U8 buffer[N]) {
160
            Serializer::deSerialize<N>(buffer, word4, owningThread, moveNo, parentIndex);
161
        }
162
    };
163
 
164
    struct StartEntry {
165
        U32 endIndex;
166
        U32 parentIndex;    // Points to NODE_START or POSITION_PART0 node.
167
        U16 move;
168
        S16 alpha;
169
        S16 beta;
170
        U8 ply;
171
        U16 depth;
172
        U32 t0Index;        // Current entry in thread 0
173
 
174
        Move getMove() const {
175
            Move ret;
176
            ret.setMove(move & 63, (move >> 6) & 63, (move >> 12) & 15, 0);
177
            return ret;
178
        }
179
 
180
        template <int N> U8* serialize(U8 buffer[N]) const {
181
            return Serializer::serialize<N>(buffer, endIndex, parentIndex, move,
182
                                            alpha, beta, ply, depth, t0Index);
183
        }
184
        template <int N> void deSerialize(const U8 buffer[N]) {
185
            Serializer::deSerialize<N>(buffer, endIndex, parentIndex, move,
186
                                       alpha, beta, ply, depth, t0Index);
187
        }
188
    };
189
 
190
    struct EndEntry {
191
        U32 startIndex;
192
        S16 score;
193
        U8 scoreType;
194
        S16 evalScore;
195
        U64 hashKey;
196
        U32 t0Index;        // Current entry in thread 0
197
 
198
        template <int N> U8* serialize(U8 buffer[N]) const {
199
            return Serializer::serialize<N>(buffer, startIndex, score, scoreType,
200
                                            evalScore, hashKey, t0Index);
201
        }
202
        template <int N> void deSerialize(const U8 buffer[N]) {
203
            Serializer::deSerialize<N>(buffer, startIndex, score, scoreType,
204
                                       evalScore, hashKey, t0Index);
205
        }
206
    };
207
 
208
    struct Entry {
209
        EntryType type;
210
        union {
211
            Position0 p0;
212
            Position1 p1;
213
            Position2 p2;
214
            StartEntry se;
215
            EndEntry ee;
216
        };
217
 
218
        static const int bufSize = 22;
219
        using Buffer = U8[bufSize];
220
 
221
        void serialize(U8 buffer[bufSize]) const {
222
            U8* ptr = buffer;
223
            using UType = std::underlying_type<EntryType>::type;
224
            const int su = sizeof(UType);
225
            UType uType = static_cast<UType>(type);
226
            ptr = Serializer::serialize<bufSize>(ptr, uType);
227
            switch (type) {
228
            case EntryType::POSITION_INCOMPLETE: p0.serialize<bufSize-su>(ptr); break;
229
            case EntryType::POSITION_PART0:      p0.serialize<bufSize-su>(ptr); break;
230
            case EntryType::POSITION_PART1:      p1.serialize<bufSize-su>(ptr); break;
231
            case EntryType::POSITION_PART2:      p2.serialize<bufSize-su>(ptr); break;
232
            case EntryType::NODE_START:          se.serialize<bufSize-su>(ptr); break;
233
            case EntryType::NODE_END:            ee.serialize<bufSize-su>(ptr); break;
234
            }
235
        }
236
 
237
        void deSerialize(U8 buffer[bufSize]) {
238
            const U8* ptr = buffer;
239
            using UType = std::underlying_type<EntryType>::type;
240
            const int su = sizeof(UType);
241
            UType uType;
242
            ptr = Serializer::deSerialize<bufSize>(ptr, uType);
243
            type = static_cast<EntryType>(uType);
244
            switch (type) {
245
            case EntryType::POSITION_INCOMPLETE: p0.deSerialize<bufSize-su>(ptr); break;
246
            case EntryType::POSITION_PART0:      p0.deSerialize<bufSize-su>(ptr); break;
247
            case EntryType::POSITION_PART1:      p1.deSerialize<bufSize-su>(ptr); break;
248
            case EntryType::POSITION_PART2:      p2.deSerialize<bufSize-su>(ptr); break;
249
            case EntryType::NODE_START:          se.deSerialize<bufSize-su>(ptr); break;
250
            case EntryType::NODE_END:            ee.deSerialize<bufSize-su>(ptr); break;
251
            }
252
        }
253
    };
254
 
255
    Entry entry;
256
    U8 entryBuffer[Entry::bufSize];
257
};
258
 
259
/** Writer class for logging search trees to file. */
260
class TreeLoggerWriter : public TreeLoggerBase {
261
public:
262
    /** Constructor. */
263
    TreeLoggerWriter();
264
 
265
    /** Destructor. */
266
    ~TreeLoggerWriter();
267
 
268
    /** Open log file for writing. */
269
    void open(const std::string& filename, ParallelData& pd, int threadNo);
270
 
271
    /** Flush write cache and close log file. */
272
    void close();
273
 
274
    /** Return true if log file is opened. */
275
    bool isOpened() const;
276
 
277
    /** Log information for new position to search.
278
     * Return index of position entry. */
279
    U64 logPosition(const Position& pos, int owningThread, U64 parentIndex, int moveNo);
280
 
281
    /**
282
     * Log information when entering a search node.
283
     * @param parentId     Index of parent node.
284
     * @param m            Move made to go from parent node to this node
285
     * @param alpha        Search parameter
286
     * @param beta         Search parameter
287
     * @param ply          Search parameter
288
     * @param depth        Search parameter
289
     * @return node index
290
     */
291
    U64 logNodeStart(U64 parentIndex, const Move& m, int alpha, int beta, int ply, int depth);
292
 
293
    /**
294
     * Log information when leaving a search node.
295
     * @param startIndex Pointer to corresponding start node entry.
296
     * @param score      Computed score for this node.
297
     * @param scoreType  See TranspositionTable, T_EXACT, T_GE, T_LE.
298
     * @param evalScore  Score returned by evaluation function at this node, if known.
299
     * @return node index
300
     */
301
    U64 logNodeEnd(U64 startIndex, int score, int scoreType, int evalScore, U64 hashKey);
302
 
303
private:
304
    /** Write position entries to end of file. */
305
    void writePosition(const Position& pos, int owningThread, U64 parentIndex, int moveNo);
306
 
307
    /** Write entry to end of file. Uses internal buffering, flushed in close(). */
308
    void appendEntry(const Entry& entry);
309
 
310
 
311
    bool opened;
312
    std::ofstream os;
313
    U64 nextIndex;
314
 
315
    ParallelData* pd;
316
    int threadNo;
317
 
318
    static const int writeCacheSize = 1024;
319
    U8 writeCache[Entry::bufSize * writeCacheSize];
320
    int nInWriteCache;
321
};
322
 
323
/** Dummy version of TreeLoggerWriter. */
324
class TreeLoggerWriterDummy {
325
public:
326
    TreeLoggerWriterDummy() { }
327
    void open(const std::string& filename, ParallelData& pd, int threadNo) { }
328
    void close() { }
329
    bool isOpened() const { return false; }
330
    U64 logPosition(const Position& pos, int owningThread, U64 parentIndex, int moveNo) { return 0; }
331
    U64 logNodeStart(U64 parentIndex, const Move& m, int alpha, int beta, int ply, int depth) { return 0; }
332
    U64 logNodeEnd(U64 startIndex, int score, int scoreType, int evalScore, U64 hashKey) { return 0; }
333
};
334
 
335
/**
336
 * Reader/analysis class for a search tree dumped to a file.
337
 */
338
class TreeLoggerReader : public TreeLoggerBase {
339
public:
340
    /** Constructor. */
341
    TreeLoggerReader(const std::string& filename);
342
 
343
    void close();
344
 
345
    /** Main loop of the interactive tree browser. */
346
    static void main(const std::string& filename);
347
 
348
private:
349
    /** Compute endIndex for all StartNode entries. */
350
    void computeForwardPointers();
351
 
352
    /** Write forward pointer data to disk. */
353
    void flushForwardPointerData(std::vector<std::pair<U64,U64>>& toWrite);
354
 
355
    /** Get root node information. */
356
    void getRootNode(U64 index, Position& pos);
357
    void getRootNode(U64 index, Position& pos, int& owningThread,
358
                     U64& parentIndex, int& moveNo, U64& t0Index);
359
 
360
    /** Read an entry. */
361
    void readEntry(U64 index, Entry& entry);
362
 
363
    /** Write an entry. */
364
    void writeEntry(U64 index, const Entry& entry);
365
 
366
    /** Run the interactive analysis main loop. */
367
    void mainLoop();
368
 
369
    bool isMove(std::string cmdStr) const;
370
 
371
    /** Return all nodes with a given hash key. */
372
    void getNodesForHashKey(U64 hashKey, std::vector<U64>& nodes, U64 maxEntry);
373
 
374
    /** Get hash key from an input string. */
375
    U64 getHashKey(std::string& s, U64 defKey) const;
376
 
377
    /** Get integer parameter from an input string. */
378
    static int getArg(const std::string& s, int defVal);
379
 
380
    /** Get a list of integer parameters from an input string. */
381
    void getArgs(const std::string& s, int defVal, std::vector<int>& args);
382
 
383
    /** Get a string parameter from an input string. */
384
    static std::string getArgStr(const std::string& s, const std::string& defVal);
385
 
386
    void printHelp();
387
 
388
    /** Read start/end entries for a tree node. Return true if the end entry exists. */
389
    bool readEntries(int index, StartEntry& se, EndEntry& ee);
390
 
391
    /** Find the parent node to a node. */
392
    S64 findParent(S64 index);
393
 
394
    /** Find all children of a node. */
395
    void findChildren(S64 index, std::vector<U64>& childs);
396
 
397
    /** Get node position in parents children list. */
398
    int getChildNo(U64 index);
399
 
400
    /** Get list of nodes from root position to a node. */
401
    void getNodeSequence(U64 index, std::vector<U64>& nodes);
402
 
403
    /** Find list of moves from root node to a node.
404
     * Return root node index. */
405
    U64 getMoveSequence(U64 index, std::vector<Move>& moves);
406
 
407
    /** Find the position corresponding to a node. */
408
    Position getPosition(U64 index);
409
 
410
    void printNodeInfo(U64 index, int childNo = -1, const std::string& filterMove = "");
411
 
412
 
413
    std::fstream fs;
414
    S64 filePos;        // Current file read position (seekg)
415
    U64 numEntries;
416
};
417
 
418
 
419
inline
420
TreeLoggerWriter::TreeLoggerWriter()
421
    : opened(false), nextIndex(0), pd(nullptr), threadNo(-1), nInWriteCache(0) {
422
}
423
 
424
inline
425
TreeLoggerWriter::~TreeLoggerWriter() {
426
    close();
427
}
428
 
429
inline bool
430
TreeLoggerWriter::isOpened() const {
431
    return opened;
432
}
433
 
434
inline U64
435
TreeLoggerWriter::logPosition(const Position& pos, int owningThread, U64 parentIndex, int moveNo) {
436
    U64 ret = nextIndex;
437
    if (threadNo == 0)
438
        pd->t0Index = (U32)ret;
439
    writePosition(pos, owningThread, parentIndex, moveNo);
440
    return ret;
441
}
442
 
443
inline U64
444
TreeLoggerWriter::logNodeStart(U64 parentIndex, const Move& m, int alpha, int beta, int ply, int depth) {
445
    if (!opened)
446
        return 0;
447
    if (threadNo == 0)
448
        pd->t0Index = (U32)nextIndex;
449
    entry.type = EntryType::NODE_START;
450
    entry.se.endIndex = -1;
451
    entry.se.parentIndex = (U32)parentIndex;
452
    entry.se.move = m.from() + (m.to() << 6) + (m.promoteTo() << 12);
453
    entry.se.alpha = alpha;
454
    entry.se.beta = beta;
455
    entry.se.ply = ply;
456
    entry.se.depth = depth;
457
    entry.se.t0Index = pd->t0Index;
458
    appendEntry(entry);
459
    return nextIndex++;
460
}
461
 
462
inline U64
463
TreeLoggerWriter::logNodeEnd(U64 startIndex, int score, int scoreType, int evalScore, U64 hashKey) {
464
    if (!opened)
465
        return 0;
466
    if (threadNo == 0)
467
        pd->t0Index = (U32)nextIndex;
468
    entry.type = EntryType::NODE_END;
469
    entry.ee.startIndex = (U32)startIndex;
470
    entry.ee.score = score;
471
    entry.ee.scoreType = scoreType;
472
    entry.ee.evalScore = evalScore;
473
    entry.ee.hashKey = hashKey;
474
    entry.ee.t0Index = pd->t0Index;
475
    appendEntry(entry);
476
    return nextIndex++;
477
}
478
 
479
inline void
480
TreeLoggerReader::getRootNode(U64 index, Position& pos) {
481
    int owningThread;
482
    U64 parentIndex;
483
    int moveNo;
484
    U64 t0Index;
485
    getRootNode(index, pos, owningThread, parentIndex, moveNo, t0Index);
486
}
487
 
488
#endif /* TREELOGGER_HPP_ */