00001 //---------------------------------------------------------------------------- 00002 /** @file GoUctUtil.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "SgSystem.h" 00007 #include "GoUctUtil.h" 00008 00009 #include <iomanip> 00010 #include <iostream> 00011 #include <boost/io/ios_state.hpp> 00012 #include "SgBWSet.h" 00013 #include "SgPointSet.h" 00014 #include "SgProp.h" 00015 #include "SgUctSearch.h" 00016 00017 using namespace std; 00018 using boost::io::ios_all_saver; 00019 using SgPointUtil::Pt; 00020 using SgPropUtil::PointToSgfString; 00021 00022 //---------------------------------------------------------------------------- 00023 00024 namespace { 00025 00026 bool IsRectEmpty(const GoBoard& bd, int left, int right, int top, int bottom) 00027 { 00028 for (SgRectIterator it(SgRect(left, right, top, bottom)); it; ++it) 00029 if (! bd.IsEmpty(*it)) 00030 return false; 00031 return true; 00032 } 00033 00034 /** Recursive function to save the UCT tree in SGF format. */ 00035 void SaveNode(ostream& out, const SgUctTree& tree, const SgUctNode& node, 00036 SgBlackWhite toPlay, int boardSize, int maxDepth, int depth) 00037 { 00038 out << "C[MoveCount " << node.MoveCount() 00039 << "\nPosCount " << node.PosCount() 00040 << "\nMean " << fixed << setprecision(2) << node.Mean(); 00041 if (! node.HasChildren()) 00042 { 00043 out << "]\n"; 00044 return; 00045 } 00046 out << "\n\nRave:"; 00047 for (SgUctChildIterator it(tree, node); it; ++it) 00048 { 00049 const SgUctNode& child = *it; 00050 SgPoint move = child.Move(); 00051 if (child.HasRaveValue()) 00052 { 00053 out << '\n' << SgWritePoint(move) << ' ' 00054 << fixed << setprecision(2) << child.RaveValue() 00055 << " (" << child.RaveCount() << ')'; 00056 } 00057 } 00058 out << "]\nLB"; 00059 for (SgUctChildIterator it(tree, node); it; ++it) 00060 { 00061 const SgUctNode& child = *it; 00062 if (! child.HasMean()) 00063 continue; 00064 out << "[" 00065 << PointToSgfString(child.Move(), boardSize, SG_PROPPOINTFMT_GO) 00066 << ':' << child.MoveCount() << ']'; 00067 } 00068 out << '\n'; 00069 if (maxDepth >= 0 && depth >= maxDepth) 00070 return; 00071 for (SgUctChildIterator it(tree, node); it; ++it) 00072 { 00073 const SgUctNode& child = *it; 00074 if (! child.HasMean()) 00075 continue; 00076 SgPoint move = child.Move(); 00077 out << "(;" << (toPlay == SG_BLACK ? 'B' : 'W') << '[' 00078 << PointToSgfString(move, boardSize, SG_PROPPOINTFMT_GO) << ']'; 00079 SaveNode(out, tree, child, SgOppBW(toPlay), boardSize, maxDepth, 00080 depth + 1); 00081 out << ")\n"; 00082 } 00083 } 00084 00085 } // namespace 00086 00087 //---------------------------------------------------------------------------- 00088 00089 void GoUctUtil::ClearStatistics(SgPointArray<SgUctStatistics>& stats) 00090 { 00091 for (SgPointArray<SgUctStatistics>::NonConstIterator 00092 it(stats); it; ++it) 00093 (*it).Clear(); 00094 } 00095 00096 SgPoint GoUctUtil::GenForcedOpeningMove(const GoBoard& bd) 00097 { 00098 int sz = bd.Size(); 00099 if (sz < 15 || bd.TotalNumStones(SG_BLACK) > 5 00100 || bd.TotalNumStones(SG_WHITE) > 5) 00101 return SG_NULLMOVE; 00102 SgSList<SgPoint,4> moves; 00103 if (IsRectEmpty(bd, 1, 5, 1, 5)) 00104 moves.PushBack(Pt(4, 4)); 00105 if (IsRectEmpty(bd, 1, 5, sz - 4, sz)) 00106 moves.PushBack(Pt(4, sz - 3)); 00107 if (IsRectEmpty(bd, sz - 4, sz, 1, 5)) 00108 moves.PushBack(Pt(sz - 3, 4)); 00109 if (IsRectEmpty(bd, sz - 4, sz, sz - 4, sz)) 00110 moves.PushBack(Pt(sz - 3, sz - 3)); 00111 if (moves.IsEmpty()) 00112 return SG_NULLMOVE; 00113 return moves[SgRandom::Global().Int(moves.Length())]; 00114 } 00115 00116 void GoUctUtil::GfxBestMove(const SgUctSearch& search, SgBlackWhite toPlay, 00117 ostream& out) 00118 { 00119 const SgUctTree& tree = search.Tree(); 00120 const SgUctNode& root = tree.Root(); 00121 out << "VAR"; 00122 const SgUctNode* bestValueChild = search.FindBestChild(root); 00123 if (bestValueChild != 0) 00124 { 00125 SgPoint move = bestValueChild->Move(); 00126 out << ' ' << (toPlay == SG_BLACK ? 'B' : 'W') << ' ' 00127 << SgWritePoint(move); 00128 } 00129 out << '\n'; 00130 } 00131 00132 void GoUctUtil::GfxCounts(const SgUctTree& tree, ostream& out) 00133 { 00134 const SgUctNode& root = tree.Root(); 00135 out << "LABEL"; 00136 if (root.HasChildren()) 00137 for (SgUctChildIterator it(tree, root); it; ++it) 00138 { 00139 const SgUctNode& child = *it; 00140 if (child.HasMean()) 00141 out << ' ' << SgWritePoint(child.Move()) << ' ' 00142 << child.MoveCount(); 00143 } 00144 out << '\n'; 00145 } 00146 00147 void GoUctUtil::GfxMoveValues(const SgUctSearch& search, SgBlackWhite toPlay, 00148 ostream& out) 00149 { 00150 const SgUctTree& tree = search.Tree(); 00151 const SgUctNode& root = tree.Root(); 00152 out << "INFLUENCE"; 00153 if (root.HasChildren()) 00154 for (SgUctChildIterator it(tree, root); it; ++it) 00155 { 00156 const SgUctNode& child = *it; 00157 if (! child.HasMean()) 00158 continue; 00159 float value = SgUctSearch::InverseEval(child.Mean()); 00160 // Scale to [-1,+1], black positive 00161 double influence = value * 2 - 1; 00162 if (toPlay == SG_WHITE) 00163 influence *= -1; 00164 SgPoint move = child.Move(); 00165 out << ' ' << SgWritePoint(move) << ' ' << fixed 00166 << setprecision(2) << influence; 00167 } 00168 out << '\n'; 00169 } 00170 \ 00171 void GoUctUtil::GfxSequence(const SgUctSearch& search, SgBlackWhite toPlay, 00172 ostream& out) 00173 { 00174 vector<SgMove> sequence; 00175 search.FindBestSequence(sequence); 00176 out << "VAR"; 00177 for (size_t i = 0; i < sequence.size(); ++i) 00178 { 00179 out << (toPlay == SG_BLACK ? " B ": " W ") 00180 << SgWritePoint(sequence[i]); 00181 toPlay = SgOppBW(toPlay); 00182 } 00183 out << '\n'; 00184 } 00185 00186 void GoUctUtil::GfxStatus(const SgUctSearch& search, ostream& out) 00187 { 00188 const SgUctTree& tree = search.Tree(); 00189 const SgUctNode& root = tree.Root(); 00190 const SgUctSearchStat& stat = search.Statistics(); 00191 int abortPercent = static_cast<int>(stat.m_aborted.Mean() * 100); 00192 out << "TEXT N=" << root.MoveCount() 00193 << " V=" << setprecision(2) << root.Mean() 00194 << " Len=" << static_cast<int>(stat.m_gameLength.Mean()) 00195 << " Tree=" << setprecision(1) << stat.m_movesInTree.Mean() 00196 << "/" << static_cast<int>(stat.m_movesInTree.Max()) 00197 << " Abrt=" << abortPercent << '%' 00198 << " Gm/s=" << static_cast<int>(stat.m_gamesPerSecond) << '\n'; 00199 } 00200 00201 void GoUctUtil::GfxTerritoryStatistics( 00202 const SgPointArray<SgUctStatistics>& territoryStatistics, 00203 const GoBoard& bd, std::ostream& out) 00204 { 00205 ios_all_saver saver(out); 00206 out << fixed << setprecision(3) << "INFLUENCE"; 00207 for (GoBoard::Iterator it(bd); it; ++it) 00208 if (territoryStatistics[*it].Count() > 0) 00209 // Scale to [-1,+1], black positive 00210 out << ' ' << SgWritePoint(*it) << ' ' 00211 << territoryStatistics[*it].Mean() * 2 - 1; 00212 out << '\n'; 00213 } 00214 00215 void GoUctUtil::SaveTree(const SgUctTree& tree, int boardSize, 00216 const SgBWSet& stones, SgBlackWhite toPlay, 00217 ostream& out, int maxDepth) 00218 { 00219 out << "(;FF[4]GM[1]SZ[" << boardSize << "]\n"; 00220 for (SgBWIterator itColor; itColor; ++itColor) 00221 { 00222 const SgPointSet& stonesColor = stones[*itColor]; 00223 if (stonesColor.Size() == 0) 00224 continue; 00225 out << ((*itColor) == SG_BLACK ? "AB" : "AW"); 00226 for (SgSetIterator it(stonesColor); it; ++it) 00227 out << '[' << PointToSgfString(*it, boardSize, SG_PROPPOINTFMT_GO) 00228 << ']'; 00229 out << '\n'; 00230 } 00231 out << "PL[" << (toPlay == SG_BLACK ? "B" : "W") << "]\n"; 00232 SaveNode(out, tree, tree.Root(), toPlay, boardSize, maxDepth, 0); 00233 out << ")\n"; 00234 } 00235 00236 namespace 00237 { 00238 00239 /** Assist to sort nodes in GoUctUtil::ChildrenStatistics */ 00240 bool IsMeanLess(const SgUctNode* lhs, const SgUctNode* rhs) 00241 { 00242 return (lhs->Mean() < rhs->Mean()); 00243 } 00244 00245 } // namespace 00246 00247 string GoUctUtil::ChildrenStatistics(const SgUctSearch& search, 00248 bool bSort, const SgUctNode& node) 00249 { 00250 ostringstream out; 00251 vector<const SgUctNode*> vec; 00252 const SgUctTree& tree = search.Tree(); 00253 for (SgUctChildIterator it(tree, node); it; ++it) 00254 { 00255 const SgUctNode& child = *it; 00256 vec.push_back(&child); 00257 } 00258 if (bSort) 00259 sort(vec.begin(), vec.end(), IsMeanLess); 00260 for (vector<const SgUctNode*>::iterator it = vec.begin(); it != vec.end(); 00261 ++it) 00262 { 00263 const SgUctNode& child = **it; 00264 out << search.MoveString(child.Move()) << " -" << " value=" 00265 << child.Mean() << " count=" << child.MoveCount() << '\n'; 00266 } 00267 return out.str(); 00268 } 00269 00270 //----------------------------------------------------------------------------