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