00001 //---------------------------------------------------------------------------- 00002 /** @file SgBookBuilder.h */ 00003 //---------------------------------------------------------------------------- 00004 00005 #ifndef SG_BOOKBUILDER_HPP 00006 #define SG_BOOKBUILDER_HPP 00007 00008 #include <cmath> 00009 #include <iostream> 00010 #include <iomanip> 00011 #include <vector> 00012 #include "SgMove.h" 00013 00014 //---------------------------------------------------------------------------- 00015 00016 /** @defgroup sgopeningbook Automatic Opening Book Construction 00017 Game independent Opening Book Construction. 00018 00019 Code is based on Thomas R. Lincke's paper "Strategies for the 00020 Automatic Construction of Opening Books" published in 2001. 00021 00022 We make the following adjustments: 00023 - Neither side is assumed to be the book player, so the expansion 00024 formula is identical for all nodes (see page 80 of the paper). In other 00025 words, both sides can play sub-optimal moves. 00026 - A single node for each state is stored, such that transpositions 00027 are not re-computed. Hence the book forms a DAG of states, not a tree. 00028 - Progressive widening is used on internal nodes to restrict the 00029 search initially. 00030 00031 We also think there is a typo with respect to the formula of epo_i on 00032 page 80. Namely, since p_i is the negamax of p_{s_j}s, then we should 00033 sum the values to find the distance from optimal, not subtract. That is, 00034 we use epo_i = 1 + min(s_j) (epb_{s_j} + alpha*(p_i + p_{s_j}) instead. */ 00035 00036 //---------------------------------------------------------------------------- 00037 00038 /** State in the Opening Book. 00039 @ingroup sgopeningbook */ 00040 class SgBookNode 00041 { 00042 public: 00043 //------------------------------------------------------------------------ 00044 00045 /** Priority of newly created leaves. */ 00046 static const float LEAF_PRIORITY; 00047 00048 //------------------------------------------------------------------------ 00049 00050 /** Heuristic value of this state. */ 00051 float m_heurValue; 00052 00053 /** Minmax value of this state. */ 00054 float m_value; 00055 00056 /** Expansion priority. */ 00057 float m_priority; 00058 00059 /** Number of times this node was explored. */ 00060 unsigned m_count; 00061 00062 //------------------------------------------------------------------------ 00063 00064 SgBookNode(); 00065 00066 /** Creates a leaf with the given heuristic value. */ 00067 SgBookNode(float heuristicValue); 00068 00069 /** Creates a node from data in string. Uses same format as 00070 ToString(). */ 00071 SgBookNode(const std::string& str); 00072 00073 /** Returns true if this node is a leaf in the opening book, ie, 00074 its count is zero. */ 00075 bool IsLeaf() const; 00076 00077 /** Returns true if propagated value is a win or a loss. */ 00078 bool IsTerminal() const; 00079 00080 /** Increment the node's counter. */ 00081 void IncrementCount(); 00082 00083 /** Outputs node in string form. */ 00084 std::string ToString() const; 00085 00086 private: 00087 void FromString(const std::string& str); 00088 }; 00089 00090 inline SgBookNode::SgBookNode() 00091 { 00092 } 00093 00094 inline SgBookNode::SgBookNode(float heuristicValue) 00095 : m_heurValue(heuristicValue), 00096 m_value(heuristicValue), 00097 m_priority(LEAF_PRIORITY), 00098 m_count(0) 00099 { 00100 } 00101 00102 inline SgBookNode::SgBookNode(const std::string& str) 00103 { 00104 FromString(str); 00105 } 00106 00107 inline void SgBookNode::IncrementCount() 00108 { 00109 m_count++; 00110 } 00111 00112 /** Extends standard stream output operator for SgBookNodes. */ 00113 inline std::ostream& operator<<(std::ostream& os, const SgBookNode& node) 00114 { 00115 os << node.ToString(); 00116 return os; 00117 } 00118 00119 //---------------------------------------------------------------------------- 00120 00121 /** @page bookrefresh Book Refresh 00122 @ingroup sgopeningbook 00123 00124 Due to transpositions, it is possible that a node's value changes, 00125 but because the node has not been revisited yet the information is 00126 not passed to its parent. Refreshing the book forces these 00127 propagations. 00128 00129 SgBookBuilder::Refresh() computes the correct propagation value for 00130 all internal nodes given the current set of leaf nodes. A node in 00131 which SgBookNode::IsLeaf() is true is treated as a leaf even 00132 if it has children in the book (ie, children from transpositions) */ 00133 00134 /** @page bookcover "Book Cover 00135 @ingroup sgopeningbook 00136 00137 The book cover operation ensures that a given set of lines is 00138 covered with the required number of expansions. When completed, 00139 each position in each line will have had at least the required 00140 number of expansions performed from it. 00141 00142 For each line, each position is processed in order. Expansions are 00143 performed until the required number are obtained (nothing is done 00144 if it already has enough). Then the next position in the line is 00145 processed. 00146 00147 If the additive flag is true, then the given number of expansions 00148 are added to the node no matter how many expansions it has already 00149 received. 00150 00151 A book refresh should be performed after this operation. */ 00152 00153 //---------------------------------------------------------------------------- 00154 00155 /** Base class for automated book building. 00156 @ingroup sgopeningbook */ 00157 class SgBookBuilder 00158 { 00159 public: 00160 SgBookBuilder(); 00161 00162 virtual ~SgBookBuilder(); 00163 00164 //--------------------------------------------------------------------- 00165 00166 /** Expands the book by expanding numExpansions leaves. */ 00167 void Expand(int numExpansions); 00168 00169 /** Ensures each node in each line has at least the given number 00170 of expansions. 00171 @ref bookcover. */ 00172 void Cover(int requiredExpansions, bool additive, 00173 const std::vector< std::vector<SgMove> >& lines); 00174 00175 /** Propagates leaf values up through the entire tree. 00176 @ref bookrefresh. */ 00177 void Refresh(); 00178 00179 /** Performs widening on all internal nodes that require it. Use 00180 this after increasing ExpandWidth() or decreasing 00181 ExpandThreshold() on an already existing book to update all 00182 the internal nodes with the new required width. Will do 00183 nothing unless parameters were changed accordingly. 00184 00185 Does not propagate values up tree, run Refresh() afterwards to 00186 do so. */ 00187 void IncreaseWidth(); 00188 00189 //--------------------------------------------------------------------- 00190 00191 /** The parameter alpha controls state expansion (big values give 00192 rise to deeper lines, while small values perform like a 00193 BFS). */ 00194 float Alpha() const; 00195 00196 /** See Alpha() */ 00197 void SetAlpha(float alpha); 00198 00199 /** Expand only the top ExpandWidth() children of a node 00200 initially, and after every ExpansionThreshold() visits add 00201 ExpandWidth() more children. */ 00202 bool UseWidening() const; 00203 00204 /** See UseWidening() */ 00205 void SetUseWidening(bool flag); 00206 00207 /** See UseWidening() */ 00208 std::size_t ExpandWidth() const; 00209 00210 /** See UseWidening() */ 00211 void SetExpandWidth(std::size_t width); 00212 00213 /** See UseWidening() */ 00214 std::size_t ExpandThreshold() const; 00215 00216 /** See UseWidening() */ 00217 void SetExpandThreshold(std::size_t threshold); 00218 00219 //--------------------------------------------------------------------- 00220 00221 /** Computes the expansion priority for the child using Alpha(), 00222 the value of the parent, and the provided values of child. */ 00223 float ComputePriority(const SgBookNode& parent, const float childValue, 00224 const float childPriority) const; 00225 00226 //--------------------------------------------------------------------- 00227 00228 /** Returns the evaluation from other player's perspective. */ 00229 virtual float InverseEval(float eval) const = 0; 00230 00231 /** Returns true if the eval is a loss. */ 00232 virtual bool IsLoss(float eval) const = 0; 00233 00234 /** Returns the value of the state according this node. 00235 Ie, takes into account swap moves, etc. */ 00236 virtual float Value(const SgBookNode& node) const = 0; 00237 00238 protected: 00239 /** See Alpha() */ 00240 float m_alpha; 00241 00242 /** See UseWidening() */ 00243 bool m_useWidening; 00244 00245 /** See UseWidening() */ 00246 std::size_t m_expandWidth; 00247 00248 /** See UseWidening() */ 00249 std::size_t m_expandThreshold; 00250 00251 /** Number of iterations after which the db is flushed to disk. */ 00252 std::size_t m_flushIterations; 00253 00254 //------------------------------------------------------------------------ 00255 00256 /** Converts move to a string (game dependent). */ 00257 virtual std::string MoveString(SgMove move) const = 0; 00258 00259 /** Print a message to a log/debug stream. */ 00260 virtual void PrintMessage(std::string msg) = 0; 00261 00262 /** Plays a move. */ 00263 virtual void PlayMove(SgMove move) = 0; 00264 00265 /** Undo last move. */ 00266 virtual void UndoMove(SgMove move) = 0; 00267 00268 /** Reads node. Returns false if node does not exist. */ 00269 virtual bool GetNode(SgBookNode& node) const = 0; 00270 00271 /** Writes node. */ 00272 virtual void WriteNode(const SgBookNode& node) = 0; 00273 00274 /** Save the book. */ 00275 virtual void FlushBook() = 0; 00276 00277 /** If current state does not exist, evaluate it and store in the 00278 book. */ 00279 virtual void EnsureRootExists() = 0; 00280 00281 /** Generates the set of moves to use in the book for this state. */ 00282 virtual bool GenerateMoves(std::vector<SgMove>& moves, float& value) = 0; 00283 00284 /** Returns all legal moves; should be a superset of those moves 00285 returned by GenerateMoves() */ 00286 virtual void GetAllLegalMoves(std::vector<SgMove>& moves) = 0; 00287 00288 /** Evaluate the children of the current state, return the values 00289 in a vector of pairs. */ 00290 virtual void EvaluateChildren(const std::vector<SgMove>& childrenToDo, 00291 std::vector<std::pair<SgMove, float> >& scores) = 0; 00292 00293 /** Hook function: called before any work is done. 00294 Default implementation does nothing. */ 00295 virtual void Init(); 00296 00297 /** Hook function: called after all work is complete. 00298 Default implementation does nothing. */ 00299 virtual void Fini(); 00300 00301 /** Hook function: called at start of iteration. 00302 Default implementation does nothing. */ 00303 virtual void StartIteration(); 00304 00305 /** Hook function: called at end of iteration. 00306 Default implementation does nothing. */ 00307 virtual void EndIteration(); 00308 00309 virtual void BeforeEvaluateChildren(); 00310 00311 virtual void AfterEvaluateChildren(); 00312 00313 virtual void ClearAllVisited() = 0; 00314 00315 virtual void MarkAsVisited() = 0; 00316 00317 virtual bool HasBeenVisited() = 0; 00318 00319 private: 00320 std::size_t m_numEvals; 00321 00322 std::size_t m_numWidenings; 00323 00324 std::size_t m_valueUpdates; 00325 00326 std::size_t m_priorityUpdates; 00327 00328 std::size_t m_internalNodes; 00329 00330 std::size_t m_leafNodes; 00331 00332 std::size_t m_terminalNodes; 00333 00334 //--------------------------------------------------------------------- 00335 00336 std::size_t NumChildren(const std::vector<SgMove>& legal); 00337 00338 void UpdateValue(SgBookNode& node, const std::vector<SgMove>& legal); 00339 00340 void UpdateValue(SgBookNode& node); 00341 00342 SgMove UpdatePriority(SgBookNode& node); 00343 00344 void DoExpansion(std::vector<SgMove>& pv); 00345 00346 bool Refresh(bool root); 00347 00348 void IncreaseWidth(bool root); 00349 00350 bool ExpandChildren(std::size_t count); 00351 }; 00352 00353 //---------------------------------------------------------------------------- 00354 00355 inline float SgBookBuilder::Alpha() const 00356 { 00357 return m_alpha; 00358 } 00359 00360 inline void SgBookBuilder::SetAlpha(float alpha) 00361 { 00362 m_alpha = alpha; 00363 } 00364 00365 inline bool SgBookBuilder::UseWidening() const 00366 { 00367 return m_useWidening; 00368 } 00369 00370 inline void SgBookBuilder::SetUseWidening(bool flag) 00371 { 00372 m_useWidening = flag; 00373 } 00374 00375 inline std::size_t SgBookBuilder::ExpandWidth() const 00376 { 00377 return m_expandWidth; 00378 } 00379 00380 inline void SgBookBuilder::SetExpandWidth(std::size_t width) 00381 { 00382 m_expandWidth = width; 00383 } 00384 00385 inline std::size_t SgBookBuilder::ExpandThreshold() const 00386 { 00387 return m_expandThreshold; 00388 } 00389 00390 inline void SgBookBuilder::SetExpandThreshold(std::size_t threshold) 00391 { 00392 m_expandThreshold = threshold; 00393 } 00394 00395 //---------------------------------------------------------------------------- 00396 00397 #endif // SG_BOOKBUILDER_HPP