00001 //---------------------------------------------------------------------------- 00002 /** @file GoUctUtil.cpp */ 00003 //---------------------------------------------------------------------------- 00004 00005 #include "SgSystem.h" 00006 #include "GoUctUtil.h" 00007 00008 #include <iomanip> 00009 #include <iostream> 00010 #include <boost/io/ios_state.hpp> 00011 #include <boost/format.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::format; 00019 using boost::io::ios_all_saver; 00020 using SgPointUtil::PointToString; 00021 using SgPointUtil::Pt; 00022 using SgPropUtil::PointToSgfString; 00023 00024 //---------------------------------------------------------------------------- 00025 00026 namespace { 00027 00028 bool IsRectEmpty(const GoBoard& bd, int left, int right, int top, int bottom) 00029 { 00030 for (SgRectIterator it(SgRect(left, right, top, bottom)); it; ++it) 00031 if (! bd.IsEmpty(*it)) 00032 return false; 00033 return true; 00034 } 00035 00036 /** Recursive function to save the UCT tree in SGF format. */ 00037 void SaveNode(ostream& out, const SgUctTree& tree, const SgUctNode& node, 00038 SgBlackWhite toPlay, int boardSize, int maxDepth, int depth) 00039 { 00040 out << "C[MoveCount " << node.MoveCount() 00041 << "\nPosCount " << node.PosCount() 00042 << "\nMean " << fixed << setprecision(2) << node.Mean(); 00043 if (! node.HasChildren()) 00044 { 00045 out << "]\n"; 00046 return; 00047 } 00048 out << "\n\nRave:"; 00049 for (SgUctChildIterator it(tree, node); it; ++it) 00050 { 00051 const SgUctNode& child = *it; 00052 SgPoint move = child.Move(); 00053 if (child.HasRaveValue()) 00054 { 00055 out << '\n' << SgWritePoint(move) << ' ' 00056 << fixed << setprecision(2) << child.RaveValue() 00057 << " (" << child.RaveCount() << ')'; 00058 } 00059 } 00060 out << "]\nLB"; 00061 for (SgUctChildIterator it(tree, node); it; ++it) 00062 { 00063 const SgUctNode& child = *it; 00064 if (! child.HasMean()) 00065 continue; 00066 out << "[" 00067 << PointToSgfString(child.Move(), boardSize, SG_PROPPOINTFMT_GO) 00068 << ':' << child.MoveCount() << ']'; 00069 } 00070 out << '\n'; 00071 if (maxDepth >= 0 && depth >= maxDepth) 00072 return; 00073 for (SgUctChildIterator it(tree, node); it; ++it) 00074 { 00075 const SgUctNode& child = *it; 00076 if (! child.HasMean()) 00077 continue; 00078 SgPoint move = child.Move(); 00079 out << "(;" << (toPlay == SG_BLACK ? 'B' : 'W') << '[' 00080 << PointToSgfString(move, boardSize, SG_PROPPOINTFMT_GO) << ']'; 00081 SaveNode(out, tree, child, SgOppBW(toPlay), boardSize, maxDepth, 00082 depth + 1); 00083 out << ")\n"; 00084 } 00085 } 00086 00087 } // namespace 00088 00089 //---------------------------------------------------------------------------- 00090 00091 void GoUctUtil::ClearStatistics(SgPointArray<SgUctStatistics>& stats) 00092 { 00093 for (SgPointArray<SgUctStatistics>::NonConstIterator 00094 it(stats); it; ++it) 00095 (*it).Clear(); 00096 } 00097 00098 SgPoint GoUctUtil::GenForcedOpeningMove(const GoBoard& bd) 00099 { 00100 int sz = bd.Size(); 00101 if (sz < 13 || bd.TotalNumStones(SG_BLACK) > 5 00102 || bd.TotalNumStones(SG_WHITE) > 5) 00103 return SG_NULLMOVE; 00104 SgArrayList<SgPoint,4> moves; 00105 if (IsRectEmpty(bd, 1, 5, 1, 5)) 00106 moves.PushBack(Pt(4, 4)); 00107 if (IsRectEmpty(bd, 1, 5, sz - 4, sz)) 00108 moves.PushBack(Pt(4, sz - 3)); 00109 if (IsRectEmpty(bd, sz - 4, sz, 1, 5)) 00110 moves.PushBack(Pt(sz - 3, 4)); 00111 if (IsRectEmpty(bd, sz - 4, sz, sz - 4, sz)) 00112 moves.PushBack(Pt(sz - 3, sz - 3)); 00113 if (moves.IsEmpty()) 00114 return SG_NULLMOVE; 00115 return moves[SgRandom::Global().SmallInt(moves.Length())]; 00116 } 00117 00118 void GoUctUtil::GfxBestMove(const SgUctSearch& search, SgBlackWhite toPlay, 00119 ostream& out) 00120 { 00121 const SgUctTree& tree = search.Tree(); 00122 const SgUctNode& root = tree.Root(); 00123 out << "VAR"; 00124 const SgUctNode* bestValueChild = search.FindBestChild(root); 00125 if (bestValueChild != 0) 00126 { 00127 SgPoint move = bestValueChild->Move(); 00128 out << ' ' << (toPlay == SG_BLACK ? 'B' : 'W') << ' ' 00129 << SgWritePoint(move); 00130 } 00131 out << '\n'; 00132 } 00133 00134 void GoUctUtil::GfxCounts(const SgUctTree& tree, ostream& out) 00135 { 00136 const SgUctNode& root = tree.Root(); 00137 out << "LABEL"; 00138 if (root.HasChildren()) 00139 for (SgUctChildIterator it(tree, root); it; ++it) 00140 { 00141 const SgUctNode& child = *it; 00142 if (child.HasMean()) 00143 out << (format(" %s %.0f") 00144 % PointToString(child.Move()) % child.MoveCount()); 00145 } 00146 out << '\n'; 00147 } 00148 00149 void GoUctUtil::GfxMoveValues(const SgUctSearch& search, SgBlackWhite toPlay, 00150 ostream& out) 00151 { 00152 const SgUctTree& tree = search.Tree(); 00153 const SgUctNode& root = tree.Root(); 00154 out << "INFLUENCE"; 00155 if (root.HasChildren()) 00156 for (SgUctChildIterator it(tree, root); it; ++it) 00157 { 00158 const SgUctNode& child = *it; 00159 if (! child.HasMean()) 00160 continue; 00161 SgUctValue value = SgUctSearch::InverseEval(child.Mean()); 00162 // Scale to [-1,+1], black positive 00163 SgUctValue influence = value * 2 - 1; 00164 if (toPlay == SG_WHITE) 00165 influence *= -1; 00166 SgPoint move = child.Move(); 00167 out << ' ' << SgWritePoint(move) << ' ' << fixed 00168 << setprecision(2) << influence; 00169 } 00170 out << '\n'; 00171 } 00172 \ 00173 void GoUctUtil::GfxSequence(const SgUctSearch& search, SgBlackWhite toPlay, 00174 ostream& out) 00175 { 00176 vector<SgMove> sequence; 00177 search.FindBestSequence(sequence); 00178 out << "VAR"; 00179 for (size_t i = 0; i < sequence.size(); ++i) 00180 { 00181 out << (toPlay == SG_BLACK ? " B ": " W ") 00182 << SgWritePoint(sequence[i]); 00183 toPlay = SgOppBW(toPlay); 00184 } 00185 out << '\n'; 00186 } 00187 00188 void GoUctUtil::GfxStatus(const SgUctSearch& search, ostream& out) 00189 { 00190 const SgUctTree& tree = search.Tree(); 00191 const SgUctNode& root = tree.Root(); 00192 const SgUctSearchStat& stat = search.Statistics(); 00193 SgUctValue abortPercent = stat.m_aborted.Mean() * SgUctValue(100); 00194 out << (format( 00195 "TEXT N=%.0f V=%.2f Len=%.0f Tree=%.1f/%.1f Abrt=%.0f%% Gm/s=%.0f\n") 00196 % root.MoveCount() % root.Mean() % stat.m_gameLength.Mean() 00197 % stat.m_movesInTree.Mean() % stat.m_movesInTree.Max() 00198 % abortPercent % stat.m_gamesPerSecond); 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 //----------------------------------------------------------------------------