00001 //---------------------------------------------------------------------------- 00002 /** @file GoEyeUtil.h 00003 GoBoard eye-related utility classes. */ 00004 //---------------------------------------------------------------------------- 00005 00006 #ifndef GO_EYEUTIL_H 00007 #define GO_EYEUTIL_H 00008 00009 #include "GoBoard.h" 00010 #include "GoBoardUtil.h" 00011 #include "SgPoint.h" 00012 00013 //---------------------------------------------------------------------------- 00014 00015 namespace GoEyeUtil 00016 { 00017 /** size of largest standard nakade shape */ 00018 const int NAKADE_LIMIT = 6; 00019 00020 /** Code for how many points of each degree there are. 00021 The degree measures how many of the (up to 4) neighbors 00022 are also in the set of points. 00023 00024 code = 1 * # degree 0 00025 + 10 * # degree 1 00026 + 100 * # degree 2 00027 + 1000 * # degree 3 00028 + 10000 * # degree 4 00029 00030 This is a different format, but has the same information 00031 as the Cazenave/Vila "neighbour classification". 00032 E.g. their code 112224 means 2x degree 1, 3x degree 2, 1x degree 4, 00033 so the DegreeCode is 10320. 00034 00035 The DegreeCode is not strong enough for graph isomorphism testing - 00036 there are nonisomorphic graphs with the same code - 00037 but it is good for distinguishing small graphs. 00038 00039 For example, it can not distinguish between a "straight" and a "bent" 00040 line of three. */ 00041 int DegreeCode(const SgPointSet& points); 00042 00043 /** Like DegreeCode, but also count diagonal neighbors */ 00044 long DegreeCode8(const SgPointSet& points); 00045 00046 /** Return an empty neighbor of p. Precondition: one must exist. */ 00047 template<class BOARD> 00048 SgPoint EmptyNeighbor(const BOARD& bd, SgPoint p); 00049 00050 /** Check if area is one of the classical nakade shapes: 00051 *,**,***, **, ***, **, ***, * , * . 00052 * * ** ** *** *** 00053 * ** */ 00054 bool IsNakadeShape(const SgPointSet& area); 00055 00056 /** Check if point is a single point eye with one or two adjacent blocks. 00057 This is a fast eye detection routine, which can be used instead of 00058 Benson's static life detection, when end-of-game detection is a 00059 performance bottle-neck (e.g. for machine-learning or monte-carlo). 00060 It detects single-point eyes surrounded by a single block or by two 00061 blocks that share another single point eye. 00062 Larger eyes can be reduced to simple eyes (assuming chinese rules, 00063 so that playing on does not change the score). 00064 @todo Add example to documentation where this method fails */ 00065 bool IsSimpleEye(const GoBoard& bd, SgPoint p, SgBlackWhite c); 00066 00067 /** */ 00068 bool IsSinglePointEye(const GoBoard& bd, SgPoint p, SgBlackWhite c); 00069 00070 /** Return true if a point can become an eye by adding one more 00071 defender's move */ 00072 bool IsPossibleEye(const GoBoard& board, SgBlackWhite color, SgPoint p); 00073 00074 /** Given opponent's safe stones, can p ever become an eye? 00075 Checks direct and diagonal neighbors. */ 00076 bool CanBecomeSinglePointEye(const GoBoard& board, SgPoint p, 00077 const SgPointSet& oppSafe); 00078 00079 /** does playing at p create one of the standard nakade shapes? */ 00080 template<class BOARD> 00081 bool MakesNakadeShape(const BOARD& bd, SgPoint p, 00082 SgBlackWhite toPlay); 00083 00084 /** Return true if a point can become an eye by adding number of 00085 defender's move. */ 00086 bool NumberOfMoveToEye(const GoBoard& bd, SgBlackWhite c, SgPoint p, 00087 int& number); 00088 00089 /** As IsSinglePointEye, but allows diagonal points to be eyes. 00090 Slightly slower, but identifies more single point eyes. 00091 E.g: 00092 @verbatim 00093 # X X X . . 00094 # O O X X X 00095 # O . O O X 00096 # . O . O X 00097 ########### 00098 @endverbatim */ 00099 bool IsSinglePointEye2(const GoBoard& bd, SgPoint p, SgBlackWhite c); 00100 00101 /** As IsSinglePointEye2, but specifying points assumed to be eyes. */ 00102 bool IsSinglePointEye2(const GoBoard& bd, SgPoint p, 00103 SgBlackWhite c, SgVector<SgPoint>& eyes); 00104 00105 /** p is in a 2 point eye surrounded by a single chain */ 00106 template<class BOARD> 00107 bool IsTwoPointEye(const BOARD& bd, SgPoint p, 00108 SgBlackWhite c); 00109 00110 /** As NumberOfMoveToEye2, but includes existing diagonal eyes, 00111 and allows opponent stones to be captured. */ 00112 bool NumberOfMoveToEye2(const GoBoard& bd, SgBlackWhite c, 00113 SgPoint p, int& nummoves); 00114 00115 /** Count number of single point eyes for block p */ 00116 int CountSinglePointEyes2(const GoBoard& bd, SgPoint p); 00117 00118 /** Does block at p have two or more single point eyes? */ 00119 bool SinglePointSafe2(const GoBoard& bd, SgPoint p); 00120 00121 /** Does removing p split s into two or more parts? */ 00122 bool IsSplitPt(SgPoint p, const SgPointSet& s); 00123 00124 /** Does p locally,within a 3x3 region, split its neighbors in s? 00125 Even if the reply is 'yes', s might still be connected outside 00126 the region. */ 00127 bool IsLocalSplitPt(SgPoint p, const SgPointSet& set); 00128 00129 /** Area is tree shape if it does not contain a 2x2 square. */ 00130 bool IsTreeShape(const SgPointSet& area); 00131 00132 /** Vital point in small shape - usually has most liberties 00133 See implementation for details. */ 00134 bool IsVitalPt(const SgPointSet& points, SgPoint p, SgBlackWhite opp, 00135 const GoBoard& bd); 00136 00137 /** Analyze small region locally for number of eyes. 00138 color: the player surrounding the area. 00139 isNakade: only one eye 00140 makeNakade: attacker can reduce to one eye, defender can live locally. 00141 makeFalse: attacker can make the area into a false eye. 00142 maybeSeki, sureSeki: can there be, or is there, a seki between 00143 boundary stones and interior opponent stones? 00144 @todo: seki support is primitive only. 00145 vital is set iff makeNakade or makeFalse */ 00146 void TestNakade(const SgPointSet& points, const GoBoard& bd, 00147 SgBlackWhite color, bool isFullyEnclosed, bool& isNakade, 00148 bool& makeNakade, bool& makeFalse, bool& maybeSeki, 00149 bool& sureSeki, SgPoint* vital); 00150 00151 bool CheckInterior(const GoBoard& bd, const SgPointSet& area, 00152 SgBlackWhite opp, bool checkBlocks); 00153 } 00154 00155 namespace { 00156 inline bool AreSameBlocks(const SgPoint anchors1[], const SgPoint anchors2[]) 00157 { 00158 int i = 0; 00159 for (; anchors1[i] != SG_ENDPOINT; ++i) 00160 { 00161 if (! GoBoardUtil::ContainsAnchor(anchors2, anchors1[i])) 00162 return false; 00163 } 00164 return (anchors2[i] == SG_ENDPOINT); 00165 } 00166 } 00167 00168 template<class BOARD> 00169 SgPoint GoEyeUtil::EmptyNeighbor(const BOARD& bd, SgPoint p) 00170 { 00171 if (bd.IsEmpty(p + SG_NS)) 00172 return p + SG_NS; 00173 if (bd.IsEmpty(p - SG_NS)) 00174 return p - SG_NS; 00175 if (bd.IsEmpty(p + SG_WE)) 00176 return p + SG_WE; 00177 if (bd.IsEmpty(p - SG_WE)) 00178 return p - SG_WE; 00179 SG_ASSERT(false); 00180 return SG_NULLPOINT; 00181 } 00182 00183 inline bool GoEyeUtil::IsSimpleEye(const GoBoard& bd, SgPoint p, 00184 SgBlackWhite c) 00185 { 00186 // Function is inline despite its large size, because it returns quickly 00187 // on average, which makes the function call an overhead 00188 00189 SgBlackWhite opp = SgOppBW(c); 00190 if (bd.HasEmptyNeighbors(p) || bd.HasNeighbors(p, opp)) 00191 return false; 00192 SgArrayList<SgPoint,2> anchors; 00193 for (SgNb4Iterator it(p); it; ++it) 00194 { 00195 SgPoint nbPoint = *it; 00196 if (bd.IsBorder(nbPoint)) 00197 continue; 00198 SG_ASSERT(bd.GetColor(nbPoint) == c); 00199 SgPoint nbAnchor = bd.Anchor(nbPoint); 00200 if (! anchors.Contains(nbAnchor)) 00201 { 00202 if (anchors.Length() > 1) 00203 return false; 00204 anchors.PushBack(nbAnchor); 00205 } 00206 } 00207 if (anchors.Length() == 1) 00208 return true; 00209 for (GoBoard::LibertyIterator it(bd, anchors[0]); it; ++it) 00210 { 00211 SgPoint lib = *it; 00212 if (lib == p) 00213 continue; 00214 bool isSecondSharedEye = true; 00215 SgArrayList<SgPoint,2> foundAnchors; 00216 for (SgNb4Iterator it2(lib); it2; ++it2) 00217 { 00218 SgPoint nbPoint = *it2; 00219 if (bd.IsBorder(nbPoint)) 00220 continue; 00221 if (bd.GetColor(nbPoint) != c) 00222 { 00223 isSecondSharedEye = false; 00224 break; 00225 } 00226 SgPoint nbAnchor = bd.Anchor(nbPoint); 00227 if (! anchors.Contains(nbAnchor)) 00228 { 00229 isSecondSharedEye = false; 00230 break; 00231 } 00232 if (! foundAnchors.Contains(nbAnchor)) 00233 foundAnchors.PushBack(nbAnchor); 00234 } 00235 if (isSecondSharedEye && foundAnchors.Length() == 2) 00236 return true; 00237 } 00238 return false; 00239 } 00240 00241 template<class BOARD> 00242 bool GoEyeUtil::IsTwoPointEye(const BOARD& bd, SgPoint p, 00243 SgBlackWhite color) 00244 { 00245 const SgBlackWhite opp = SgOppBW(color); 00246 if (bd.NumEmptyNeighbors(p) == 1 00247 && bd.NumNeighbors(p, opp) == 0 00248 ) 00249 { 00250 const SgPoint p2 = GoBoardUtil::FindNeighbor(bd, p, SG_EMPTY); 00251 if ( bd.NumEmptyNeighbors(p2) == 1 00252 && bd.NumNeighbors(p2, opp) == 0 00253 ) 00254 { 00255 // check if p1, p2 are adjacent to the same blocks 00256 SgPoint nbanchorp[4 + 1]; 00257 SgPoint nbanchorp2[4 + 1]; 00258 bd.NeighborBlocks(p, color, nbanchorp); 00259 bd.NeighborBlocks(p2, color, nbanchorp2); 00260 SG_ASSERT(nbanchorp[0] != SG_ENDPOINT); 00261 SG_ASSERT(nbanchorp2[0] != SG_ENDPOINT); 00262 return AreSameBlocks(nbanchorp, nbanchorp2); 00263 } 00264 } 00265 return false; 00266 } 00267 00268 template<class BOARD> 00269 bool GoEyeUtil::MakesNakadeShape(const BOARD& bd, SgPoint p, 00270 SgBlackWhite toPlay) 00271 { 00272 SgPointSet area; 00273 area.Include(p); 00274 int nu = 1; 00275 SgVector<SgPoint> toProcess; 00276 toProcess.PushBack(p); 00277 while (toProcess.NonEmpty()) 00278 { 00279 SgPoint p = toProcess.Back(); 00280 toProcess.PopBack(); 00281 for (SgNb4Iterator it(p); it; ++it) 00282 if (bd.IsColor(*it, toPlay) && ! area.Contains(*it)) 00283 { 00284 area.Include(*it); 00285 toProcess.PushBack(*it); 00286 if (++nu > NAKADE_LIMIT) 00287 return false; 00288 } 00289 } 00290 return IsNakadeShape(area); 00291 } 00292 00293 //---------------------------------------------------------------------------- 00294 00295 #endif // GO_EYEUTIL_H 00296