Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

HexUctState.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file HexUctState.cpp
00003 
00004     @note Use SG_ASSERT so that the assertion handler is used to dump
00005     the state of each thread when an assertion fails.
00006 
00007     @bug Running with assertions and a non-zero knowledge threshold in
00008     lock-free mode will cause some assertions to fail. In particular,
00009     the way we handle terminal states (by deleting all children) can
00010     cause SgUctChildIterator to discover it has no children (in
00011     SgUctSearch::UpdateRaveValues and SgUctSearch::SelectChild) which
00012     it asserts is not true. It is also possible for threads to play
00013     into filled-in cells during the in-tree phase.
00014 */
00015 //----------------------------------------------------------------------------
00016 
00017 #include "SgSystem.h"
00018 
00019 #include "SgException.h"
00020 #include "SgMove.h"
00021 
00022 #include "BoardUtils.hpp"
00023 #include "BitsetIterator.hpp"
00024 #include "HexUctPolicy.hpp"
00025 #include "HexUctUtil.hpp"
00026 #include "PatternState.hpp"
00027 #include "EndgameUtils.hpp"
00028 #include "SequenceHash.hpp"
00029 
00030 using namespace benzene;
00031 
00032 /** Prints output during knowledge computation. */
00033 static const bool DEBUG_KNOWLEDGE = false;
00034 
00035 /** Prints hash sequence before computing knowledge. 
00036     Use to see what threads are doing knowledge computations. */
00037 static const bool TRACK_KNOWLEDGE = false;
00038 
00039 //----------------------------------------------------------------------------
00040 
00041 namespace
00042 {
00043 
00044 /** Returns true if board is entirely filled. */
00045 bool GameOver(const StoneBoard& brd)
00046 {
00047     return brd.GetEmpty().none();
00048 }
00049 
00050 /** Determines the winner of a filled-in board. */
00051 HexColor GetWinner(const StoneBoard& brd)
00052 {
00053     SG_ASSERT(GameOver(brd));
00054     if (BoardUtils::ConnectedOnBitset(brd.Const(), brd.GetColor(BLACK), 
00055                                       NORTH, SOUTH))
00056         return BLACK;
00057     return WHITE;
00058 }
00059 
00060 /** Returns winner if there is one. Returns EMPTY if no winner. */
00061 HexColor CheckIfWinner(const StoneBoard& brd)
00062 {
00063     if (BoardUtils::ConnectedOnBitset(brd.Const(), brd.GetColor(BLACK), 
00064                                       NORTH, SOUTH))
00065         return BLACK;
00066     if (BoardUtils::ConnectedOnBitset(brd.Const(), brd.GetColor(WHITE),
00067                                       EAST, WEST))
00068         return WHITE;
00069     return EMPTY;
00070 }
00071 
00072 } // namespace
00073 
00074 //----------------------------------------------------------------------------
00075 
00076 HexUctState::AssertionHandler::AssertionHandler(const HexUctState& state)
00077     : m_state(state)
00078 {
00079 }
00080 
00081 void HexUctState::AssertionHandler::Run()
00082 {
00083     LogSevere() << m_state.Dump() << '\n';
00084 }
00085 
00086 //----------------------------------------------------------------------------
00087 
00088 HexUctState::HexUctState(const unsigned int threadId,
00089              HexUctSearch& sch,
00090                          int treeUpdateRadius,
00091                          int playoutUpdateRadius)
00092     : SgUctThreadState(threadId, HexUctUtil::ComputeMaxNumMoves()),
00093       m_assertionHandler(*this),
00094 
00095       m_bd(0),
00096       m_vc_brd(0),
00097       m_policy(0),
00098       m_shared_data(0),
00099       m_knowledge(*this),
00100       m_search(sch),
00101        m_treeUpdateRadius(treeUpdateRadius),
00102       m_playoutUpdateRadius(playoutUpdateRadius),
00103       m_isInPlayout(false)
00104 {
00105 }
00106 
00107 HexUctState::~HexUctState()
00108 {
00109 }
00110 
00111 void HexUctState::SetPolicy(HexUctSearchPolicy* policy)
00112 {
00113     m_policy.reset(policy);
00114 }
00115 
00116 std::string HexUctState::Dump() const
00117 {
00118     std::ostringstream os;
00119     os << "HexUctState[" << m_threadId << "] ";
00120     if (m_isInPlayout) 
00121         os << "[playout] ";
00122     os << "board:" << *m_bd;
00123     return os.str();
00124 }
00125 
00126 SgUctValue HexUctState::Evaluate()
00127 {
00128     SG_ASSERT(GameOver(*m_bd));
00129     SgUctValue score = (GetWinner(*m_bd) == m_toPlay) ? 1.0 : 0.0;
00130     return score;
00131 }
00132 
00133 void HexUctState::Execute(SgMove sgmove)
00134 {
00135     ExecuteTreeMove(static_cast<HexPoint>(sgmove));
00136     m_toPlay = !m_toPlay;
00137 }
00138 
00139 void HexUctState::ExecutePlayout(SgMove sgmove)
00140 {
00141     ExecuteRolloutMove(static_cast<HexPoint>(sgmove));
00142     m_toPlay = !m_toPlay;
00143 }
00144 
00145 void HexUctState::ExecuteTreeMove(HexPoint move)
00146 {
00147     {
00148         HexUctPolicy* blah = dynamic_cast<HexUctPolicy*>(m_policy.get());
00149         if (!blah)
00150             abort();
00151         blah->AddResponse(m_toPlay, m_lastMovePlayed, move);
00152     }
00153 
00154     m_game_sequence.push_back(Move(m_toPlay, move));
00155     ExecutePlainMove(move, m_treeUpdateRadius);
00156     HexUctStoneData stones;
00157     if (m_shared_data->stones.get(SequenceHash::Hash(m_game_sequence), stones))
00158     {
00159         m_bd->StartNewGame();
00160         m_bd->SetColor(BLACK, stones.black);
00161         m_bd->SetColor(WHITE, stones.white);
00162         m_bd->SetPlayed(stones.played);
00163         m_pastate->Update();
00164     }
00165 }
00166 
00167 void HexUctState::ExecuteRolloutMove(HexPoint move)
00168 {
00169     ExecutePlainMove(move, m_playoutUpdateRadius);
00170 }
00171 
00172 void HexUctState::ExecutePlainMove(HexPoint cell, int updateRadius)
00173 {
00174     // Lock-free mode: It is possible we are playing into a filled-in
00175     // cell during the in-tree phase. This can occur if the thread
00176     // happens upon this state after fillin was published but before
00177     // the tree was pruned.
00178     //   If assertions are off, this results in a board possibly
00179     // containing cells of both colors and erroneous pattern state
00180     // info, resulting in an inaccurate playout value. In practice,
00181     // this does not seem to matter too much.
00182     //   If assertions are on, this will cause the search to abort
00183     // needlessly.
00184     // @todo Handle case when assertions are on.
00185     SG_ASSERT(m_bd->IsEmpty(cell));
00186     SG_ASSERT(m_pastate->UpdateRadius() == updateRadius);
00187     
00188     m_bd->PlayMove(m_toPlay, cell);
00189     if (updateRadius == 1)
00190     m_pastate->UpdateRingGodel(cell);
00191     else
00192     m_pastate->Update(cell);
00193     
00194     m_lastMovePlayed = cell;
00195     m_new_game = false;
00196 }
00197 
00198 bool HexUctState::GenerateAllMoves(SgUctValue count, 
00199                                    std::vector<SgMoveInfo>& moves,
00200                                    SgProvenNodeType& provenType)
00201 {
00202     moves.clear();
00203 
00204     // Handle root node as a special case
00205     if (m_new_game)
00206     {
00207         for (BitsetIterator it(m_shared_data->root_consider); it; ++it)
00208             moves.push_back(SgMoveInfo(*it));
00209         if (count == 0)
00210             m_knowledge.ProcessPosition(moves);
00211         return false;
00212     }
00213 
00214     bool truncateChildTrees = false;
00215     if (count == 0)
00216     {
00217         {
00218             HexColor winner = CheckIfWinner(*m_bd);
00219             if (winner != EMPTY)
00220             {
00221                 provenType = (winner == m_toPlay) 
00222                     ? SG_PROVEN_WIN : SG_PROVEN_LOSS;
00223                 return false;
00224             }
00225         }
00226         for (BitsetIterator it(m_bd->GetEmpty()); it; ++it)
00227             moves.push_back(SgMoveInfo(*it));
00228         m_knowledge.ProcessPosition(moves);
00229     }
00230     else
00231     {
00232         // Prune moves outside of mustplay and fillin
00233         if (TRACK_KNOWLEDGE)
00234         {
00235             hash_t hash = SequenceHash::Hash(m_game_sequence);
00236             LogInfo() << m_threadId << ": " 
00237                       << HashUtil::toString(hash) << '\n';
00238         }
00239         truncateChildTrees = true;
00240         bitset_t moveset = m_bd->GetEmpty() & ComputeKnowledge(provenType);
00241         for (BitsetIterator it(moveset); it; ++it)
00242             moves.push_back(SgMoveInfo(*it));
00243     }
00244     return truncateChildTrees;
00245 }
00246 
00247 SgMove HexUctState::GeneratePlayoutMove(bool& skipRaveUpdate)
00248 {
00249     skipRaveUpdate = false;
00250     
00251     if (GameOver(*m_bd))
00252         return SG_NULLMOVE;
00253         
00254     SgPoint move = m_policy->GenerateMove(*m_pastate, m_toPlay,
00255                                           m_lastMovePlayed);
00256     SG_ASSERT(move != SG_NULLMOVE);
00257     return move;
00258 }
00259 
00260 void HexUctState::StartSearch()
00261 {
00262     LogInfo() << "StartSearch()[" << m_threadId <<"]" << '\n';
00263     m_shared_data = &m_search.SharedData();
00264 
00265     // @todo Fix the interface to HexBoard so this can be constant!
00266     // The problem is that VCBuilder (which is inside of HexBoard)
00267     // expects a non-const reference to a VCBuilderParam object.
00268     HexBoard& brd = const_cast<HexBoard&>(m_search.Board());
00269     
00270     if (!m_bd.get() 
00271         || m_bd->Width() != brd.Width() 
00272         || m_bd->Height() != brd.Height())
00273     {
00274         m_bd.reset(new StoneBoard(brd.Width(), brd.Height()));
00275         m_pastate.reset(new PatternState(*m_bd));
00276         m_vc_brd.reset(new HexBoard(brd.Width(), brd.Height(), 
00277                                     brd.ICE(), brd.Builder().Parameters()));
00278     }
00279 
00280     m_policy->InitializeForSearch();
00281 }
00282 
00283 void HexUctState::TakeBackInTree(std::size_t nuMoves)
00284 {
00285     SG_UNUSED(nuMoves);
00286 }
00287 
00288 void HexUctState::TakeBackPlayout(std::size_t nuMoves)
00289 {
00290     SG_UNUSED(nuMoves);
00291 }
00292 
00293 SgBlackWhite HexUctState::ToPlay() const
00294 {
00295     return HexUctUtil::ToSgBlackWhite(m_toPlay);
00296 }
00297 
00298 void HexUctState::GameStart()
00299 {
00300     m_new_game = true;
00301     m_isInPlayout = false;
00302     m_game_sequence = m_shared_data->game_sequence;
00303     m_toPlay = m_shared_data->root_to_play;
00304     m_lastMovePlayed = m_shared_data->root_last_move_played;
00305     m_pastate->SetUpdateRadius(m_treeUpdateRadius);
00306 
00307     m_bd->StartNewGame();
00308     m_bd->SetColor(BLACK, m_shared_data->root_stones.black);
00309     m_bd->SetColor(WHITE, m_shared_data->root_stones.white);
00310     m_bd->SetPlayed(m_shared_data->root_stones.played);
00311     m_pastate->Update();
00312 }
00313 
00314 void HexUctState::StartPlayouts()
00315 {
00316     m_isInPlayout = true;
00317     m_pastate->SetUpdateRadius(m_playoutUpdateRadius);
00318     
00319     /** Playout radius should normally be no bigger than tree radius,
00320     but if it is, we need to do an extra update for each playout
00321     during the transition from the tree phase to the playout
00322     phase. */
00323     if (m_playoutUpdateRadius > m_treeUpdateRadius)
00324     m_pastate->Update();
00325 }
00326 
00327 void HexUctState::StartPlayout()
00328 {
00329     m_policy->InitializeForRollout(*m_bd);
00330 }
00331 
00332 void HexUctState::EndPlayout()
00333 {
00334 }
00335 
00336 /** Computes moves to consider and stores fillin in the shared
00337     data. */
00338 bitset_t HexUctState::ComputeKnowledge(SgProvenNodeType& provenType)
00339 {
00340     m_vc_brd->GetPosition().StartNewGame();
00341     m_vc_brd->GetPosition().SetColor(BLACK, m_bd->GetPlayed(BLACK));
00342     m_vc_brd->GetPosition().SetColor(WHITE, m_bd->GetPlayed(WHITE));
00343     m_vc_brd->GetPosition().SetPlayed(m_bd->GetPlayed());
00344     m_vc_brd->ComputeAll(m_toPlay);
00345 
00346     // Consider set will be all empty cells if state is a determined
00347     // state (can't compute consider set in this case and we cannot
00348     // delete the children as this will cause a race condition in the
00349     // parent class).
00350     //
00351     // Consider set is the set of moves to consider otherwise.
00352     bitset_t consider;
00353     if (EndgameUtils::IsDeterminedState(*m_vc_brd, m_toPlay))
00354     {
00355         HexColor winner = m_toPlay;
00356         provenType = SG_PROVEN_WIN;
00357         if (EndgameUtils::IsLostGame(*m_vc_brd, m_toPlay))
00358         {
00359             winner = !m_toPlay;
00360             provenType = SG_PROVEN_LOSS;
00361         }
00362         if (DEBUG_KNOWLEDGE)
00363             LogInfo() << "Found win for " << winner << ": " << '\n' 
00364                       << *m_vc_brd << '\n';
00365         return m_bd->GetEmpty();
00366     }
00367     else
00368     {
00369         provenType = SG_NOT_PROVEN;
00370         consider = EndgameUtils::MovesToConsider(*m_vc_brd, m_toPlay);
00371     }
00372 
00373     m_shared_data->stones.put(SequenceHash::Hash(m_game_sequence), 
00374                               HexUctStoneData(m_vc_brd->GetPosition()));
00375     if (DEBUG_KNOWLEDGE)
00376         LogInfo() << "===================================" << '\n'
00377                   << "Recomputed state:" << '\n' << *m_bd << '\n'
00378                   << "Consider:" << m_vc_brd->Write(consider) << '\n';
00379 
00380     return consider;
00381 }
00382     
00383 //----------------------------------------------------------------------------


6 Jan 2011 Doxygen 1.6.3