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