00001
00002
00003
00004
00005 #include "SgSystem.h"
00006 #include "GoUctSearch.h"
00007
00008 #include <fstream>
00009 #include <iostream>
00010 #include "GoBoardUtil.h"
00011 #include "GoNodeUtil.h"
00012 #include "GoUctUtil.h"
00013 #include "SgDebug.h"
00014 #include "SgGameWriter.h"
00015 #include "SgNode.h"
00016 #include "SgUctTreeUtil.h"
00017
00018 using namespace std;
00019
00020
00021
00022 namespace {
00023
00024
00025
00026 const int MOVERANGE = SG_PASS + 1;
00027
00028 SgNode* AppendChild(SgNode* node, const string& comment)
00029 {
00030 SgNode* child = node->NewRightMostSon();
00031 child->AddComment(comment);
00032 return child;
00033 }
00034
00035 SgNode* AppendChild(SgNode* node, SgBlackWhite color, SgPoint move)
00036 {
00037 SgNode* child = node->NewRightMostSon();
00038 SgPropID propId =
00039 (color == SG_BLACK ? SG_PROP_MOVE_BLACK : SG_PROP_MOVE_WHITE);
00040 child->Add(new SgPropMove(propId, move));
00041 return child;
00042 }
00043
00044
00045 void AppendGame(SgNode* node, SgUctValue gameNumber, unsigned int threadId,
00046 SgBlackWhite toPlay, const SgUctGameInfo& info)
00047 {
00048 SG_ASSERT(node != 0);
00049 {
00050 ostringstream comment;
00051 comment << "Thread " << threadId << '\n'
00052 << "Game " << gameNumber << '\n';
00053 node = AppendChild(node, comment.str());
00054 }
00055 size_t nuMovesInTree = info.m_inTreeSequence.size();
00056 for (size_t i = 0; i < nuMovesInTree; ++i)
00057 {
00058 node = AppendChild(node, toPlay, info.m_inTreeSequence[i]);
00059 toPlay = SgOppBW(toPlay);
00060 }
00061 SgNode* lastInTreeNode = node;
00062 SgBlackWhite lastInTreeToPlay = toPlay;
00063 for (size_t i = 0; i < info.m_eval.size(); ++i)
00064 {
00065 node = lastInTreeNode;
00066 toPlay = lastInTreeToPlay;
00067 ostringstream comment;
00068 comment << "Playout " << i << '\n'
00069 << "Eval " << info.m_eval[i] << '\n'
00070 << "Aborted " << info.m_aborted[i] << '\n';
00071 node = AppendChild(node, comment.str());
00072 for (size_t j = nuMovesInTree; j < info.m_sequence[i].size(); ++j)
00073 {
00074 node = AppendChild(node, toPlay, info.m_sequence[i][j]);
00075 toPlay = SgOppBW(toPlay);
00076 }
00077 }
00078 }
00079
00080
00081
00082 }
00083
00084
00085
00086 GoUctState::AssertionHandler::AssertionHandler(const GoUctState& state)
00087 : m_state(state)
00088 {
00089 }
00090
00091 void GoUctState::AssertionHandler::Run()
00092 {
00093 m_state.Dump(SgDebug());
00094 }
00095
00096
00097
00098 GoUctState::GoUctState(unsigned int threadId, const GoBoard& bd)
00099 : SgUctThreadState(threadId, MOVERANGE),
00100 m_assertionHandler(*this),
00101 m_uctBd(bd),
00102 m_synchronizer(bd)
00103 {
00104 m_synchronizer.SetSubscriber(m_bd);
00105 m_isInPlayout = false;
00106 }
00107
00108 void GoUctState::Dump(ostream& out) const
00109 {
00110 out << "GoUctState[" << m_threadId << "] ";
00111 if (m_isInPlayout)
00112 out << "playout board:\n" << m_uctBd;
00113 else
00114 out << "board:\n" << m_bd;
00115 }
00116
00117 void GoUctState::Execute(SgMove move)
00118 {
00119 SG_ASSERT(! m_isInPlayout);
00120 SG_ASSERT(move == SG_PASS || ! m_bd.Occupied(move));
00121
00122
00123 GoRestoreKoRule restoreKoRule(m_bd);
00124 m_bd.Rules().SetKoRule(GoRules::SIMPLEKO);
00125 m_bd.Play(move);
00126 SG_ASSERT(! m_bd.LastMoveInfo(GO_MOVEFLAG_ILLEGAL));
00127 ++m_gameLength;
00128 }
00129
00130 void GoUctState::ExecutePlayout(SgMove move)
00131 {
00132 SG_ASSERT(m_isInPlayout);
00133 SG_ASSERT(move == SG_PASS || ! m_uctBd.Occupied(move));
00134 m_uctBd.Play(move);
00135 ++m_gameLength;
00136 }
00137
00138 void GoUctState::GameStart()
00139 {
00140 m_isInPlayout = false;
00141 m_gameLength = 0;
00142 }
00143
00144 void GoUctState::StartPlayout()
00145 {
00146 m_uctBd.Init(m_bd);
00147 }
00148
00149 void GoUctState::StartPlayouts()
00150 {
00151 m_isInPlayout = true;
00152 }
00153
00154 void GoUctState::StartSearch()
00155 {
00156 m_synchronizer.UpdateSubscriber();
00157 }
00158
00159 void GoUctState::TakeBackInTree(std::size_t nuMoves)
00160 {
00161 for (size_t i = 0; i < nuMoves; ++i)
00162 m_bd.Undo();
00163 }
00164
00165 void GoUctState::TakeBackPlayout(std::size_t nuMoves)
00166 {
00167 m_gameLength -= nuMoves;
00168 }
00169
00170
00171
00172 GoUctSearch::GoUctSearch(GoBoard& bd, SgUctThreadStateFactory* factory)
00173 : SgUctSearch(factory, MOVERANGE),
00174 m_keepGames(false),
00175 m_liveGfxInterval(5000),
00176 m_toPlay(SG_BLACK),
00177 m_bd(bd),
00178 m_root(0),
00179 m_liveGfx(GOUCT_LIVEGFX_NONE)
00180 {
00181 SetRaveCheckSame(true);
00182 }
00183
00184 GoUctSearch::~GoUctSearch()
00185 {
00186 if (m_root != 0)
00187 m_root->DeleteTree();
00188 m_root = 0;
00189 }
00190
00191 std::string GoUctSearch::MoveString(SgMove move) const
00192 {
00193 return SgPointUtil::PointToString(move);
00194 }
00195
00196 void GoUctSearch::OnSearchIteration(SgUctValue gameNumber,
00197 unsigned int threadId,
00198 const SgUctGameInfo& info)
00199 {
00200 SgUctSearch::OnSearchIteration(gameNumber, threadId, info);
00201
00202 if (m_liveGfx != GOUCT_LIVEGFX_NONE && threadId == 0
00203 && gameNumber > m_nextLiveGfx)
00204 {
00205 m_nextLiveGfx = gameNumber + m_liveGfxInterval;
00206 DisplayGfx();
00207 }
00208 if (! LockFree() && m_root != 0)
00209 AppendGame(m_root, gameNumber, threadId, m_toPlay, info);
00210 }
00211
00212 void GoUctSearch::DisplayGfx()
00213 {
00214 SgDebug() << "gogui-gfx:\n";
00215 switch (m_liveGfx)
00216 {
00217 case GOUCT_LIVEGFX_COUNTS:
00218 GoUctUtil::GfxBestMove(*this, m_toPlay, SgDebug());
00219 GoUctUtil::GfxMoveValues(*this, m_toPlay, SgDebug());
00220 GoUctUtil::GfxCounts(Tree(), SgDebug());
00221 GoUctUtil::GfxStatus(*this, SgDebug());
00222 break;
00223 case GOUCT_LIVEGFX_SEQUENCE:
00224 GoUctUtil::GfxSequence(*this, m_toPlay, SgDebug());
00225 GoUctUtil::GfxStatus(*this, SgDebug());
00226 break;
00227 case GOUCT_LIVEGFX_NONE:
00228 SG_ASSERT(false);
00229 break;
00230 }
00231 SgDebug() << '\n';
00232 }
00233
00234 void GoUctSearch::OnStartSearch()
00235 {
00236 SgUctSearch::OnStartSearch();
00237
00238 if (m_root != 0)
00239 {
00240 m_root->DeleteTree();
00241 m_root = 0;
00242 }
00243 if (m_keepGames)
00244 {
00245 m_root = GoNodeUtil::CreateRoot(m_bd);
00246 if (LockFree())
00247 SgWarning() <<
00248 "GoUctSearch: keep games will be ignored"
00249 " in lock free search\n";
00250 }
00251 m_toPlay = m_bd.ToPlay();
00252 for (SgBWIterator it; it; ++it)
00253 m_stones[*it] = m_bd.All(*it);
00254 int size = m_bd.Size();
00255
00256 int maxGameLength = min(3 * size * size,
00257 GO_MAX_NUM_MOVES - m_bd.MoveNumber());
00258 SetMaxGameLength(maxGameLength);
00259 m_boardHistory.SetFromBoard(m_bd);
00260
00261 m_nextLiveGfx = m_liveGfxInterval;
00262 }
00263
00264 void GoUctSearch::SaveGames(const string& fileName) const
00265 {
00266 if (MpiSynchronizer()->IsRootProcess())
00267 {
00268 if (m_root == 0)
00269 throw SgException("No games to save");
00270 ofstream out(fileName.c_str());
00271 SgGameWriter writer(out);
00272 writer.WriteGame(*m_root, true, 0, 1, 19);
00273 }
00274 }
00275
00276 void GoUctSearch::SaveTree(std::ostream& out, int maxDepth) const
00277 {
00278 GoUctUtil::SaveTree(Tree(), m_bd.Size(), m_stones, m_toPlay, out,
00279 maxDepth);
00280 }
00281
00282 SgBlackWhite GoUctSearch::ToPlay() const
00283 {
00284 return m_toPlay;
00285 }
00286
00287
00288
00289 SgPoint GoUctSearchUtil::TrompTaylorPassCheck(SgPoint move,
00290 const GoUctSearch& search)
00291 {
00292 const GoBoard& bd = search.Board();
00293 bool isFirstPass = (bd.GetLastMove() != SG_PASS);
00294 bool isTrompTaylorRules = bd.Rules().CaptureDead();
00295 if (move != SG_PASS || ! isTrompTaylorRules || ! isFirstPass)
00296 return move;
00297 float komi = bd.Rules().Komi().ToFloat();
00298 float trompTaylorScore = GoBoardUtil::TrompTaylorScore(bd, komi);
00299 if (search.ToPlay() != SG_BLACK)
00300 trompTaylorScore *= -1;
00301 const SgUctTree& tree = search.Tree();
00302 const SgUctNode& root = tree.Root();
00303 SgUctValue value = root.Mean();
00304 SgUctValue trompTaylorWinValue = (trompTaylorScore > 0 ? 1 : 0);
00305 if (value < trompTaylorWinValue)
00306 return move;
00307 SgDebug() << "GoUctSearchUtil::TrompTaylorPassCheck: bad pass move value="
00308 << value << " trompTaylorScore=" << trompTaylorScore << '\n';
00309 vector<SgMove> excludeMoves;
00310 excludeMoves.push_back(SG_PASS);
00311 const SgUctNode* bestChild = search.FindBestChild(root, &excludeMoves);
00312 if (bestChild == 0)
00313 {
00314 SgDebug() <<
00315 "GoUctSearchUtil::TrompTaylorPassCheck: "
00316 "(no second best move found)\n";
00317 return move;
00318 }
00319 return bestChild->Move();
00320 }
00321
00322