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-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_ */
  489.