00001 //---------------------------------------------------------------------------- 00002 /** @file BookBuilder.hpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #ifndef BOOKBUILDER_HPP 00007 #define BOOKBUILDER_HPP 00008 00009 #include <cmath> 00010 #include "BenzenePlayer.hpp" 00011 #include "BitsetIterator.hpp" 00012 #include "HashDB.hpp" 00013 #include "Book.hpp" 00014 #include "EndgameUtils.hpp" 00015 #include "StateDB.hpp" 00016 #include "Resistance.hpp" 00017 #include "SgThreadedWorker.h" 00018 #include "SgBookBuilder.h" 00019 #include "SgUctSearch.h" 00020 00021 _BEGIN_BENZENE_NAMESPACE_ 00022 00023 //---------------------------------------------------------------------------- 00024 00025 /** Expands a Book using the given player to evaluate 00026 game positions. 00027 00028 Supports multithreaded evaluation of states. 00029 00030 We do not include the swap rule as a move, since this would lead 00031 to redundant evaluation computations (such as a2-f6 and 00032 a2-swap-f6). We do handle swap implicitly, however. States in 00033 which swap is a valid move are scored taking it into account. 00034 00035 @ingroup openingbook 00036 */ 00037 template<class PLAYER> 00038 class BookBuilder : public SgBookBuilder 00039 { 00040 public: 00041 00042 /** Constructor. Takes a reference to the player that will 00043 evaluate states, which must be a reference to a 00044 BenzenePlayer. */ 00045 BookBuilder(PLAYER& player); 00046 00047 /** Destructor. */ 00048 ~BookBuilder(); 00049 00050 //--------------------------------------------------------------------- 00051 00052 /** Sets the state to start work from. */ 00053 void SetState(Book& book, const HexState& state); 00054 00055 /** Sets the board the book builder can use to perform work. */ 00056 void SetWorkBoard(HexBoard& brd); 00057 00058 //--------------------------------------------------------------------- 00059 00060 /** Whether to prune out inferior cells from the book or not. */ 00061 bool UseICE() const; 00062 00063 /** See UseICE() */ 00064 void SetUseICE(bool flag); 00065 00066 /** Number of players to use during leaf expansion. Each player 00067 may use a multi-threaded search. Should speed up the expansion 00068 of leaf states by a factor of (very close to) NumThreads(). */ 00069 std::size_t NumThreads() const; 00070 00071 /** See NumThreads() */ 00072 void SetNumThreads(std::size_t num); 00073 00074 protected: 00075 std::string MoveString(SgMove move) const; 00076 00077 void PrintMessage(std::string msg); 00078 00079 float InverseEval(float eval) const; 00080 00081 bool IsLoss(float eval) const; 00082 00083 void PlayMove(SgMove move); 00084 00085 void UndoMove(SgMove move); 00086 00087 bool GetNode(SgBookNode& node) const; 00088 00089 float Value(const SgBookNode& node) const; 00090 00091 void WriteNode(const SgBookNode& node); 00092 00093 void FlushBook(); 00094 00095 void EnsureRootExists(); 00096 00097 bool GenerateMoves(std::vector<SgMove>& moves, float& value); 00098 00099 void GetAllLegalMoves(std::vector<SgMove>& moves); 00100 00101 void EvaluateChildren(const std::vector<SgMove>& childrenToDo, 00102 std::vector<std::pair<SgMove, float> >& scores); 00103 void Init(); 00104 00105 void StartIteration(int iteration); 00106 00107 void EndIteration(); 00108 00109 void BeforeEvaluateChildren(); 00110 00111 void AfterEvaluateChildren(); 00112 00113 void Fini(); 00114 00115 void ClearAllVisited(); 00116 00117 void MarkAsVisited(); 00118 00119 bool HasBeenVisited(); 00120 00121 private: 00122 00123 /** Copyable worker. */ 00124 class Worker 00125 { 00126 public: 00127 Worker(std::size_t id, BenzenePlayer& player, HexBoard& brd); 00128 00129 void SetState(const HexState& state); 00130 00131 float operator()(const SgMove& move); 00132 00133 private: 00134 00135 std::size_t m_id; 00136 00137 HexBoard* m_brd; 00138 00139 BenzenePlayer* m_player; 00140 00141 HexState m_state; 00142 }; 00143 00144 /** Book this builder is expanding */ 00145 Book* m_book; 00146 00147 /** Player passed to constructor. */ 00148 PLAYER& m_orig_player; 00149 00150 HexBoard* m_brd; 00151 00152 HexState m_state; 00153 00154 std::set<hash_t> m_visited; 00155 00156 /** See UseICE() */ 00157 bool m_use_ice; 00158 00159 /** See NumberThreads() */ 00160 std::size_t m_num_threads; 00161 00162 std::size_t m_num_evals; 00163 00164 std::size_t m_num_widenings; 00165 00166 std::size_t m_value_updates; 00167 00168 std::size_t m_priority_updates; 00169 00170 std::size_t m_internal_nodes; 00171 00172 std::size_t m_leaf_nodes; 00173 00174 std::size_t m_terminal_nodes; 00175 00176 /** Boards for each thread. */ 00177 std::vector<HexBoard*> m_boards; 00178 00179 /** Players for each thread. */ 00180 std::vector<BenzenePlayer*> m_players; 00181 00182 std::vector<Worker> m_workers; 00183 00184 SgThreadedWorker<SgMove,float,Worker>* m_threadedWorker; 00185 00186 void CreateWorkers(); 00187 00188 void DestroyWorkers(); 00189 }; 00190 00191 //---------------------------------------------------------------------------- 00192 00193 template<class PLAYER> 00194 BookBuilder<PLAYER>::BookBuilder(PLAYER& player) 00195 : SgBookBuilder(), 00196 m_book(0), 00197 m_orig_player(player), 00198 m_brd(0), 00199 m_use_ice(false), 00200 m_num_threads(1) 00201 { 00202 } 00203 00204 template<class PLAYER> 00205 BookBuilder<PLAYER>::~BookBuilder() 00206 { 00207 } 00208 00209 //---------------------------------------------------------------------------- 00210 00211 template<class PLAYER> 00212 inline bool BookBuilder<PLAYER>::UseICE() const 00213 { 00214 return m_use_ice; 00215 } 00216 00217 template<class PLAYER> 00218 inline void BookBuilder<PLAYER>::SetUseICE(bool flag) 00219 { 00220 m_use_ice = flag; 00221 } 00222 00223 template<class PLAYER> 00224 inline std::size_t BookBuilder<PLAYER>::NumThreads() const 00225 { 00226 return m_num_threads; 00227 } 00228 00229 template<class PLAYER> 00230 inline void BookBuilder<PLAYER>::SetNumThreads(std::size_t num) 00231 { 00232 m_num_threads = num; 00233 } 00234 00235 //---------------------------------------------------------------------------- 00236 00237 /** Copies the player and board and creates the threads. */ 00238 template<class PLAYER> 00239 void BookBuilder<PLAYER>::CreateWorkers() 00240 { 00241 LogInfo() << "BookBuilder::CreateWorkers()\n"; 00242 for (std::size_t i = 0; i < m_num_threads; ++i) 00243 { 00244 PLAYER* newPlayer = new PLAYER(); 00245 /** @todo Use concept checking to verify this method exists. */ 00246 newPlayer->CopySettingsFrom(m_orig_player); 00247 newPlayer->SetSearchSingleton(true); 00248 // Set select to something not SG_UCTMOVESELECT_COUNT to 00249 // force it to perform the required number of playouts. 00250 newPlayer->Search().SetMoveSelect(SG_UCTMOVESELECT_BOUND); 00251 00252 m_players.push_back(newPlayer); 00253 m_boards.push_back(new HexBoard(*m_brd)); 00254 m_workers.push_back(Worker(i, *m_players[i], *m_boards[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 BookBuilder<PLAYER>::DestroyWorkers() 00263 { 00264 LogInfo() << "BookBuilder::DestroyWorkers()\n"; 00265 for (std::size_t i = 0; i < m_num_threads; ++i) 00266 { 00267 delete m_boards[i]; 00268 delete m_players[i]; 00269 } 00270 delete m_threadedWorker; 00271 m_workers.clear(); 00272 m_boards.clear(); 00273 m_players.clear(); 00274 } 00275 00276 template<class PLAYER> 00277 void BookBuilder<PLAYER>::Init() 00278 { 00279 CreateWorkers(); 00280 } 00281 00282 template<class PLAYER> 00283 void BookBuilder<PLAYER>::Fini() 00284 { 00285 DestroyWorkers(); 00286 } 00287 00288 //---------------------------------------------------------------------------- 00289 00290 template<class PLAYER> 00291 BookBuilder<PLAYER>::Worker::Worker(std::size_t id, BenzenePlayer& player, 00292 HexBoard& brd) 00293 00294 : m_id(id), 00295 m_brd(&brd), 00296 m_player(&player) 00297 { 00298 } 00299 00300 template<class PLAYER> 00301 void BookBuilder<PLAYER>::Worker::SetState(const HexState& state) 00302 { 00303 m_state = state; 00304 } 00305 00306 template<class PLAYER> 00307 float BookBuilder<PLAYER>::Worker::operator()(const SgMove& move) 00308 { 00309 HexState state(m_state); 00310 if (move >= 0) 00311 state.PlayMove(static_cast<HexPoint>(move)); 00312 00313 // stupid crap to meet interface of player 00314 StoneBoard blah(state.Position()); 00315 Game game(blah); 00316 00317 LogInfo() << "Evaluating: " << state.Position() << '\n'; 00318 00319 HexEval score; 00320 m_brd->GetPosition().SetPosition(state.Position()); 00321 m_player->GenMove(state, game, *m_brd, 99999, score); 00322 return score; 00323 } 00324 00325 //---------------------------------------------------------------------------- 00326 00327 template<class PLAYER> 00328 inline void BookBuilder<PLAYER>::SetState(Book& book, const HexState& state) 00329 { 00330 m_book = &book; 00331 m_state = state; 00332 } 00333 00334 template<class PLAYER> 00335 inline void BookBuilder<PLAYER>::SetWorkBoard(HexBoard& workBoard) 00336 { 00337 m_brd = &workBoard; 00338 } 00339 00340 template<class PLAYER> 00341 std::string BookBuilder<PLAYER>::MoveString(SgMove move) const 00342 { 00343 return HexPointUtil::ToString(static_cast<HexPoint>(move)); 00344 } 00345 00346 template<class PLAYER> 00347 void BookBuilder<PLAYER>::PrintMessage(std::string msg) 00348 { 00349 LogInfo() << msg; 00350 } 00351 00352 template<class PLAYER> 00353 inline float BookBuilder<PLAYER>::InverseEval(float eval) const 00354 { 00355 return BookUtil::InverseEval(eval); 00356 } 00357 00358 template<class PLAYER> 00359 inline bool BookBuilder<PLAYER>::IsLoss(float eval) const 00360 { 00361 return HexEvalUtil::IsLoss(eval); 00362 } 00363 00364 template<class PLAYER> 00365 void BookBuilder<PLAYER>::PlayMove(SgMove move) 00366 { 00367 m_state.PlayMove(static_cast<HexPoint>(move)); 00368 } 00369 00370 template<class PLAYER> 00371 void BookBuilder<PLAYER>::UndoMove(SgMove move) 00372 { 00373 m_state.UndoMove(static_cast<HexPoint>(move)); 00374 } 00375 00376 template<class PLAYER> 00377 bool BookBuilder<PLAYER>::GetNode(SgBookNode& node) const 00378 { 00379 HexBookNode hexNode; 00380 if (m_book->Get(m_state, hexNode)) 00381 { 00382 node = hexNode; 00383 return true; 00384 } 00385 return false; 00386 } 00387 00388 template<class PLAYER> 00389 void BookBuilder<PLAYER>::WriteNode(const SgBookNode& node) 00390 { 00391 HexBookNode hexNode(node); 00392 m_book->Put(m_state, hexNode); 00393 } 00394 00395 template<class PLAYER> 00396 void BookBuilder<PLAYER>::FlushBook() 00397 { 00398 LogInfo() << "Flushing DB...\n"; 00399 m_book->Flush(); 00400 } 00401 00402 template<class PLAYER> 00403 float BookBuilder<PLAYER>::Value(const SgBookNode& node) const 00404 { 00405 return BookUtil::Value(node, m_state); 00406 } 00407 00408 template<class PLAYER> 00409 void BookBuilder<PLAYER>::GetAllLegalMoves(std::vector<SgMove>& moves) 00410 { 00411 for (BitsetIterator it(m_state.Position().GetEmpty()); it; ++it) 00412 moves.push_back(*it); 00413 } 00414 00415 /** Creates root node if necessary. */ 00416 template<class PLAYER> 00417 void BookBuilder<PLAYER>::EnsureRootExists() 00418 { 00419 SgBookNode root; 00420 if (!GetNode(root)) 00421 { 00422 m_workers[0].SetState(m_state); 00423 float value = m_workers[0](SG_NULLMOVE); 00424 WriteNode(SgBookNode(value)); 00425 } 00426 } 00427 00428 /** Computes an ordered set of moves to consider. Returns true if 00429 state is determined, with the value set in value and moves 00430 untouched. Returns false otherwise, in which case moves will 00431 contain the sorted moves and value will be untouched. */ 00432 template<class PLAYER> 00433 bool BookBuilder<PLAYER>::GenerateMoves(std::vector<SgMove>& moves, 00434 float& value) 00435 { 00436 // Turn off ICE (controlled by method UseICE()): compute the moves 00437 // to consider without using any ice, so that we do not leave the 00438 // book if opponent plays an inferiogr move. 00439 HexColor toMove = m_state.ToPlay(); 00440 bool useICE = m_brd->UseICE(); 00441 m_brd->SetUseICE(m_use_ice); 00442 m_brd->GetPosition().SetPosition(m_state.Position()); 00443 m_brd->ComputeAll(toMove); 00444 m_brd->SetUseICE(useICE); 00445 00446 { 00447 HexEval hexValue; 00448 if (EndgameUtils::IsDeterminedState(*m_brd, toMove, hexValue)) 00449 { 00450 value = hexValue; 00451 return true; 00452 } 00453 } 00454 00455 bitset_t children = EndgameUtils::MovesToConsider(*m_brd, toMove); 00456 HexAssert(children.any()); 00457 00458 Resistance resist; 00459 resist.Evaluate(*m_brd); 00460 std::vector<HexMoveValue> ordered; 00461 00462 // BUG: This does NOT take swap into account. This means the 00463 // ordered set of moves returned in the root state is not ordered 00464 // according to the swap rule. No real way to fix this while using 00465 // resistance values, but it is possible to fix if we used mohex 00466 // evaluations to sort the moves. 00467 00468 for (BitsetIterator it(children); it; ++it) 00469 // use negative so higher values go to front 00470 ordered.push_back(HexMoveValue(*it, -resist.Score(*it))); 00471 std::stable_sort(ordered.begin(), ordered.end()); 00472 for (std::size_t i = 0; i < ordered.size(); ++i) 00473 moves.push_back(ordered[i].point()); 00474 return false; 00475 } 00476 00477 template<class PLAYER> 00478 void BookBuilder<PLAYER>::BeforeEvaluateChildren() 00479 { 00480 for (std::size_t i = 0; i < m_workers.size(); ++i) 00481 m_workers[i].SetState(m_state); 00482 } 00483 00484 template<class PLAYER> 00485 void BookBuilder<PLAYER> 00486 ::EvaluateChildren(const std::vector<SgMove>& childrenToDo, 00487 std::vector<std::pair<SgMove, float> >& scores) 00488 { 00489 m_threadedWorker->DoWork(childrenToDo, scores); 00490 } 00491 00492 template<class PLAYER> 00493 void BookBuilder<PLAYER>::AfterEvaluateChildren() 00494 { 00495 } 00496 00497 template<class PLAYER> 00498 void BookBuilder<PLAYER>::StartIteration(int iteration) 00499 { 00500 LogInfo() << "\n--Iteration " << iteration << "--\n"; 00501 } 00502 00503 template<class PLAYER> 00504 void BookBuilder<PLAYER>::EndIteration() 00505 { 00506 // DO NOTHING FOR NOW 00507 } 00508 00509 template<class PLAYER> 00510 void BookBuilder<PLAYER>::ClearAllVisited() 00511 { 00512 m_visited.clear(); 00513 } 00514 00515 template<class PLAYER> 00516 void BookBuilder<PLAYER>::MarkAsVisited() 00517 { 00518 m_visited.insert(m_state.Hash()); 00519 } 00520 00521 template<class PLAYER> 00522 bool BookBuilder<PLAYER>::HasBeenVisited() 00523 { 00524 return m_visited.count(m_state.Hash()) == 1; 00525 } 00526 00527 //---------------------------------------------------------------------------- 00528 00529 _END_BENZENE_NAMESPACE_ 00530 00531 #endif // BOOKBUILDER_HPP