00001 //---------------------------------------------------------------------------- 00002 /** @file GoUctDefaultPriorKnowledge.cpp 00003 See GoUctDefaultPriorKnowledge.h 00004 */ 00005 //---------------------------------------------------------------------------- 00006 00007 #include "SgSystem.h" 00008 #include "GoUctDefaultPriorKnowledge.h" 00009 00010 using namespace std; 00011 00012 //---------------------------------------------------------------------------- 00013 00014 namespace { 00015 00016 bool SetsAtari(const GoBoard& bd, SgPoint p) 00017 { 00018 SG_ASSERT(bd.IsEmpty(p)); // Already checked 00019 SgBlackWhite opp = SgOppBW(bd.ToPlay()); 00020 if (bd.NumNeighbors(p, opp) == 0) 00021 return false; 00022 if (! bd.IsBorder(p + SG_NS) && bd.GetColor(p + SG_NS) == opp 00023 && bd.NumLiberties(p + SG_NS) == 2) 00024 return true; 00025 if (! bd.IsBorder(p - SG_NS) && bd.GetColor(p - SG_NS) == opp 00026 && bd.NumLiberties(p - SG_NS) == 2) 00027 return true; 00028 if (! bd.IsBorder(p + SG_WE) && bd.GetColor(p + SG_WE) == opp 00029 && bd.NumLiberties(p + SG_WE) == 2) 00030 return true; 00031 if (! bd.IsBorder(p - SG_WE) && bd.GetColor(p - SG_WE) == opp 00032 && bd.NumLiberties(p - SG_WE) == 2) 00033 return true; 00034 return false; 00035 } 00036 00037 } // namespace 00038 00039 //---------------------------------------------------------------------------- 00040 00041 GoUctKnowledge::GoUctKnowledge(const GoBoard& bd) 00042 : m_bd(bd) 00043 { 00044 } 00045 00046 GoUctKnowledge::~GoUctKnowledge() 00047 { 00048 } 00049 00050 void GoUctKnowledge::Add(SgPoint p, float value, size_t count) 00051 { 00052 m_values[p].Add(value, count); 00053 } 00054 00055 void GoUctKnowledge::Initialize(SgPoint p, float value, size_t count) 00056 { 00057 m_values[p].Initialize(value, count); 00058 } 00059 00060 void GoUctKnowledge::ClearValues() 00061 { 00062 for (std::size_t i = 0; i < SG_PASS+1; ++i) 00063 m_values[i].Clear(); 00064 } 00065 00066 void GoUctKnowledge::TransferValues(std::vector<SgMoveInfo>& outmoves) const 00067 { 00068 for (std::size_t i = 0; i < outmoves.size(); ++i) 00069 { 00070 SgMove p = outmoves[i].m_move; 00071 if (m_values[p].IsDefined()) 00072 { 00073 outmoves[i].m_count = m_values[p].Count(); 00074 outmoves[i].m_value = 00075 SgUctSearch::InverseEval(m_values[p].Mean()); 00076 outmoves[i].m_raveCount = m_values[p].Count(); 00077 outmoves[i].m_raveValue = m_values[p].Mean(); 00078 } 00079 } 00080 } 00081 00082 //---------------------------------------------------------------------------- 00083 00084 GoUctDefaultPriorKnowledge::GoUctDefaultPriorKnowledge(const GoBoard& bd, 00085 const GoUctPlayoutPolicyParam& param) 00086 : GoUctKnowledge(bd), 00087 m_policy(bd, param) 00088 { 00089 } 00090 00091 void GoUctDefaultPriorKnowledge::AddLocalityBonus(GoPointList& emptyPoints, 00092 bool isSmallBoard) 00093 { 00094 SgPoint last = m_bd.GetLastMove(); 00095 if (last != SG_NULLMOVE && last != SG_PASS) 00096 { 00097 SgPointArray<int> dist = GoBoardUtil::CfgDistance(m_bd, last, 3); 00098 const size_t count = (isSmallBoard ? 4 : 5); 00099 for (GoPointList::Iterator it(emptyPoints); it; ++it) 00100 { 00101 const SgPoint p = *it; 00102 switch (dist[p]) 00103 { 00104 case 1: 00105 Add(p, 1.0, count); 00106 break; 00107 case 2: 00108 Add(p, 0.6, count); 00109 break; 00110 case 3: 00111 Add(p, 0.6, count); 00112 break; 00113 default: 00114 Add(p, 0.1, count); 00115 break; 00116 } 00117 } 00118 Add(SG_PASS, 0.1, count); 00119 } 00120 } 00121 00122 /** Find global moves that match a playout pattern or set a block into atari. 00123 @param[out] pattern 00124 @param[out] atari 00125 @param[out] empty As a side effect, this function finds all empty points 00126 on the board 00127 @return @c true if any such moves was found 00128 */ 00129 bool GoUctDefaultPriorKnowledge::FindGlobalPatternAndAtariMoves( 00130 SgPointSet& pattern, 00131 SgPointSet& atari, 00132 GoPointList& empty) const 00133 { 00134 SG_ASSERT(empty.IsEmpty()); 00135 const GoUctPatterns<GoBoard>& patterns = m_policy.Patterns(); 00136 bool result = false; 00137 for (GoBoard::Iterator it(m_bd); it; ++it) 00138 if (m_bd.IsEmpty(*it)) 00139 { 00140 empty.PushBack(*it); 00141 if (patterns.MatchAny(*it)) 00142 { 00143 pattern.Include(*it); 00144 result = true; 00145 } 00146 if (SetsAtari(m_bd, *it)) 00147 { 00148 atari.Include(*it); 00149 result = true; 00150 } 00151 } 00152 return result; 00153 } 00154 00155 void 00156 GoUctDefaultPriorKnowledge::ProcessPosition(std::vector<SgMoveInfo>& outmoves) 00157 { 00158 m_policy.StartPlayout(); 00159 m_policy.GenerateMove(); 00160 GoUctPlayoutPolicyType type = m_policy.MoveType(); 00161 bool isFullBoardRandom = 00162 (type == GOUCT_RANDOM || type == GOUCT_FILLBOARD); 00163 SgPointSet pattern; 00164 SgPointSet atari; 00165 GoPointList empty; 00166 bool anyHeuristic = FindGlobalPatternAndAtariMoves(pattern, atari, empty); 00167 00168 // The initialization values/counts are mainly tuned by selfplay 00169 // experiments and games vs MoGo Rel 3 and GNU Go 3.6 on 9x9 and 19x19. 00170 // If different values are used for the small and large board, the ones 00171 // from the 9x9 experiments are used for board sizes < 15, the ones from 00172 // 19x19 otherwise. 00173 const bool isSmallBoard = (m_bd.Size() < 15); 00174 00175 Initialize(SG_PASS, 0.1, isSmallBoard ? 9 : 18); 00176 if (isFullBoardRandom && ! anyHeuristic) 00177 { 00178 for (GoBoard::Iterator it(m_bd); it; ++it) 00179 { 00180 SgPoint p = *it; 00181 if (! m_bd.IsEmpty(p)) 00182 continue; 00183 if (GoBoardUtil::SelfAtari(m_bd, *it)) 00184 Initialize(*it, 0.1, isSmallBoard ? 9 : 18); 00185 else 00186 m_values[p].Clear(); // Don't initialize 00187 } 00188 } 00189 else if (isFullBoardRandom && anyHeuristic) 00190 { 00191 for (GoBoard::Iterator it(m_bd); it; ++it) 00192 { 00193 SgPoint p = *it; 00194 if (! m_bd.IsEmpty(p)) 00195 continue; 00196 if (GoBoardUtil::SelfAtari(m_bd, *it)) 00197 Initialize(*it, 0.1, isSmallBoard ? 9 : 18); 00198 else if (atari[*it]) 00199 Initialize(*it, 1.0, 3); 00200 else if (pattern[*it]) 00201 Initialize(*it, 0.9, 3); 00202 else 00203 Initialize(*it, 0.5, 3); 00204 } 00205 } 00206 else 00207 { 00208 for (GoBoard::Iterator it(m_bd); it; ++it) 00209 { 00210 SgPoint p = *it; 00211 if (! m_bd.IsEmpty(p)) 00212 continue; 00213 if (GoBoardUtil::SelfAtari(m_bd, *it)) 00214 Initialize(*it, 0.1, isSmallBoard ? 9 : 18); 00215 else if (atari[*it]) 00216 Initialize(*it, 0.8, isSmallBoard ? 9 : 18); 00217 else if (pattern[*it]) 00218 Initialize(*it, 0.6, isSmallBoard ? 9 : 18); 00219 else 00220 Initialize(*it, 0.4, isSmallBoard ? 9 : 18); 00221 } 00222 GoPointList moves = m_policy.GetEquivalentBestMoves(); 00223 for (GoPointList::Iterator it(moves); it; ++it) 00224 Initialize(*it, 1.0, isSmallBoard ? 9 : 18); 00225 } 00226 AddLocalityBonus(empty, isSmallBoard); 00227 m_policy.EndPlayout(); 00228 00229 TransferValues(outmoves); 00230 } 00231 00232 //----------------------------------------------------------------------------