00001 //---------------------------------------------------------------------------- 00002 /** @file GoUctBookBuilder.h */ 00003 //---------------------------------------------------------------------------- 00004 00005 #ifndef GOBOOKBUILDER_H 00006 #define GOBOOKBUILDER_H 00007 00008 #include <cmath> 00009 #include <iostream> 00010 #include <fstream> 00011 #include <set> 00012 #include "SgBookBuilder.h" 00013 #include "SgThreadedWorker.h" 00014 #include "GoBoard.h" 00015 #include "GoAutoBook.h" 00016 #include "GoBoardSynchronizer.h" 00017 00018 //---------------------------------------------------------------------------- 00019 00020 /** Expands a Book using the given player to evaluate game positions. 00021 Supports multithreaded evaluation of children. 00022 @todo Copy settings from passed player to other players. */ 00023 template<class PLAYER> 00024 class GoUctBookBuilder : public SgBookBuilder 00025 { 00026 public: 00027 GoUctBookBuilder(const GoBoard& brd); 00028 00029 ~GoUctBookBuilder(); 00030 00031 //--------------------------------------------------------------------- 00032 00033 /** Sets the player to use. Settings are copied from this player 00034 to the players used for each thread. 00035 @todo Currently not used! Since it is not clear how to copy 00036 settings from one player to another nicely. */ 00037 void SetPlayer(PLAYER& player); 00038 00039 /** Sets the state to start work from. */ 00040 void SetState(GoAutoBook& book); 00041 00042 //--------------------------------------------------------------------- 00043 00044 /** Number of players to use during leaf expansion. Each player 00045 may use a multi-threaded search. Should speed up the expansion 00046 of leaf states by a factor of (very close to) NumThreads(). */ 00047 std::size_t NumThreads() const; 00048 00049 /** See NumThreads() */ 00050 void SetNumThreads(std::size_t num); 00051 00052 /** Number of games to play when evaluation a state. */ 00053 SgUctValue NumGamesPerEvaluation() const; 00054 00055 /** See NumGamesPerEvaluation(). */ 00056 void SetNumGamesPerEvaluation(SgUctValue num); 00057 00058 /** Number of games to play when sorting children. */ 00059 SgUctValue NumGamesPerSort() const; 00060 00061 /** See NumGamesForSort() */ 00062 void SetNumGamesPerSort(SgUctValue num); 00063 00064 //--------------------------------------------------------------------- 00065 00066 float InverseEval(float eval) const; 00067 00068 bool IsLoss(float eval) const; 00069 00070 float Value(const SgBookNode& node) const; 00071 00072 protected: 00073 std::string MoveString(SgMove move) const; 00074 00075 void PrintMessage(std::string msg); 00076 00077 void PlayMove(SgMove move); 00078 00079 void UndoMove(SgMove move); 00080 00081 bool GetNode(SgBookNode& node) const; 00082 00083 void WriteNode(const SgBookNode& node); 00084 00085 void FlushBook(); 00086 00087 void EnsureRootExists(); 00088 00089 bool GenerateMoves(std::vector<SgMove>& moves, float& value); 00090 00091 void GetAllLegalMoves(std::vector<SgMove>& moves); 00092 00093 void EvaluateChildren(const std::vector<SgMove>& childrenToDo, 00094 std::vector<std::pair<SgMove, float> >& scores); 00095 void Init(); 00096 00097 void StartIteration(); 00098 00099 void EndIteration(); 00100 00101 void BeforeEvaluateChildren(); 00102 00103 void AfterEvaluateChildren(); 00104 00105 void Fini(); 00106 00107 void ClearAllVisited(); 00108 00109 void MarkAsVisited(); 00110 00111 bool HasBeenVisited(); 00112 00113 private: 00114 /** Copyable worker. */ 00115 class Worker 00116 { 00117 public: 00118 Worker(std::size_t id, PLAYER& player); 00119 00120 float operator()(const SgMove& move); 00121 00122 private: 00123 std::size_t m_id; 00124 00125 PLAYER* m_player; 00126 }; 00127 00128 /** Book this builder is expanding */ 00129 GoAutoBook* m_book; 00130 00131 PLAYER* m_origPlayer; 00132 00133 GoAutoBookState m_state; 00134 00135 std::set<SgHashCode> m_visited; 00136 00137 /** See NumberThreads() */ 00138 std::size_t m_numThreads; 00139 00140 /** See NumGamesPerEvaluation. */ 00141 SgUctValue m_numGamesPerEvaluation; 00142 00143 /** See NumGamesForSort() */ 00144 SgUctValue m_numGamesPerSort; 00145 00146 std::size_t m_numEvals; 00147 00148 std::size_t m_numWidenings; 00149 00150 std::size_t m_valueUpdates; 00151 00152 std::size_t m_priorityUpdates; 00153 00154 std::size_t m_internalNodes; 00155 00156 std::size_t m_leafNodes; 00157 00158 std::size_t m_terminalNodes; 00159 00160 /** Players for each thread. */ 00161 std::vector<PLAYER*> m_players; 00162 00163 /** Workers for each thread. */ 00164 std::vector<Worker> m_workers; 00165 00166 SgThreadedWorker<SgMove,float,Worker>* m_threadedWorker; 00167 00168 void CreateWorkers(); 00169 00170 void DestroyWorkers(); 00171 }; 00172 00173 //---------------------------------------------------------------------------- 00174 00175 template<class PLAYER> 00176 inline std::size_t GoUctBookBuilder<PLAYER>::NumThreads() const 00177 { 00178 return m_numThreads; 00179 } 00180 00181 template<class PLAYER> 00182 inline void GoUctBookBuilder<PLAYER>::SetNumThreads(std::size_t num) 00183 { 00184 m_numThreads = num; 00185 } 00186 00187 template<class PLAYER> 00188 inline SgUctValue GoUctBookBuilder<PLAYER>::NumGamesPerEvaluation() const 00189 { 00190 return m_numGamesPerEvaluation; 00191 } 00192 00193 template<class PLAYER> 00194 inline void GoUctBookBuilder<PLAYER>::SetNumGamesPerEvaluation(SgUctValue num) 00195 { 00196 m_numGamesPerEvaluation = num; 00197 } 00198 00199 template<class PLAYER> 00200 inline SgUctValue GoUctBookBuilder<PLAYER>::NumGamesPerSort() const 00201 { 00202 return m_numGamesPerSort; 00203 } 00204 00205 template<class PLAYER> 00206 inline void GoUctBookBuilder<PLAYER>::SetNumGamesPerSort(SgUctValue num) 00207 { 00208 m_numGamesPerSort = num; 00209 } 00210 00211 //---------------------------------------------------------------------------- 00212 00213 template<class PLAYER> 00214 GoUctBookBuilder<PLAYER>::GoUctBookBuilder(const GoBoard& bd) 00215 : SgBookBuilder(), 00216 m_book(0), 00217 m_origPlayer(0), 00218 m_state(bd), 00219 m_numThreads(1), 00220 m_numGamesPerEvaluation(10000), 00221 m_numGamesPerSort(10000) 00222 { 00223 SetAlpha(30.0); 00224 SetExpandWidth(8); 00225 } 00226 00227 template<class PLAYER> 00228 GoUctBookBuilder<PLAYER>::~GoUctBookBuilder() 00229 { 00230 } 00231 00232 //---------------------------------------------------------------------------- 00233 00234 /** Copies the player and board and creates the threads. */ 00235 template<class PLAYER> 00236 void GoUctBookBuilder<PLAYER>::CreateWorkers() 00237 { 00238 PrintMessage("GoUctBookBuilder::CreateWorkers()\n"); 00239 for (std::size_t i = 0; i < m_numThreads; ++i) 00240 { 00241 PLAYER* newPlayer = new PLAYER(m_state.Board()); 00242 00243 // TODO: COPY SETTINGS SOMEHOW 00244 //newPlayer->CopySettingsFrom(m_origPlayer); 00245 00246 // Always search, don't use forced moves 00247 newPlayer->SetForcedOpeningMoves(false); 00248 // Ensure all games are played; ie, do not use early count abort. 00249 newPlayer->Search().SetMoveSelect(SG_UCTMOVESELECT_ESTIMATE); 00250 // Should be enough for a 100k search. Needs 10GB 8 threaded. 00251 newPlayer->Search().SetMaxNodes(8500000); 00252 newPlayer->SetWriteDebugOutput(false); 00253 00254 m_players.push_back(newPlayer); 00255 m_workers.push_back(Worker(i, *m_players[i])); 00256 } 00257 m_threadedWorker 00258 = new SgThreadedWorker<SgMove,float,Worker>(m_workers); 00259 } 00260 00261 /** Destroys copied players, boards, and threads. */ 00262 template<class PLAYER> 00263 void GoUctBookBuilder<PLAYER>::DestroyWorkers() 00264 { 00265 PrintMessage("GoUctBookBuilder::DestroyWorkers()\n"); 00266 for (std::size_t i = 0; i < m_numThreads; ++i) 00267 delete m_players[i]; 00268 delete m_threadedWorker; 00269 m_workers.clear(); 00270 m_players.clear(); 00271 } 00272 00273 template<class PLAYER> 00274 void GoUctBookBuilder<PLAYER>::Init() 00275 { 00276 CreateWorkers(); 00277 } 00278 00279 template<class PLAYER> 00280 void GoUctBookBuilder<PLAYER>::Fini() 00281 { 00282 DestroyWorkers(); 00283 } 00284 00285 //---------------------------------------------------------------------------- 00286 00287 template<class PLAYER> 00288 GoUctBookBuilder<PLAYER>::Worker::Worker(std::size_t id, PLAYER& player) 00289 00290 : m_id(id), 00291 m_player(&player) 00292 { 00293 } 00294 00295 template<class PLAYER> 00296 float GoUctBookBuilder<PLAYER>::Worker::operator()(const SgMove& move) 00297 { 00298 m_player->UpdateSubscriber(); 00299 if (move >= 0) 00300 m_player->Board().Play(move); 00301 m_player->GenMove(SgTimeRecord(true, 9999), m_player->Board().ToPlay()); 00302 GoUctSearch& search 00303 = dynamic_cast<GoUctSearch&>(m_player->Search()); 00304 float score = static_cast<float>(search.Tree().Root().Mean()); 00305 return score; 00306 } 00307 00308 //---------------------------------------------------------------------------- 00309 00310 template<class PLAYER> 00311 inline void GoUctBookBuilder<PLAYER>::SetPlayer(PLAYER& player) 00312 { 00313 m_origPlayer = &player; 00314 } 00315 00316 template<class PLAYER> 00317 inline void GoUctBookBuilder<PLAYER>::SetState(GoAutoBook& book) 00318 { 00319 m_book = &book; 00320 m_state.Synchronize(); 00321 } 00322 00323 template<class PLAYER> 00324 inline std::string GoUctBookBuilder<PLAYER>::MoveString(SgMove move) const 00325 { 00326 return SgPointUtil::PointToString(move); 00327 } 00328 00329 template<class PLAYER> 00330 void GoUctBookBuilder<PLAYER>::PrintMessage(std::string msg) 00331 { 00332 SgDebug() << msg; 00333 } 00334 00335 template<class PLAYER> 00336 inline float GoUctBookBuilder<PLAYER>::InverseEval(float eval) const 00337 { 00338 return 1.f - eval; 00339 } 00340 00341 template<class PLAYER> 00342 inline bool GoUctBookBuilder<PLAYER>::IsLoss(float eval) const 00343 { 00344 return eval < -100; 00345 } 00346 00347 template<class PLAYER> 00348 void GoUctBookBuilder<PLAYER>::PlayMove(SgMove move) 00349 { 00350 m_state.Play(move); 00351 } 00352 00353 template<class PLAYER> 00354 void GoUctBookBuilder<PLAYER>::UndoMove(SgMove move) 00355 { 00356 SG_UNUSED(move); 00357 m_state.Undo(); 00358 } 00359 00360 template<class PLAYER> 00361 bool GoUctBookBuilder<PLAYER>::GetNode(SgBookNode& node) const 00362 { 00363 return m_book->Get(m_state, node); 00364 } 00365 00366 template<class PLAYER> 00367 void GoUctBookBuilder<PLAYER>::WriteNode(const SgBookNode& node) 00368 { 00369 m_book->Put(m_state, node); 00370 } 00371 00372 template<class PLAYER> 00373 void GoUctBookBuilder<PLAYER>::FlushBook() 00374 { 00375 SgDebug() << "Flushing DB...\n"; 00376 m_book->Flush(); 00377 } 00378 00379 template<class PLAYER> 00380 float GoUctBookBuilder<PLAYER>::Value(const SgBookNode& node) const 00381 { 00382 return node.m_value; 00383 } 00384 00385 template<class PLAYER> 00386 void GoUctBookBuilder<PLAYER>::GetAllLegalMoves(std::vector<SgMove>& moves) 00387 { 00388 for (GoBoard::Iterator it(m_state.Board()); it; ++it) 00389 if (m_state.Board().IsLegal(*it)) 00390 moves.push_back(*it); 00391 } 00392 00393 /** Creates root node if necessary. */ 00394 template<class PLAYER> 00395 void GoUctBookBuilder<PLAYER>::EnsureRootExists() 00396 { 00397 SgBookNode root; 00398 if (!GetNode(root)) 00399 { 00400 BeforeEvaluateChildren(); 00401 PrintMessage("Creating root node...\n"); 00402 float value = m_workers[0](SG_NULLMOVE); 00403 WriteNode(SgBookNode(value)); 00404 } 00405 } 00406 00407 /** Computes an ordered set of moves to consider. */ 00408 template<class PLAYER> 00409 bool GoUctBookBuilder<PLAYER>::GenerateMoves(std::vector<SgMove>& moves, 00410 float& value) 00411 { 00412 SG_UNUSED(value); 00413 00414 // Search for a few seconds. 00415 SgDebug() << m_state.Board() << '\n'; 00416 m_players[0]->SetMaxGames(m_numGamesPerSort); 00417 m_workers[0](SG_NULLMOVE); 00418 std::vector<std::pair<SgUctValue, SgMove> > ordered; 00419 // Store counts for each move in vector. 00420 { 00421 const SgUctTree& tree = m_players[0]->Search().Tree(); 00422 const SgUctNode& root = tree.Root(); 00423 for (GoBoard::Iterator it(m_state.Board()); it; ++it) 00424 if (m_state.Board().IsLegal(*it)) 00425 { 00426 SgMove move = *it; 00427 const SgUctNode* node = 00428 SgUctTreeUtil::FindChildWithMove(tree, root, move); 00429 if (node && node->PosCount() > 0) 00430 { 00431 ordered.push_back(std::make_pair(-node->PosCount(), move)); 00432 } 00433 } 00434 } 00435 // Sort moves based on count of this search. 00436 std::stable_sort(ordered.begin(), ordered.end()); 00437 for (std::size_t i = 0; i < ordered.size(); ++i) 00438 moves.push_back(ordered[i].second); 00439 SgDebug() << '\n'; 00440 return false; 00441 } 00442 00443 template<class PLAYER> 00444 void GoUctBookBuilder<PLAYER>::BeforeEvaluateChildren() 00445 { 00446 for (std::size_t i = 0; i < m_numThreads; ++i) 00447 m_players[i]->SetMaxGames(m_numGamesPerEvaluation); 00448 } 00449 00450 template<class PLAYER> 00451 void GoUctBookBuilder<PLAYER> 00452 ::EvaluateChildren(const std::vector<SgMove>& childrenToDo, 00453 std::vector<std::pair<SgMove, float> >& scores) 00454 { 00455 SgDebug() << "Evaluating children:"; 00456 for (std::size_t i = 0; i < childrenToDo.size(); ++i) 00457 SgDebug() << ' ' << SgWritePoint(childrenToDo[i]); 00458 SgDebug() << '\n'; 00459 m_threadedWorker->DoWork(childrenToDo, scores); 00460 } 00461 00462 template<class PLAYER> 00463 void GoUctBookBuilder<PLAYER>::AfterEvaluateChildren() 00464 { 00465 } 00466 00467 template<class PLAYER> 00468 void GoUctBookBuilder<PLAYER>::StartIteration() 00469 { 00470 } 00471 00472 template<class PLAYER> 00473 void GoUctBookBuilder<PLAYER>::EndIteration() 00474 { 00475 } 00476 00477 template<class PLAYER> 00478 void GoUctBookBuilder<PLAYER>::ClearAllVisited() 00479 { 00480 m_visited.clear(); 00481 } 00482 00483 template<class PLAYER> 00484 void GoUctBookBuilder<PLAYER>::MarkAsVisited() 00485 { 00486 m_visited.insert(m_state.GetHashCode()); 00487 } 00488 00489 template<class PLAYER> 00490 bool GoUctBookBuilder<PLAYER>::HasBeenVisited() 00491 { 00492 return m_visited.count(m_state.GetHashCode()) == 1; 00493 } 00494 00495 //---------------------------------------------------------------------------- 00496 00497 #endif // GOBOOKBUILDER_HPP