Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

BookBuilder.hpp

Go to the documentation of this file.
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


6 Jan 2011 Doxygen 1.6.3