Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoEyeUtil.h

Go to the documentation of this file.
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 


17 Jun 2010 Doxygen 1.4.7