00001 //---------------------------------------------------------------------------- 00002 /** @file SgUctSearch.h 00003 Class SgUctSearch and helper classes. */ 00004 //---------------------------------------------------------------------------- 00005 00006 #ifndef SG_UCTSEARCH_H 00007 #define SG_UCTSEARCH_H 00008 00009 #include <fstream> 00010 #include <vector> 00011 #include <boost/scoped_array.hpp> 00012 #include <boost/shared_ptr.hpp> 00013 #include <boost/thread/barrier.hpp> 00014 #include <boost/thread/condition.hpp> 00015 #include <boost/thread/mutex.hpp> 00016 #include <boost/thread/recursive_mutex.hpp> 00017 #include <boost/thread/thread.hpp> 00018 #include "SgBlackWhite.h" 00019 #include "SgBWArray.h" 00020 #include "SgTimer.h" 00021 #include "SgUctTree.h" 00022 #include "SgMpiSynchronizer.h" 00023 00024 #define SG_UCTFASTLOG 1 00025 #if SG_UCTFASTLOG 00026 #include "SgFastLog.h" 00027 #endif 00028 00029 //---------------------------------------------------------------------------- 00030 00031 /** @defgroup sguctgroup Monte Carlo tree search 00032 Game-independent Monte Carlo tree search using UCT. 00033 00034 The main class SgUctSearch keeps a tree with statistics for each node 00035 visited more than a certain number of times, and then continues with 00036 random playout (not necessarily uniform random). 00037 Within the tree, the move with the highest upper confidence bound is 00038 chosen according to the basic UCT formula: 00039 @f[ \bar{X}_j + c \sqrt{\frac{\log{n}}{T_j(n)}} @f] 00040 with: 00041 - @f$ j @f$ the move index 00042 - @f$ X_{j,\gamma} @f$ reward for move @f$ j @f$ at sample @f$ \gamma @f$ 00043 - @f$ n @f$ number of times the father node was visited 00044 - @f$ T_j(n) @f$ number of times the move has been played 00045 - @f$ c @f$ an appropriate constant 00046 00047 References: 00048 - Kocsis, Szepesvari: 00049 <a href="http://zaphod.aml.sztaki.hu/papers/ecml06.pdf"> 00050 Bandit based Monte-Carlo Planning</a> 00051 - Auer, Cesa-Bianchi, Fischer: 00052 <a href="http://homes.dsi.unimi.it/~cesabian/Pubblicazioni/ml-02.pdf"> 00053 Finite-time Analysis of the Multiarmed Bandit Problem</a> 00054 - Gelly, Wang, Munos, Teytaud: 00055 <a href="http://hal.inria.fr/docs/00/12/15/16/PDF/RR-6062.pdf"> 00056 Modification of UCT with patterns in Monte-Carlo Go</a> 00057 - Silver, Gelly: 00058 <a href= 00059 "http://imls.engr.oregonstate.edu/www/htdocs/proceedings/icml2007/papers/387.pdf"> 00060 Combining Online and Offline Knowledge in UCT</a> 00061 00062 @see 00063 - @ref sguctsearchlockfree 00064 - @ref sguctsearchweights 00065 - @ref sguctsearchproven */ 00066 00067 /** @page sguctsearchlockfree Lock-free mode in SgUctSearch 00068 00069 The basic idea of the lock-free mode in SgUctSearch is to share a tree between 00070 multiple threads without using any locks. Because of specific requirements on 00071 the memory model of the hardware platform, lock-free mode is an optional 00072 feature of the SgUctSearch and needs to be enabled explicitly. 00073 00074 @section sguctsearchlockfreetree Modifying the Tree Structure 00075 00076 The first change to make the lock-free search work is in the handling of 00077 concurrent changes to the structure of the tree. SgUctSearch never deletes 00078 nodes during a search; new nodes are created in a pre-allocated memory array. 00079 In the lock-free algorithm, each thread has its own memory array for creating 00080 new nodes. Only after the nodes are fully created and initialized, are they 00081 linked to the parent node. This can cause some memory overhead, because if 00082 several threads expand the same node only the children created by the last 00083 thread will be used in future simulations. It can also happen that some of the 00084 children that are lost already received value updates; these updates will be 00085 lost. 00086 00087 The child information of a node consists of two variables: a pointer to the 00088 first child in the array, and the number of children. To avoid that another 00089 thread sees an inconsistent state of these variables, all threads assume that 00090 the number of children is valid if the pointer to the first child is not null. 00091 Linking a parent to a new set of children requires first writing the number of 00092 children, then the pointer to the first child. The compiler is prevented from 00093 reordering the writes by declaring these variables as volatile. 00094 00095 @section sguctsearchlockfreevalues Updating Values 00096 00097 The move and RAVE values are stored in the nodes as counts and mean values. 00098 The mean values are updated using an incremental algorithm. Updating them 00099 without protection by a mutex can cause updates of the mean to be lost with or 00100 without increment of the count, as well as updates of the mean occurring 00101 without increment of the count. It could also happen that one thread reads the 00102 count and mean while they are written by another thread, and the first thread 00103 sees an erroneous state that exists only temporarily. In practice, these 00104 faulty updates occur with a low probability and will have only a small effect 00105 on the counts and mean values. They are intentionally ignored. 00106 00107 The only problematic case is if a count is zero, because the mean value is 00108 undefined if the count is zero, and this case has a special meaning at several 00109 places in the search. For example, the computation of the values for the 00110 selection of children in the in-tree phase distinguishes three cases: if the 00111 move count and RAVE count is non-zero, the value will be computed as a 00112 weighted linear combination of both mean values, if the move count is zero, 00113 only the RAVE mean is used, and if both counts are zero, a configurable 00114 constant value, the first play urgency, is used. To avoid this problem, all 00115 threads assume that a mean value is only valid if the corresponding count is 00116 non-zero. Updating a value requires first writing the new mean value, then the 00117 new count. The compiler is prevented from reordering the writes by declaring 00118 the counts and mean values as volatile. 00119 00120 @section sguctsearchlockfreeplatform Platform Requirements 00121 00122 There are some requirements on the memory model of the platform to make the 00123 lock-free search algorithm work. Writes of certain basic types (size_t, int, 00124 float, pointers) must be atomic. Writes by one thread must be seen by other 00125 threads in the same order. The IA-32 and Intel-64 CPU architectures, which are 00126 used in most modern standard computers, guarantee these assumptions. They also 00127 synchronize CPU caches after writes. (See 00128 <a href="http://download.intel.com/design/processor/manuals/253668.pdf"> 00129 Intel 64 and IA-32 Architectures Software Developer's Manual</a>, chapter 00130 7.1 Locked Atomic Operations and 7.2 Memory Ordering). */ 00131 00132 /** @page sguctsearchweights Estimator weights in SgUctSearch 00133 The weights of the estimators (move value, RAVE value) are chosen by 00134 assuming that the estimators are uncorrelated and modeling the mean 00135 squared error of estimator @f$ i @f$ by a function that depends on the 00136 number of samples and parameter constants, which represent the variance 00137 and bias of the estimator and need to be determined experimentally: 00138 00139 @f[ 00140 w_i = \frac{1}{Z} \frac{1}{{\rm MSE}_i} 00141 \qquad Z = \sum_i \frac{1}{{\rm MSE}_i} 00142 \qquad {\rm MSE}_i = \frac{c_{\rm variance}}{N_i} + c_{\rm bias}^2 00143 @f] 00144 00145 Note that this formula is nearly equivalent to the formula suggested 00146 by David Silver on the Computer Go mailing list for the case of two 00147 estimators (move value and RAVE value) and used in newer versions of MoGo. 00148 However, MoGo uses the measured variance of the current RAVE value (for 00149 both the move weight and RAVE weight) instead variance parameter 00150 constants. 00151 00152 The formula is then reformulated to use different constants that describe 00153 the initial steepness and final asymptotic value of the unnormalized 00154 weight: 00155 00156 @f[ 00157 Z w_i = 00158 \frac{c_{\rm initial}} 00159 {\frac{1}{N} + \frac{c_{\rm initial}}{c_{\rm final}}} 00160 @f] 00161 00162 with: 00163 - @f$ N @f$ sample count of the estimator 00164 - @f$ c_{\rm initial} @f$ Initial weight parameter; this is they weight if 00165 @f$ N = 1 @f$ and @f$ c_{\rm final} \gg c_{\rm initial} @f$ 00166 - @f$ c_{\rm final} @f$ Final weight parameter; this is they weight if 00167 @f$ N \rightarrow \infty @f$ 00168 00169 For the move value, @f$ c_{\rm bias} = 0 @f$, and the variance can become 00170 part of the normalization constant, so the weight is simply 00171 @f$ N_{\rm move} @f$. 00172 If no estimator has a sample count yet, the first-play-urgency parameter 00173 is used for the value estimate. */ 00174 00175 /** @page sguctsearchproven Proven Nodes in SgUctSearch 00176 00177 When expanding a node (or computing knowledge at a node) it is 00178 possible the derived class can prove that the state corresponding 00179 to the node is a proven win or loss. Note this differs from a 00180 terminal node since a terminal node by definition has no children, 00181 whereas a proven node can have any number of children. 00182 00183 Any in-tree phase that encounters a proven node is immediately 00184 terminated and the result passed back up the tree just as if a 00185 playout (with the value corresponding to the proven value) had 00186 been performed. 00187 00188 The existence of proven nodes in a SgUctTree does not change the 00189 move selection in any way. For example: a node with both winning 00190 and losing children will continue to select children without 00191 regard to their proven value. This means losing children will not 00192 be avoided and winning children will not be chosen except as a 00193 result of the playouts recorded as those children are visisted 00194 (the values of which would now be perfectly accurate). 00195 00196 Proven values are not currently passed up the tree like in a 00197 min-max search. It would be possible to pass back winning moves, 00198 that is, mark the parent of winning moves as winning, but it is 00199 not possible mark a node as losing if all its moves are 00200 losing. This is because it is possible progressive widening (or 00201 other heuristics) are being used by the derived state and in such 00202 a case it may not be true that all the current children being 00203 losses implies the parent state is a loss. */ 00204 00205 //---------------------------------------------------------------------------- 00206 00207 /** Game result, sequence and nodes of one Monte-Carlo game in SgUctSearch. 00208 @ingroup sguctgroup */ 00209 struct SgUctGameInfo 00210 { 00211 /** The game result of the playout(s). 00212 The result is from the view of the player at the root. */ 00213 std::vector<SgUctValue> m_eval; 00214 00215 /** The sequence of the in-tree phase. */ 00216 std::vector<SgMove> m_inTreeSequence; 00217 00218 /** The sequence of the playout(s). 00219 For convenient usage, they also include the moves from 00220 m_inTreeSequence, even if they are the same for each playout. */ 00221 std::vector<std::vector<SgMove> > m_sequence; 00222 00223 /** Was the playout aborted due to maxGameLength (stored for each 00224 playout). */ 00225 std::vector<bool> m_aborted; 00226 00227 /** Nodes visited in the in-tree phase. */ 00228 std::vector<const SgUctNode*> m_nodes; 00229 00230 /** Flag to skip RAVE update for moves of the playout(s). 00231 For convenient usage, the index corresponds to the move number from 00232 the root position on, even if the flag is currently only used for 00233 moves in the playout phase, so the flag is false for all moves in the 00234 in-tree phase. */ 00235 std::vector<std::vector<bool> > m_skipRaveUpdate; 00236 00237 void Clear(std::size_t numberPlayouts); 00238 }; 00239 00240 //---------------------------------------------------------------------------- 00241 00242 /** Move selection strategy after search is finished. 00243 @ingroup sguctgroup */ 00244 enum SgUctMoveSelect 00245 { 00246 /** Select move with highest mean value. */ 00247 SG_UCTMOVESELECT_VALUE, 00248 00249 /** Select move with highest count. */ 00250 SG_UCTMOVESELECT_COUNT, 00251 00252 /** Use UCT bound (or combined bound if RAVE is enabled). */ 00253 SG_UCTMOVESELECT_BOUND, 00254 00255 /** Use the weighted sum of UCT and RAVE value (but without 00256 bias terms). */ 00257 SG_UCTMOVESELECT_ESTIMATE 00258 }; 00259 00260 //---------------------------------------------------------------------------- 00261 00262 /** Base class for the thread state. 00263 Subclasses must be thread-safe, it must be possible to use different 00264 instances of this class in different threads (after construction, the 00265 constructor does not need to be thread safe). Beware not to use classes 00266 that are not thread-safe, because they use global variables 00267 (e.g. SgRandom::Global()) 00268 @note Technically it is possible to use a non-thread safe implementation 00269 of subclasses, as long as the search is run with only one thread. 00270 @ingroup sguctgroup */ 00271 class SgUctThreadState 00272 { 00273 public: 00274 /** Number of the thread between 0 and SgUctSearch::NumberThreads() - 1 */ 00275 const unsigned int m_threadId; 00276 00277 bool m_isSearchInitialized; 00278 00279 /** Flag indicating the a node could not be expanded, because the 00280 maximum tree size was reached. */ 00281 bool m_isTreeOutOfMem; 00282 00283 SgUctGameInfo m_gameInfo; 00284 00285 /** Local variable for SgUctSearch::UpdateRaveValues(). 00286 Reused for efficiency. Stores the first time a move was played 00287 by the color to play at the root position (move is used as an index, 00288 do m_moveRange must be > 0); numeric_limits<size_t>::max(), if the 00289 move was not played. */ 00290 boost::scoped_array<std::size_t> m_firstPlay; 00291 00292 /** Local variable for SgUctSearch::UpdateRaveValues(). 00293 Like m_firstPlayToPlay, but for opponent color. */ 00294 boost::scoped_array<std::size_t> m_firstPlayOpp; 00295 00296 /** Local variable for SgUctSearch::PlayInTree(). 00297 Reused for efficiency. */ 00298 std::vector<SgUctMoveInfo> m_moves; 00299 00300 /** Local variable for SgUctSearch::CheckCountAbort(). 00301 Reused for efficiency. */ 00302 std::vector<SgMove> m_excludeMoves; 00303 00304 /** Thread's counter for Randomized Rave in SgUctSearch::SelectChild(). */ 00305 int m_randomizeCounter; 00306 00307 SgUctThreadState(unsigned int threadId, int moveRange = 0); 00308 00309 virtual ~SgUctThreadState(); 00310 00311 /** @name Pure virtual functions */ 00312 // @{ 00313 00314 /** Evaluate end-of-game position. 00315 Will only be called if GenerateAllMoves() or GeneratePlayoutMove() 00316 returns no moves. Should return larger values if position is better 00317 for the player to move. */ 00318 virtual SgUctValue Evaluate() = 0; 00319 00320 /** Execute a move. 00321 @param move The move */ 00322 virtual void Execute(SgMove move) = 0; 00323 00324 /** Execute a move in the playout phase. 00325 For optimization if the subclass uses uses a different game state 00326 representation in the playout phase. Otherwise the function can be 00327 implemented in the subclass by simply calling Execute(). 00328 @param move The move */ 00329 virtual void ExecutePlayout(SgMove move) = 0; 00330 00331 /** Generate moves. 00332 Moves will be explored in the order of the returned list. 00333 If return is true, trees under children will be deleted. 00334 @param count Number of times node has been visited. For knowledge- 00335 based computations. 00336 @param[out] moves The generated moves or empty list at end of game 00337 @param[out] provenType */ 00338 virtual bool GenerateAllMoves(SgUctValue count, 00339 std::vector<SgUctMoveInfo>& moves, 00340 SgUctProvenType& provenType) = 0; 00341 00342 /** Generate random move. 00343 Generate a random move in the play-out phase (outside the UCT tree). 00344 @param[out] skipRaveUpdate This value should be set to true, if the 00345 move should be excluded from RAVE updates. Otherwise it can be 00346 ignored. 00347 @return The move or SG_NULLMOVE at the end of the game. */ 00348 virtual SgMove GeneratePlayoutMove(bool& skipRaveUpdate) = 0; 00349 00350 /** Start search. 00351 This function should do any necessary preparations for playing games 00352 in the thread, like initializing the thread's copy of the game state 00353 from the global game state. The function does not have to be 00354 thread-safe. */ 00355 virtual void StartSearch() = 0; 00356 00357 /** Take back moves played in the in-tree phase. */ 00358 virtual void TakeBackInTree(std::size_t nuMoves) = 0; 00359 00360 /** Take back moves played in the playout phase. 00361 The search engine does not assume that the moves are really taken back 00362 after this function is called. If the subclass implements the playout 00363 in s separate state, which is initialized in StartPlayout() and does 00364 not support undo, the implementation of this function can be left 00365 empty in the subclass. */ 00366 virtual void TakeBackPlayout(std::size_t nuMoves) = 0; 00367 00368 // @} // name 00369 00370 00371 /** @name Virtual functions */ 00372 // @{ 00373 00374 /** Function that will be called by PlayGame() before the game. 00375 Default implementation does nothing. */ 00376 virtual void GameStart(); 00377 00378 /** Function that will be called at the beginning of the playout phase. 00379 Will be called only once (not once per playout!). Can be used for 00380 example to save some state of the current position for more efficient 00381 implementation of TakeBackPlayout(). 00382 Default implementation does nothing. */ 00383 virtual void StartPlayouts(); 00384 00385 /** Function that will be called at the beginning of each playout. 00386 Default implementation does nothing. */ 00387 virtual void StartPlayout(); 00388 00389 /** Function that will be called after each playout. 00390 Default implementation does nothing. */ 00391 virtual void EndPlayout(); 00392 00393 // @} // name 00394 }; 00395 00396 //---------------------------------------------------------------------------- 00397 00398 class SgUctSearch; 00399 00400 /** Create game specific thread state. 00401 @see SgUctThreadState 00402 @ingroup sguctgroup */ 00403 class SgUctThreadStateFactory 00404 { 00405 public: 00406 virtual ~SgUctThreadStateFactory(); 00407 00408 virtual SgUctThreadState* Create(unsigned int threadId, 00409 const SgUctSearch& search) = 0; 00410 }; 00411 00412 //---------------------------------------------------------------------------- 00413 00414 /** Statistics of the last search performed by SgUctSearch. */ 00415 struct SgUctSearchStat 00416 { 00417 double m_time; 00418 00419 /** Number of nodes for which the knowledge threshold was exceeded. */ 00420 SgUctValue m_knowledge; 00421 00422 /** Games per second. 00423 Useful values only if search time is higher than resolution of 00424 SgTime::Get(). */ 00425 double m_gamesPerSecond; 00426 00427 SgStatisticsExt<SgUctValue,SgUctValue> m_gameLength; 00428 00429 SgStatisticsExt<SgUctValue,SgUctValue> m_movesInTree; 00430 00431 SgUctStatistics m_aborted; 00432 00433 void Clear(); 00434 00435 void Write(std::ostream& out) const; 00436 }; 00437 00438 //---------------------------------------------------------------------------- 00439 00440 /** Optional parameters to SgUctSearch::Search() to allow early aborts. 00441 If early abort is used, the search will be aborted after a fraction of the 00442 resources (max time, max nodes) are spent, if the value is a clear win 00443 (above a threshold). */ 00444 struct SgUctEarlyAbortParam 00445 { 00446 /** The threshold to define what a clear win is. */ 00447 SgUctValue m_threshold; 00448 00449 /** The minimum number of games to allow an early abort. 00450 For a very low number of simulations, the value can be very 00451 unreliable. */ 00452 SgUctValue m_minGames; 00453 00454 /** The inverse fraction of the total resources (max time, max nodes), 00455 after which the early abort check is performed. */ 00456 SgUctValue m_reductionFactor; 00457 }; 00458 00459 //---------------------------------------------------------------------------- 00460 00461 /** Monte Carlo tree search using UCT. 00462 The evaluation function is assumed to be in <code>[0..1]</code> and 00463 inverted with <code>1 - eval</code>. 00464 @ingroup sguctgroup */ 00465 class SgUctSearch 00466 { 00467 public: 00468 static SgUctValue InverseEval(SgUctValue eval); 00469 00470 static SgUctValue InverseEstimate(SgUctValue eval); 00471 00472 /** Constructor. 00473 @param threadStateFactory The tread state factory (takes ownership). 00474 Can be null and set later (before using the search) with 00475 SetThreadStateFactory to allow multi-step construction. 00476 @param moveRange Upper integer limit (exclusive) used for move 00477 representation. Certain enhancements of SgUctSearch (e.g. Rave()) 00478 need to store data in arrays using the move as an index for 00479 efficient implementation. If the game does not use a small integer 00480 range for its move representation, this parameter should be 0. 00481 Then, enhancements that require a small move range cannot be enabled. */ 00482 SgUctSearch(SgUctThreadStateFactory* threadStateFactory, 00483 int moveRange = 0); 00484 00485 virtual ~SgUctSearch(); 00486 00487 void SetThreadStateFactory(SgUctThreadStateFactory* factory); 00488 00489 /** @name Pure virtual functions */ 00490 // @{ 00491 00492 /** Convert move to string (game dependent). 00493 This function needs to be thread-safe. */ 00494 virtual std::string MoveString(SgMove move) const = 0; 00495 00496 /** Evaluation value to use if evaluation is unknown. 00497 This value will be used, if games are aborted, because they exceed 00498 the maximum game length. 00499 This function needs to be thread-safe. */ 00500 virtual SgUctValue UnknownEval() const = 0; 00501 00502 // @} // name 00503 00504 00505 /** @name Virtual functions */ 00506 // @{ 00507 00508 /** Hook function that will be called by Search() after each game. 00509 Default implementation does nothing. 00510 This function does not need to be thread-safe. 00511 @param gameNumber The number of this iteration. 00512 @param threadId 00513 @param info The game info of the thread which played this iteration. 00514 @warning If LockFree() is enabled, this function will be called from 00515 multiple threads without locking. The subclass should handle this 00516 case appropriately by using its own lock or disabling functionality 00517 that will not work without locking. */ 00518 virtual void OnSearchIteration(SgUctValue gameNumber, 00519 unsigned int threadId, 00520 const SgUctGameInfo& info); 00521 00522 /** Hook function that will be called by StartSearch(). 00523 Default implementation calls m_mpiSynchronizer.StartSearch(). 00524 This function does not need to be thread-safe. */ 00525 virtual void OnStartSearch(); 00526 00527 /** Hook function that will be called when search 00528 completes. Default implementation calls 00529 m_mpiSynchronizer.EndSearch(). This function 00530 does not need to be thread-safe. */ 00531 virtual void OnEndSearch(); 00532 00533 virtual void OnThreadStartSearch(SgUctThreadState& state); 00534 00535 virtual void OnThreadEndSearch(SgUctThreadState& state); 00536 00537 virtual SgUctValue GamesPlayed() const; 00538 00539 // @} // name 00540 00541 00542 /** @name Search functions */ 00543 // @{ 00544 00545 /** Get a list of all generated moves. 00546 Sets up thread state 0 for a seach and calls GenerateAllMoves 00547 of the thread state. */ 00548 void GenerateAllMoves(std::vector<SgUctMoveInfo>& moves); 00549 00550 /** Play a single game. 00551 Plays a single game using the thread state of the first thread. 00552 Call StartSearch() before calling this function. */ 00553 void PlayGame(); 00554 00555 /** Start search. 00556 Initializes search for current position and clears statistics. 00557 @param rootFilter Moves to filter at the root node 00558 @param initTree The tree to initialize the search with. 0 for no 00559 initialization. The trees are actually swapped, not copied. */ 00560 void StartSearch(const std::vector<SgMove>& rootFilter 00561 = std::vector<SgMove>(), 00562 SgUctTree* initTree = 0); 00563 00564 void EndSearch(); 00565 00566 /** Calls StartSearch() and then PlayGame() in a loop. 00567 @param maxGames The maximum number of games (greater or equal 00568 one). The number of games includes the ones already counted in 00569 the initialization tree (see parameter initTree). 00570 @param maxTime The maximum time in seconds. 00571 @param[out] sequence The move sequence with the best value. 00572 @param rootFilter Moves to filter at the root node 00573 @param initTree The tree to initialize the search with. 0 for no 00574 initialization. The trees are actually swapped, not copied. 00575 @param earlyAbort See SgUctEarlyAbortParam. Null means not to do an 00576 early abort. 00577 @return The value of the root position. */ 00578 SgUctValue Search(SgUctValue maxGames, double maxTime, 00579 std::vector<SgMove>& sequence, 00580 const std::vector<SgMove>& rootFilter 00581 = std::vector<SgMove>(), 00582 SgUctTree* initTree = 0, 00583 SgUctEarlyAbortParam* earlyAbort = 0); 00584 00585 /** Do a one-ply Monte-Carlo search instead of the UCT search. 00586 @param maxGames 00587 @param maxTime 00588 @param[out] value The value of the best move */ 00589 SgPoint SearchOnePly(SgUctValue maxGames, double maxTime, 00590 SgUctValue& value); 00591 00592 /** Find child node with best move. 00593 @param node The father node. 00594 @param excludeMoves Optional list of moves to ignore in the children 00595 nodes. 00596 @return The best child or 0 if no child nodes exists. */ 00597 const SgUctNode* 00598 FindBestChild(const SgUctNode& node, 00599 const std::vector<SgMove>* excludeMoves = 0) const; 00600 00601 /** Extract sequence of best moves from root node. 00602 @param[out] sequence The resulting sequence. */ 00603 void FindBestSequence(std::vector<SgMove>& sequence) const; 00604 00605 /** Return the bound of a move. 00606 This is the bound that was used for move selection. It can be the 00607 pure UCT bound or the combined bound if RAVE is used. 00608 @param useRave Whether rave should be used or not. 00609 @param node The node corresponding to the position 00610 @param child The node corresponding to the move */ 00611 SgUctValue GetBound(bool useRave, const SgUctNode& node, 00612 const SgUctNode& child) const; 00613 00614 // @} // name 00615 00616 00617 /** @name Search data */ 00618 // @{ 00619 00620 /** Info for last game. 00621 Returns the last game info of the first thread. 00622 This function is not thread-safe. */ 00623 const SgUctGameInfo& LastGameInfo() const; 00624 00625 /** One-line summary text for last game. 00626 Contains: move, count and mean for all nodes; result 00627 Returns the last game info of the first thread. 00628 This function is not thread-safe. */ 00629 std::string LastGameSummaryLine() const; 00630 00631 /** See parameter earlyAbort in Search() */ 00632 bool WasEarlyAbort() const; 00633 00634 const SgUctTree& Tree() const; 00635 00636 /** Get temporary tree. 00637 Returns a tree that is compatible in size and number of allocators 00638 to the tree of the search. This tree is used by the search itself as 00639 a temporary tree for certain operations during the search, but can be 00640 used by other code while the search is not running. */ 00641 SgUctTree& GetTempTree(); 00642 00643 // @} // name 00644 00645 00646 /** @name Parameters */ 00647 // @{ 00648 00649 /** Constant c in the bias term. 00650 This constant corresponds to 2 C_p in the original UCT paper. 00651 The default value is 0.7, which works well in 9x9 Go. */ 00652 float BiasTermConstant() const; 00653 00654 /** See BiasTermConstant() */ 00655 void SetBiasTermConstant(float biasTermConstant); 00656 00657 /** Points at which to recompute children. 00658 Specifies the number of visits at which GenerateAllMoves() is 00659 called again at the node. This is to allow multiple knowledge 00660 computations to occur as a node becomes more 00661 important. Returned move info will be merged with info in the 00662 tree. This is can be used prune, add children, give a bonus to 00663 a move, etc. */ 00664 std::vector<SgUctValue> KnowledgeThreshold() const; 00665 00666 /** See KnowledgeThreshold() */ 00667 void SetKnowledgeThreshold(const std::vector<SgUctValue>& counts); 00668 00669 /** Maximum number of nodes in the tree. 00670 @note The search owns two trees, one of which is used as a temporary 00671 tree for some operations (see GetTempTree()). This functions sets 00672 the maximum number of nodes per tree. */ 00673 std::size_t MaxNodes() const; 00674 00675 /** See MaxNodes() 00676 @param maxNodes Maximum number of nodes (>= 1) */ 00677 void SetMaxNodes(std::size_t maxNodes); 00678 00679 /** The number of threads to use during the search. */ 00680 unsigned int NumberThreads() const; 00681 00682 /** See SetNumberThreads() */ 00683 void SetNumberThreads(unsigned int n); 00684 00685 /** Lock-free usage of multi-threaded search. 00686 @ref sguctsearchlockfree */ 00687 bool LockFree() const; 00688 00689 /** See LockFree() */ 00690 void SetLockFree(bool enable); 00691 00692 /** See SetRandomizeRaveFrequency() */ 00693 int RandomizeRaveFrequency() const; 00694 00695 /** Randomly turns off rave with given frequency - 00696 once in every frequency child selections. 00697 Helps in rare cases where rave ignores the best move. 00698 Set frequency to 0 to switch it off. */ 00699 void SetRandomizeRaveFrequency(int frequency); 00700 00701 /** Don't update RAVE value if opponent played the same move first. 00702 Default is false (since it depends on the game and move 00703 representation, if it should be used). */ 00704 bool RaveCheckSame() const; 00705 00706 /** See RaveCheckSame() */ 00707 void SetRaveCheckSame(bool enable); 00708 00709 /** First play urgency. 00710 The value for unexplored moves. According to UCT they should 00711 always be preferred to moves, that have been played at least once. 00712 According to the MoGo tech report, it can be useful to use smaller 00713 values (as low as 1) to encorouge early exploitation. 00714 Default value is 10000. 00715 @see @ref sguctsearchweights */ 00716 SgUctValue FirstPlayUrgency() const; 00717 00718 /** See FirstPlayUrgency() */ 00719 void SetFirstPlayUrgency(SgUctValue firstPlayUrgency); 00720 00721 /** Log one-line summary for each game during Search() to a file. 00722 @todo File name still is hard-coded to "uctsearch.log" */ 00723 bool LogGames() const; 00724 00725 /** See LogGames() */ 00726 void SetLogGames(bool enable); 00727 00728 /** Maximum game length. 00729 If the number of moves in a game exceed this length, it will be 00730 counted as a loss. 00731 The default is @c numeric_limits<size_t>::max() */ 00732 std::size_t MaxGameLength() const; 00733 00734 /** See MaxGameLength() */ 00735 void SetMaxGameLength(std::size_t maxGameLength); 00736 00737 /** Required number of simulations to expand a node in the tree. 00738 The default is 2, which means a node will be expanded on the second 00739 visit. */ 00740 SgUctValue ExpandThreshold() const; 00741 00742 /** See ExpandThreshold() */ 00743 void SetExpandThreshold(SgUctValue expandThreshold); 00744 00745 /** The number of playouts per simulated game. 00746 Useful for multi-threading to increase the workload of the threads. 00747 Default is 1. */ 00748 std::size_t NumberPlayouts() const; 00749 00750 void SetNumberPlayouts(std::size_t n); 00751 00752 /** Use the RAVE algorithm (Rapid Action Value Estimation). 00753 See Gelly, Silver 2007 in the references in the class description. 00754 In difference to the original description of the RAVE algorithm, 00755 no "RAVE bias term" is used. The estimated value of a move is the 00756 weighted mean of the move value and the RAVE value and then a 00757 single UCT-like bias term is added. 00758 @see RaveWeightFunc */ 00759 bool Rave() const; 00760 00761 /** See Rave() */ 00762 void SetRave(bool enable); 00763 00764 /** See SgUctMoveSelect */ 00765 SgUctMoveSelect MoveSelect() const; 00766 00767 /** See SgUctMoveSelect */ 00768 void SetMoveSelect(SgUctMoveSelect moveSelect); 00769 00770 /** See @ref sguctsearchweights. */ 00771 float RaveWeightInitial() const; 00772 00773 /** See @ref sguctsearchweights. */ 00774 void SetRaveWeightInitial(float value); 00775 00776 /** See @ref sguctsearchweights. */ 00777 float RaveWeightFinal() const; 00778 00779 /** See @ref sguctsearchweights. */ 00780 void SetRaveWeightFinal(float value); 00781 00782 /** Weight RAVE updates. 00783 Gives more weight to moves that are closer to the position for which 00784 the RAVE statistics are stored. The weighting function is linearly 00785 decreasing from 2 to 0 with the move number from the position for 00786 which the RAVE statistics are stored to the end of the simulated game. */ 00787 bool WeightRaveUpdates() const; 00788 00789 /** See WeightRaveUpdates() */ 00790 void SetWeightRaveUpdates(bool enable); 00791 00792 /** Whether search uses virtual loss. */ 00793 bool VirtualLoss() const; 00794 00795 /** See VirtualLoss() */ 00796 void SetVirtualLoss(bool enable); 00797 00798 /** Prune nodes with low counts if tree is full. 00799 This will prune nodes below a minimum count, if the tree gets full 00800 during a search. The minimum count is PruneMinCount() at the beginning 00801 of the search and is doubled every time a pruning operation does not 00802 reduce the tree by at least a factor of 2. The creation of the pruned 00803 tree uses the temporary tree (see GetTempTree()). */ 00804 bool PruneFullTree() const; 00805 00806 /** See PruneFullTree() */ 00807 void SetPruneFullTree(bool enable); 00808 00809 /** See PruneFullTree() */ 00810 SgUctValue PruneMinCount() const; 00811 00812 /** See PruneFullTree() */ 00813 void SetPruneMinCount(SgUctValue n); 00814 00815 /** Terminate the search if the counts can no longer be represented 00816 precisely by SgUctValue. 00817 Default is true. */ 00818 bool CheckFloatPrecision() const; 00819 00820 /** See CheckFloatPrecision() */ 00821 void SetCheckFloatPrecision(bool enable); 00822 00823 void SetMpiSynchronizer(const SgMpiSynchronizerHandle &synchronizerHandle); 00824 00825 SgMpiSynchronizerHandle MpiSynchronizer(); 00826 00827 const SgMpiSynchronizerHandle MpiSynchronizer() const; 00828 00829 // @} // name 00830 00831 00832 /** @name Statistics */ 00833 // @{ 00834 00835 const SgUctSearchStat& Statistics() const; 00836 00837 void WriteStatistics(std::ostream& out) const; 00838 00839 // @} // name 00840 00841 /** Get state of one of the threads. 00842 Requires: ThreadsCreated() */ 00843 SgUctThreadState& ThreadState(int i) const; 00844 00845 /** Check if threads are already created. 00846 The threads are created at the beginning of the first search 00847 (to allow multi-step construction with setting the policy after 00848 the constructor call). */ 00849 bool ThreadsCreated() const; 00850 00851 /** Create threads. 00852 The threads are created at the beginning of the first search 00853 (to allow multi-step construction with setting the policy after 00854 the constructor call). This function needs to be called explicitely 00855 only, if a thread state is going to be used before the first search. */ 00856 void CreateThreads(); 00857 00858 private: 00859 typedef boost::recursive_mutex::scoped_lock GlobalLock; 00860 00861 friend class Thread; 00862 00863 class Thread 00864 { 00865 public: 00866 std::auto_ptr<SgUctThreadState> m_state; 00867 00868 Thread(SgUctSearch& search, std::auto_ptr<SgUctThreadState> state); 00869 00870 ~Thread(); 00871 00872 void StartPlay(); 00873 00874 void WaitPlayFinished(); 00875 00876 private: 00877 /** Copyable function object that invokes Thread::operator(). 00878 Needed because the the constructor of boost::thread copies the 00879 function object argument. */ 00880 class Function 00881 { 00882 public: 00883 Function(Thread& thread); 00884 00885 void operator()(); 00886 00887 private: 00888 Thread& m_thread; 00889 }; 00890 00891 friend class Thread::Function; 00892 00893 SgUctSearch& m_search; 00894 00895 bool m_quit; 00896 00897 boost::barrier m_threadReady; 00898 00899 boost::mutex m_startPlayMutex; 00900 00901 boost::mutex m_playFinishedMutex; 00902 00903 boost::condition m_startPlay; 00904 00905 boost::condition m_playFinished; 00906 00907 boost::mutex::scoped_lock m_playFinishedLock; 00908 00909 GlobalLock m_globalLock; 00910 00911 /** The thread. 00912 Order dependency: must be constructed as the last member, because 00913 the constructor starts the thread. */ 00914 boost::thread m_thread; 00915 00916 void operator()(); 00917 00918 void PlayGames(); 00919 }; 00920 00921 std::auto_ptr<SgUctThreadStateFactory> m_threadStateFactory; 00922 00923 /** See LogGames() */ 00924 bool m_logGames; 00925 00926 /** See Rave() */ 00927 bool m_rave; 00928 00929 /** See KnowledgeThreshold() */ 00930 std::vector<SgUctValue> m_knowledgeThreshold; 00931 00932 /** Flag indicating that the search was terminated because the maximum 00933 time or number of games was reached. */ 00934 volatile bool m_aborted; 00935 00936 volatile bool m_isTreeOutOfMemory; 00937 00938 std::auto_ptr<boost::barrier> m_searchLoopFinished; 00939 00940 /** See SgUctEarlyAbortParam. */ 00941 bool m_wasEarlyAbort; 00942 00943 /** See SgUctEarlyAbortParam. 00944 The auto pointer is empty, if no early abort is used. */ 00945 std::auto_ptr<SgUctEarlyAbortParam> m_earlyAbort; 00946 00947 /** See SgUctMoveSelect */ 00948 SgUctMoveSelect m_moveSelect; 00949 00950 /** See RaveCheckSame() */ 00951 bool m_raveCheckSame; 00952 00953 /** See SetRandomizeRaveFrequency() */ 00954 int m_randomizeRaveFrequency; 00955 00956 /** See LockFree() */ 00957 bool m_lockFree; 00958 00959 /** See WeightRaveUpdates() */ 00960 bool m_weightRaveUpdates; 00961 00962 /** See PruneFullTree() */ 00963 bool m_pruneFullTree; 00964 00965 /** See CheckFloatPrecision() */ 00966 bool m_checkFloatPrecision; 00967 00968 /** See NumberThreads() */ 00969 unsigned int m_numberThreads; 00970 00971 /** See NumberPlayouts() */ 00972 std::size_t m_numberPlayouts; 00973 00974 /** See MaxNodes() */ 00975 std::size_t m_maxNodes; 00976 00977 /** See PruneMinCount() */ 00978 SgUctValue m_pruneMinCount; 00979 00980 /** See parameter moveRange in constructor */ 00981 const int m_moveRange; 00982 00983 /** See MaxGameLength() */ 00984 std::size_t m_maxGameLength; 00985 00986 /** See ExpandThreshold() */ 00987 SgUctValue m_expandThreshold; 00988 00989 /** Number of games limit for the current search. */ 00990 SgUctValue m_maxGames; 00991 00992 /** Number of games played in the current search. */ 00993 SgUctValue m_numberGames; 00994 00995 SgUctValue m_startRootMoveCount; 00996 00997 /** Interval in number of games in which to check time abort. 00998 Avoids that the potentially expensive SgTime::Get() is called after 00999 every game. The interval is updated dynamically according to the 01000 current games/sec, such that it is called ten times per second 01001 (if the total search time is at least one second, otherwise ten times 01002 per total maximum search time) */ 01003 SgUctValue m_checkTimeInterval; 01004 01005 volatile SgUctValue m_nextCheckTime; 01006 01007 double m_lastScoreDisplayTime; 01008 01009 /** See BiasTermConstant() */ 01010 float m_biasTermConstant; 01011 01012 /** See FirstPlayUrgency() */ 01013 SgUctValue m_firstPlayUrgency; 01014 01015 /** See @ref sguctsearchweights. */ 01016 float m_raveWeightInitial; 01017 01018 /** See @ref sguctsearchweights. */ 01019 float m_raveWeightFinal; 01020 01021 /** 1 / m_raveWeightInitial precomputed for efficiency */ 01022 SgUctValue m_raveWeightParam1; 01023 01024 /** m_raveWeightInitial / m_raveWeightFinal precomputed for efficiency */ 01025 SgUctValue m_raveWeightParam2; 01026 01027 /** Time limit for current search. */ 01028 double m_maxTime; 01029 01030 /** See VirtualLoss() */ 01031 bool m_virtualLoss; 01032 01033 std::string m_logFileName; 01034 01035 SgTimer m_timer; 01036 01037 SgUctTree m_tree; 01038 01039 /** See GetTempTree() */ 01040 SgUctTree m_tempTree; 01041 01042 /** See parameter rootFilter in function Search() */ 01043 std::vector<SgMove> m_rootFilter; 01044 01045 std::ofstream m_log; 01046 01047 /** Mutex for protecting global variables during multi-threading. 01048 Currently, only the play-out phase of games is thread safe, therefore 01049 this lock is always locked elsewhere (in-tree phase, updating of 01050 values and statistics, etc.) */ 01051 boost::recursive_mutex m_globalMutex; 01052 01053 SgUctSearchStat m_statistics; 01054 01055 /** List of threads. 01056 The elements are owned by the vector (shared_ptr is only used because 01057 auto_ptr should not be used with standard containers) */ 01058 std::vector<boost::shared_ptr<Thread> > m_threads; 01059 01060 #if SG_UCTFASTLOG 01061 SgFastLog m_fastLog; 01062 #endif 01063 01064 boost::shared_ptr<SgMpiSynchronizer> m_mpiSynchronizer; 01065 01066 01067 void ApplyRootFilter(std::vector<SgUctMoveInfo>& moves); 01068 01069 void PropagateProvenStatus(const vector<const SgUctNode*>& nodes); 01070 01071 bool CheckAbortSearch(SgUctThreadState& state); 01072 01073 bool CheckEarlyAbort() const; 01074 01075 bool CheckCountAbort(SgUctThreadState& state, 01076 SgUctValue remainingGames) const; 01077 01078 void Debug(const SgUctThreadState& state, const std::string& textLine); 01079 01080 void DeleteThreads(); 01081 01082 void ExpandNode(SgUctThreadState& state, const SgUctNode& node); 01083 01084 void CreateChildren(SgUctThreadState& state, const SgUctNode& node, 01085 bool deleteChildTrees); 01086 01087 SgUctValue GetBound(bool useRave, SgUctValue logPosCount, 01088 const SgUctNode& child) const; 01089 01090 SgUctValue GetValueEstimate(bool useRave, const SgUctNode& child) const; 01091 01092 SgUctValue GetValueEstimateRave(const SgUctNode& child) const; 01093 01094 SgUctValue Log(SgUctValue x) const; 01095 01096 bool NeedToComputeKnowledge(const SgUctNode* current); 01097 01098 void PlayGame(SgUctThreadState& state, GlobalLock* lock); 01099 01100 bool PlayInTree(SgUctThreadState& state, bool& isTerminal); 01101 01102 bool PlayoutGame(SgUctThreadState& state, std::size_t playout); 01103 01104 void PrintSearchProgress(double currTime) const; 01105 01106 void SearchLoop(SgUctThreadState& state, GlobalLock* lock); 01107 01108 const SgUctNode& SelectChild(int& randomizeCounter, const SgUctNode& node); 01109 01110 std::string SummaryLine(const SgUctGameInfo& info) const; 01111 01112 void UpdateCheckTimeInterval(double time); 01113 01114 void UpdateDynRaveBias(); 01115 01116 void UpdateRaveValues(SgUctThreadState& state); 01117 01118 void UpdateRaveValues(SgUctThreadState& state, std::size_t playout); 01119 01120 void UpdateRaveValues(SgUctThreadState& state, std::size_t playout, 01121 SgUctValue eval, std::size_t i, 01122 const std::size_t firstPlay[], 01123 const std::size_t firstPlayOpp[]); 01124 01125 void UpdateStatistics(const SgUctGameInfo& info); 01126 01127 void UpdateTree(const SgUctGameInfo& info); 01128 }; 01129 01130 inline float SgUctSearch::BiasTermConstant() const 01131 { 01132 return m_biasTermConstant; 01133 } 01134 01135 inline bool SgUctSearch::CheckFloatPrecision() const 01136 { 01137 return m_checkFloatPrecision; 01138 } 01139 01140 inline SgUctValue SgUctSearch::ExpandThreshold() const 01141 { 01142 return m_expandThreshold; 01143 } 01144 01145 inline SgUctValue SgUctSearch::FirstPlayUrgency() const 01146 { 01147 return m_firstPlayUrgency; 01148 } 01149 01150 inline SgUctValue SgUctSearch::InverseEval(SgUctValue eval) 01151 { 01152 return (1 - eval); 01153 } 01154 01155 inline SgUctValue SgUctSearch::InverseEstimate(SgUctValue eval) 01156 { 01157 return (1 - eval); 01158 } 01159 01160 inline bool SgUctSearch::LockFree() const 01161 { 01162 return m_lockFree; 01163 } 01164 01165 inline const SgUctGameInfo& SgUctSearch::LastGameInfo() const 01166 { 01167 return ThreadState(0).m_gameInfo; 01168 } 01169 01170 inline bool SgUctSearch::LogGames() const 01171 { 01172 return m_logGames; 01173 } 01174 01175 inline std::size_t SgUctSearch::MaxGameLength() const 01176 { 01177 return m_maxGameLength; 01178 } 01179 01180 inline std::size_t SgUctSearch::MaxNodes() const 01181 { 01182 return m_maxNodes; 01183 } 01184 01185 inline SgUctMoveSelect SgUctSearch::MoveSelect() const 01186 { 01187 return m_moveSelect; 01188 } 01189 01190 inline unsigned int SgUctSearch::NumberThreads() const 01191 { 01192 return m_numberThreads; 01193 } 01194 01195 inline std::size_t SgUctSearch::NumberPlayouts() const 01196 { 01197 return m_numberPlayouts; 01198 } 01199 01200 inline void SgUctSearch::PlayGame() 01201 { 01202 PlayGame(ThreadState(0), 0); 01203 } 01204 01205 inline bool SgUctSearch::PruneFullTree() const 01206 { 01207 return m_pruneFullTree; 01208 } 01209 01210 inline SgUctValue SgUctSearch::PruneMinCount() const 01211 { 01212 return m_pruneMinCount; 01213 } 01214 01215 inline bool SgUctSearch::Rave() const 01216 { 01217 return m_rave; 01218 } 01219 01220 inline bool SgUctSearch::RaveCheckSame() const 01221 { 01222 return m_raveCheckSame; 01223 } 01224 01225 inline float SgUctSearch::RaveWeightInitial() const 01226 { 01227 return m_raveWeightInitial; 01228 } 01229 01230 inline float SgUctSearch::RaveWeightFinal() const 01231 { 01232 return m_raveWeightFinal; 01233 } 01234 01235 inline void SgUctSearch::SetBiasTermConstant(float biasTermConstant) 01236 { 01237 m_biasTermConstant = biasTermConstant; 01238 } 01239 01240 inline void SgUctSearch::SetCheckFloatPrecision(bool enable) 01241 { 01242 m_checkFloatPrecision = enable; 01243 } 01244 01245 inline void SgUctSearch::SetExpandThreshold(SgUctValue expandThreshold) 01246 { 01247 SG_ASSERT(expandThreshold >= 0); 01248 m_expandThreshold = expandThreshold; 01249 } 01250 01251 inline void SgUctSearch::SetFirstPlayUrgency(SgUctValue firstPlayUrgency) 01252 { 01253 m_firstPlayUrgency = firstPlayUrgency; 01254 } 01255 01256 inline void SgUctSearch::SetLockFree(bool enable) 01257 { 01258 m_lockFree = enable; 01259 } 01260 01261 inline void SgUctSearch::SetLogGames(bool enable) 01262 { 01263 m_logGames = enable; 01264 } 01265 01266 inline void SgUctSearch::SetMaxGameLength(std::size_t maxGameLength) 01267 { 01268 m_maxGameLength = maxGameLength; 01269 } 01270 01271 inline void SgUctSearch::SetMaxNodes(std::size_t maxNodes) 01272 { 01273 m_maxNodes = maxNodes; 01274 if (m_threads.size() > 0) // Threads already created 01275 m_tree.SetMaxNodes(m_maxNodes); 01276 } 01277 01278 inline void SgUctSearch::SetMoveSelect(SgUctMoveSelect moveSelect) 01279 { 01280 m_moveSelect = moveSelect; 01281 } 01282 01283 inline std::vector<SgUctValue> SgUctSearch::KnowledgeThreshold() const 01284 { 01285 return m_knowledgeThreshold; 01286 } 01287 01288 inline void 01289 SgUctSearch::SetKnowledgeThreshold(const std::vector<SgUctValue>& t) 01290 { 01291 m_knowledgeThreshold = t; 01292 } 01293 01294 inline void SgUctSearch::SetNumberPlayouts(std::size_t n) 01295 { 01296 SG_ASSERT(n >= 1); 01297 m_numberPlayouts = n; 01298 } 01299 01300 inline void SgUctSearch::SetPruneFullTree(bool enable) 01301 { 01302 m_pruneFullTree = enable; 01303 } 01304 01305 inline void SgUctSearch::SetPruneMinCount(SgUctValue n) 01306 { 01307 m_pruneMinCount = n; 01308 } 01309 01310 inline void SgUctSearch::SetMpiSynchronizer(const SgMpiSynchronizerHandle 01311 &synchronizerHandle) 01312 { 01313 m_mpiSynchronizer = SgMpiSynchronizerHandle(synchronizerHandle); 01314 } 01315 01316 inline SgMpiSynchronizerHandle SgUctSearch::MpiSynchronizer() 01317 { 01318 return SgMpiSynchronizerHandle(m_mpiSynchronizer); 01319 } 01320 01321 inline const SgMpiSynchronizerHandle SgUctSearch::MpiSynchronizer() const 01322 { 01323 return SgMpiSynchronizerHandle(m_mpiSynchronizer); 01324 } 01325 01326 inline int SgUctSearch::RandomizeRaveFrequency() const 01327 { 01328 return m_randomizeRaveFrequency; 01329 } 01330 01331 inline void SgUctSearch::SetRandomizeRaveFrequency(int frequency) 01332 { 01333 m_randomizeRaveFrequency = frequency; 01334 } 01335 01336 inline void SgUctSearch::SetRaveCheckSame(bool enable) 01337 { 01338 m_raveCheckSame = enable; 01339 } 01340 01341 inline void SgUctSearch::SetRaveWeightFinal(float value) 01342 { 01343 m_raveWeightFinal = value; 01344 } 01345 01346 inline void SgUctSearch::SetRaveWeightInitial(float value) 01347 { 01348 m_raveWeightInitial = value; 01349 } 01350 01351 inline void SgUctSearch::SetWeightRaveUpdates(bool enable) 01352 { 01353 m_weightRaveUpdates = enable; 01354 } 01355 01356 inline bool SgUctSearch::VirtualLoss() const 01357 { 01358 return m_virtualLoss; 01359 } 01360 01361 inline void SgUctSearch::SetVirtualLoss(bool enable) 01362 { 01363 m_virtualLoss = enable; 01364 } 01365 01366 inline const SgUctSearchStat& SgUctSearch::Statistics() const 01367 { 01368 return m_statistics; 01369 } 01370 01371 inline bool SgUctSearch::ThreadsCreated() const 01372 { 01373 return (m_threads.size() > 0); 01374 } 01375 01376 inline SgUctThreadState& SgUctSearch::ThreadState(int i) const 01377 { 01378 SG_ASSERT(static_cast<std::size_t>(i) < m_threads.size()); 01379 return *m_threads[i]->m_state; 01380 } 01381 01382 inline const SgUctTree& SgUctSearch::Tree() const 01383 { 01384 return m_tree; 01385 } 01386 01387 inline bool SgUctSearch::WasEarlyAbort() const 01388 { 01389 return m_wasEarlyAbort; 01390 } 01391 01392 inline bool SgUctSearch::WeightRaveUpdates() const 01393 { 01394 return m_weightRaveUpdates; 01395 } 01396 01397 //---------------------------------------------------------------------------- 01398 01399 #endif // SG_UCTSEARCH_H