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