Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoEyeUtil.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoEyeUtil.cpp
00003     See GoEyeUtil.h */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgSystem.h"
00007 #include "GoEyeUtil.h"
00008 
00009 #include "GoBoardUtil.h"
00010 #include "GoEyeCount.h"
00011 #include "SgNbIterator.h"
00012 
00013 //----------------------------------------------------------------------------
00014 namespace
00015 {
00016 
00017 /** Count number of points on edge of board (Line 1) */
00018 int NuEdgePoints(const GoBoard& bd, const SgPointSet& points)
00019 {
00020     return (points & bd.LineSet(1)).Size();
00021 }
00022 
00023 /** Recognizes 2x2 block of points.
00024     Relies on the current implementation 
00025     where SgSetIterator produces set members in sorted order,
00026     such that bulky four points have values p, p+WE, p+NS, p+WE+NS */
00027 bool IsBulkyFour(const SgPointSet& points)
00028 {
00029     SG_ASSERT(points.IsSize(4));
00030     SgSetIterator it(points); 
00031     SgPoint p1 = *it;
00032     ++it;
00033     if (*it != p1 + SG_WE)
00034         return false;
00035     ++it;
00036     if (*it != p1 + SG_NS)
00037         return false;
00038     ++it;
00039     if (*it != p1 + SG_WE + SG_NS)
00040         return false;
00041     SG_ASSERT(GoEyeUtil::DegreeCode(points) == 400);
00042     return true;
00043 }
00044 
00045 bool IsTShape(const SgPointSet& block)
00046 {
00047     return GoEyeUtil::DegreeCode(block) == 1030;
00048 }
00049 
00050 bool IsBulkyFive(const SgPointSet& block)
00051 {
00052     return GoEyeUtil::DegreeCode(block) == 1310;
00053 }
00054 
00055 bool IsCross(const SgPointSet& block)
00056 {
00057     return GoEyeUtil::DegreeCode(block) == 10040;
00058 }
00059 
00060 bool IsRabbitySix(const SgPointSet& block)
00061 {
00062     return GoEyeUtil::DegreeCode(block) == 10320;
00063 }
00064         
00065 bool Is2x3Area(const SgPointSet& area)
00066 {
00067     return GoEyeUtil::DegreeCode(area) == 2400;
00068 }
00069         
00070 /** Block has a shape that gives the opponent two eyes,
00071     if it gets captured by a single opponent surrounding block.
00072     @todo needs to check if all points in block 
00073     are adjacent to defender's stones - to avoid bent 4, 3x2 in corner etc. */
00074 bool IsAliveBlock(const SgPointSet& block)
00075 {
00076     const int code = GoEyeUtil::DegreeCode(block);
00077     
00078     return code == 220 // stretched 4 in a row (not alive in bent four)
00079         || code == 320 // streched 5 in a row
00080         || code == 1130 // T-shape or L-plus-foot
00081         || code == 2400 // solid 2x3 block (not alive in Corner)
00082         || code == 2220 // alive 6 point - not rabbity six
00083         ;
00084 }
00085 
00086 bool CheckAlwaysAlive8Code(long code)
00087 {
00088     return    code == 11210 // T
00089            || code == 3110 // L with foot
00090            ;
00091 }
00092 
00093 /** Block has a shape that gives the opponent two eyes,
00094     and cannot be extended into a nakade shape.
00095     @todo needs to check if all points in block 
00096     are adjacent to defender's stones - to avoid bent 4, 3x2 in corner etc. */
00097 bool IsAlwaysAliveBlock(const SgPointSet& block)
00098 {
00099     const int code = GoEyeUtil::DegreeCode(block);
00100     
00101     return code == 320 // streched 5 in a row
00102         || (   code == 1130 // T-shape or L-plus-foot
00103             && CheckAlwaysAlive8Code(GoEyeUtil::DegreeCode8(block))
00104            )
00105         ;
00106 }
00107 
00108 /** Is single block, and has one of the standard nakade shapes */
00109 bool IsNakadeBlock(const GoBoard& bd, 
00110                    const SgPointSet& block)
00111 {
00112     if (! GoEyeUtil::IsNakadeShape(block))
00113         return false;
00114     return bd.NumStones(block.PointOf()) == block.Size();
00115 }
00116 
00117 
00118 /** area is all filled by stones, except for one empty point
00119     These stones are in an alive shape (two eyes for opponent). */
00120 bool AlmostFilledByLivingShape(const GoBoard& bd, 
00121                           const SgPointSet& points,
00122                           SgBlackWhite stoneColor)
00123 {
00124     // area must be surrounded by opponent.
00125     SG_ASSERT(points.Border(bd.Size()).SubsetOf(bd.All(SgOppBW(stoneColor))));
00126 
00127     return   (points & bd.AllEmpty()).IsSize(1)
00128           && IsAliveBlock(points & bd.All(stoneColor));
00129 }
00130 
00131 /** Area contains stones in an alive shape (two eyes for opponent).
00132     It cannot be extended into a nakade shape. */
00133 bool ContainsLivingShape(const GoBoard& bd, 
00134                           const SgPointSet& points,
00135                           SgBlackWhite stoneColor)
00136 {
00137     // area must be surrounded by opponent.
00138     SG_ASSERT(points.Border(bd.Size()).SubsetOf(bd.All(SgOppBW(stoneColor))));
00139 
00140     return IsAlwaysAliveBlock(points & bd.All(stoneColor));
00141 }
00142 
00143 /** area is all filled by stones, except for one empty point.
00144     These stones are in a nakade shape (only one eye). */
00145 bool AlmostFilledByNakade(const GoBoard& bd, 
00146                           const SgPointSet& points,
00147                           SgBlackWhite stoneColor)
00148 {
00149     // area must be surrounded by opponent.
00150     SG_ASSERT(points.Border(bd.Size()).SubsetOf(bd.All(SgOppBW(stoneColor))));
00151 
00152     if ((points & bd.AllEmpty()).IsSize(1))
00153     {
00154         return IsNakadeBlock(bd, points & bd.All(stoneColor));
00155     }
00156     return false;
00157 }
00158 
00159 /** Test for the case of a 3 stone block in a bulky 5 shape, which is not
00160     handled well by the standard heuristics.
00161     It is almost always nakade, except when one empty point is not
00162     a liberty of the block. */
00163 GoEyeStatus BulkyFiveNakade(const GoBoard& bd, 
00164                             const SgPointSet& points,
00165                             SgBlackWhite stoneColor)
00166 {
00167     const SgPointSet stones = points & bd.All(stoneColor);
00168     if (   IsBulkyFive(points) 
00169         && stones.IsSize(3)
00170        )
00171     {
00172         const SgPoint p = stones.PointOf();
00173         if (bd.NumStones(p) == 3) // single block
00174         // check if both empty points adjacent to block. if yes, nakade.
00175         // if no, alive.
00176         {
00177             SG_ASSERT((points & bd.AllEmpty()).IsSize(2));
00178             for (SgSetIterator it(points & bd.AllEmpty()); it; ++it)
00179             {
00180                 if (bd.NumNeighbors(*it, stoneColor) == 0)
00181                 {
00182                     return EYE_ONE_AND_HALF;
00183                 }
00184             }
00185             return EYE_ONE;
00186         }
00187         else // 2 blocks (2 eyes) or 3 blocks (unsettled)
00188         {
00189             return GoEyeUtil::DegreeCode(stones) == 3 ? 
00190                  EYE_ONE_AND_HALF : // 3x degree 0 = 3 single stones
00191                  EYE_TWO; // 2 separate blocks in bulky five are alive shapes
00192         }
00193 
00194     }
00195     return EYE_UNKNOWN;
00196 }
00197 
00198 /** Exceptional 2x3 areas that are not handled correctly by heuristics */
00199 GoEyeStatus Special2x3Cases(const GoBoard& bd, 
00200                             const SgPointSet& points,
00201                             SgBlackWhite stoneColor)
00202 { 
00203     const SgPointSet stones = points & bd.All(stoneColor);
00204     const int nuStones = stones.Size();
00205     if (nuStones > 0)
00206     {
00207         const int code = GoEyeUtil::DegreeCode(stones);
00208         const long code8 = GoEyeUtil::DegreeCode8(stones);
00209         // oo.
00210         // .oo
00211         if (code == 220 && code8 == 2200)
00212             return EYE_ONE_AND_HALF;
00213         // oo.
00214         // ..o
00215         if (code == 21 && code8 == 120)
00216             return EYE_TWO;
00217         if (NuEdgePoints(bd, points) == 3)
00218         {
00219             const SgPoint p = stones.PointOf();
00220             switch (nuStones)
00221             {
00222             case 1: // o.. Single stone on edge can kill by diagonal move
00223                      // ...
00224                 if (bd.Line(p) == 1
00225                     && bd.HasNeighbors(p, SgOppBW(stoneColor)))
00226                     return EYE_ONE_AND_HALF;
00227                 break;
00228             case 2: // o.o 1.5 eyes. B can make sente seki, W can kill
00229                 // ...
00230                 if (   bd.Line(p) == 1
00231                        && code == 2
00232                        && NuEdgePoints(bd, stones) == 2
00233                     )
00234                     return EYE_ONE_AND_HALF;
00235                 // o.. followup from case 1: only 1 eye
00236                 // .o.
00237                 if (   code == 2
00238                     && code8 == 20
00239                        )
00240                 {
00241                     const SgPoint p1 = (stones & bd.LineSet(1)).PointOf();
00242                     if (bd.HasNeighbors(p1, SgOppBW(stoneColor)))
00243                         return EYE_ONE;
00244                 }
00245                 break;
00246             case 3 : // o.o 1 eye
00247                      // .o.
00248                 if (   code == 3
00249                     && code8 == 120
00250                     )
00251                 {
00252                     const SgPoint p1 = (stones & bd.LineSet(1)).PointOf();
00253                     if (bd.HasNeighbors(p1, SgOppBW(stoneColor)))
00254                         return EYE_ONE;
00255                 }
00256                 break;
00257             }
00258         }
00259     }
00260     return EYE_UNKNOWN; // no special case. Default rules work, just use them.
00261 }
00262 
00263 /** is p+ns, p+we locally split? */
00264 inline bool TestDiagonal(const SgPointSet& set, SgPoint p, int ns, int we)
00265 { 
00266     return     ! set[p+ns+we] // connected the short way
00267             && ! (   set[p+ns-we]
00268                   && set[p-we]
00269                   && set[p-ns-we] 
00270                   && set[p-ns]
00271                   && set[p-ns+we]
00272                  ); // connected the long way
00273 }
00274 
00275 /** is p+ns, p-ns locally split? */
00276 inline bool TestOpposite(const SgPointSet& set, SgPoint p, int ns, int we)
00277 {
00278     return    ! (set[p-ns-we] && set[p-we] && set[p+ns-we])  // connected west
00279            && ! (set[p-ns+we] && set[p+we] && set[p+ns+we]); // connected east
00280 }
00281 
00282 
00283 /** Check for bent four in the corner. brute force check of all eight cases. 
00284     @todo rewrite using the pattern symmetry functions */
00285 bool IsBentFour(const SgPointSet& points, int boardSize, SgPoint* vital)
00286 {
00287     SG_ASSERT(points.IsSize(4));
00288 
00289     if (    points.Contains(SgPointUtil::Pt(1, 1))
00290          && points.Contains(SgPointUtil::Pt(2, 1))
00291          && points.Contains(SgPointUtil::Pt(1, 2))
00292        )
00293     {
00294         if (points.Contains(SgPointUtil::Pt(3, 1)))
00295         {
00296             *vital = SgPointUtil::Pt(2, 1);
00297             return true;
00298         }
00299         else if (points.Contains(SgPointUtil::Pt(1, 3)))
00300         {
00301             *vital = SgPointUtil::Pt(1, 2);
00302             return true;
00303         }
00304     }
00305     if (    points.Contains(SgPointUtil::Pt(1, boardSize))
00306          && points.Contains(SgPointUtil::Pt(2, boardSize))
00307          && points.Contains(SgPointUtil::Pt(1, boardSize - 1))
00308        )
00309     {
00310         if (points.Contains(SgPointUtil::Pt(3, boardSize)))
00311         {
00312             *vital = SgPointUtil::Pt(2, boardSize);
00313             return true;
00314         }
00315         else if (points.Contains(SgPointUtil::Pt(1, boardSize - 2)))
00316         {
00317             *vital = SgPointUtil::Pt(1, boardSize - 1);
00318             return true;
00319         }
00320     }
00321     if (    points.Contains(SgPointUtil::Pt(boardSize, 1))
00322          && points.Contains(SgPointUtil::Pt(boardSize - 1, 1))
00323          && points.Contains(SgPointUtil::Pt(boardSize, 2))
00324        )
00325     {
00326         if (points.Contains(SgPointUtil::Pt(boardSize - 2, 1)))
00327         {
00328             *vital = SgPointUtil::Pt(boardSize - 1, 1);
00329             return true;
00330         }
00331         else if (points.Contains(SgPointUtil::Pt(boardSize, 3)))
00332         {
00333             *vital = SgPointUtil::Pt(boardSize, 2);
00334             return true;
00335         }
00336     }
00337     if (    points.Contains(SgPointUtil::Pt(boardSize, boardSize))
00338          && points.Contains(SgPointUtil::Pt(boardSize - 1, boardSize))
00339          && points.Contains(SgPointUtil::Pt(boardSize, boardSize - 1))
00340        )
00341     {
00342         if (points.Contains(SgPointUtil::Pt(boardSize - 2, boardSize)))
00343         {
00344             *vital = SgPointUtil::Pt(boardSize - 1, boardSize);
00345             return true;
00346         }
00347         else if (points.Contains(SgPointUtil::Pt(boardSize, boardSize - 2)))
00348         {
00349             *vital = SgPointUtil::Pt(boardSize, boardSize - 1);
00350             return true;
00351         }
00352     }
00353     
00354     return false;   
00355 }
00356 
00357 /** The pattern with 2 diagonal stones is the only one that is unsettled.
00358     All others are nakade - only 1 eye */
00359 bool TwoDiagonalStonesInBulkyFour(const GoBoard& bd, 
00360                                   const SgPointSet& points,
00361                                   SgBlackWhite stoneColor)
00362 {
00363     // bulky four area must be surrounded by opponent.
00364     SG_ASSERT(points.Border(bd.Size()).SubsetOf(bd.All(SgOppBW(stoneColor))));
00365     
00366     if ((points & bd.All(stoneColor)).IsSize(2))
00367     {
00368         //2 stones, 2 empty
00369         SG_ASSERT((points & bd.AllEmpty()).IsSize(2));
00370         
00371         const SgPoint p = points.PointOf();
00372         if (bd.IsEmpty(p))
00373             return bd.NumNeighbors(p, stoneColor) == 2;
00374         else
00375             return bd.NumEmptyNeighbors(p) == 2;
00376     }
00377     return false;
00378 }
00379 
00380 inline bool ProcessStatus(GoEyeStatus status,
00381                           bool& isNakade,
00382                           bool& makeNakade)
00383 {
00384     if (status == EYE_ONE)
00385         isNakade = true;
00386     else if (status == EYE_ONE_AND_HALF)
00387         makeNakade = true;
00388     return status != EYE_UNKNOWN;
00389 }
00390 } // namespace
00391 //----------------------------------------------------------------------------
00392 
00393 int GoEyeUtil::DegreeCode(const SgPointSet& points)
00394 {
00395     int degrees[5] = {0,0,0,0,0};
00396     
00397     for (SgSetIterator it(points); it; ++it)
00398     {
00399         const SgPoint p(*it);
00400         int nbs = 0;
00401         for (SgNb4Iterator it(p); it; ++it)
00402         {
00403             if (points.Contains(*it))
00404                 ++nbs;
00405         }
00406         ++(degrees[nbs]);
00407     }
00408     return          degrees[0] 
00409              + 10 * degrees[1]
00410             + 100 * degrees[2]
00411            + 1000 * degrees[3]
00412           + 10000 * degrees[4];
00413 }
00414 
00415 long GoEyeUtil::DegreeCode8(const SgPointSet& points)
00416 {
00417     int degrees[9] = {0,0,0,0,0};
00418     
00419     for (SgSetIterator it(points); it; ++it)
00420     {
00421         const SgPoint p(*it);
00422         int nbs = 0;
00423         for (SgNb8Iterator it(p); it; ++it)
00424         {
00425             if (points.Contains(*it))
00426                 ++nbs;
00427         }
00428         ++(degrees[nbs]);
00429     }
00430     return              degrees[0] 
00431                  + 10 * degrees[1]
00432                 + 100 * degrees[2]
00433                + 1000 * degrees[3]
00434               + 10000 * degrees[4]
00435              + 100000 * degrees[5]
00436             + 1000000 * degrees[6]
00437            + 10000000 * degrees[7]
00438           + 100000000 * degrees[8];
00439 }
00440 
00441 bool GoEyeUtil::IsNakadeShape(const SgPointSet& area)
00442 {
00443     switch (area.Size())
00444     {
00445         case 1:
00446         case 2:
00447         case 3: return true;
00448         case 4: return IsBulkyFour(area) || IsTShape(area);
00449         case 5: return IsBulkyFive(area) || IsCross(area);
00450         case 6: return IsRabbitySix(area);
00451         default: // too big
00452             return false;
00453     }
00454 }
00455 
00456 bool GoEyeUtil::IsSinglePointEye(const GoBoard& bd, SgPoint p,
00457                                  SgBlackWhite color)
00458 {
00459     SG_ASSERT(bd.IsEmpty(p));
00460     const SgBlackWhite opp = SgOppBW(color);
00461     if (bd.HasEmptyNeighbors(p) || bd.HasNeighbors(p, opp))
00462         return false;
00463     if (bd.Line(p) == 1)
00464         return ! (bd.HasDiagonals(p, SG_EMPTY) || bd.HasDiagonals(p, opp));
00465     return (bd.NumDiagonals(p, SG_EMPTY) + bd.NumDiagonals(p, opp)) <= 1;
00466 }
00467 
00468 bool GoEyeUtil::IsPossibleEye(const GoBoard& board, SgBlackWhite color,
00469                               SgPoint p)
00470 {
00471     bool isPossibleEye = false;
00472     SG_ASSERT(board.GetColor(p) != color);
00473     const SgBlackWhite opp = SgOppBW(color);
00474     if (board.Line(p) == 1) // corner or edge
00475     {
00476         const int nuOwn = (board.Pos(p) == 1) ? 2 : 4;
00477         if ( board.Num8Neighbors(p, color) == nuOwn
00478              && board.Num8EmptyNeighbors(p) == 1
00479            )
00480         {     
00481             isPossibleEye = true;
00482         }
00483     }
00484     else // in center
00485     {
00486         // have all neighbors, and 2 diagonals, and can get a third
00487         if (    board.NumNeighbors(p, color) == 4
00488              && board.NumDiagonals(p, color) == 2
00489              && board.NumEmptyDiagonals(p) > 0
00490            )
00491         {     
00492             isPossibleEye = true;
00493         }
00494         // have 3 of 4 neighbors, can get the 4th, and have enough diagonals
00495         else if (   board.NumNeighbors(p, color) == 3
00496                  && board.NumNeighbors(p, opp) == 0
00497                  && board.NumDiagonals(p, color) >= 3 
00498                 )
00499         {
00500             isPossibleEye = true;
00501         }
00502     }
00503     
00504     return isPossibleEye;
00505 }
00506 
00507 bool GoEyeUtil::NumberOfMoveToEye(const GoBoard& board, SgBlackWhite color,
00508                                   SgPoint p, int& number)
00509 {
00510     SG_ASSERT(board.IsEmpty(p));
00511     SgBlackWhite att = SgOppBW(color);  // attacker
00512 
00513     if ( board.Line(p) == 1) // corner or edge
00514     {
00515         if ( board.Num8Neighbors(p, att) > 0 )
00516             return false;
00517         else
00518         {
00519             number = board.Num8EmptyNeighbors(p);
00520             return true;
00521         }
00522     }
00523     else // in center
00524     {
00525         if ( board.Num8Neighbors(p, att) >= 2 )
00526             return false;
00527         else if ( board.NumNeighbors(p, att) > 0 )
00528             return false;
00529         else // only 0 or 1 attacker point and not in NB4
00530         {
00531             number = board.Num8EmptyNeighbors(p);
00532             return true;
00533         }
00534     }
00535     
00536 }
00537 
00538 bool GoEyeUtil::IsSinglePointEye2(const GoBoard& board, SgPoint p, 
00539                                   SgBlackWhite color, SgVector<SgPoint>& eyes)
00540 {
00541     // Must be an empty point
00542     if (! board.IsColor(p, SG_EMPTY))
00543         return false;
00544     // All adjacent neighbours must be friendly
00545     SgBoardColor opp = SgOppBW(color);
00546     for (SgNb4Iterator adj(p); adj; ++adj)
00547     {
00548         SgBoardColor adjColor = board.GetColor(*adj);
00549         if (adjColor == opp || adjColor == SG_EMPTY)
00550             return false;
00551     }
00552     // All diagonals (with up to one exception) must be friendly or an eye
00553     int baddiags = 0;
00554     int maxbaddiags = (board.Line(p) == 1 ? 0 : 1);
00555     for (SgNb4DiagIterator it(p); it; ++it)
00556     {
00557         if (board.IsColor(*it, opp))
00558             ++baddiags;
00559         if (board.IsColor(*it, SG_EMPTY) && ! eyes.Contains(*it))
00560         {
00561             // Assume this point is an eye and recurse
00562             eyes.PushBack(p);
00563             if (! IsSinglePointEye2(board, *it, color, eyes))
00564                 ++baddiags;
00565             eyes.PopBack();
00566         }
00567         if (baddiags > maxbaddiags)
00568             return false;
00569     }
00570     return true;
00571 }
00572 
00573 bool GoEyeUtil::IsSinglePointEye2(const GoBoard& board, SgPoint p, 
00574                                   SgBlackWhite color)
00575 {
00576     SgVector<SgPoint> emptylist;
00577     return IsSinglePointEye2(board, p, color, emptylist);
00578 }
00579 
00580 bool GoEyeUtil::NumberOfMoveToEye2(const GoBoard& board, SgBlackWhite color,
00581                                    SgPoint p, int& nummoves)
00582 {
00583     nummoves = 0;
00584     bool capturing = false;
00585     SgVector<SgPoint> usedpoints;
00586     usedpoints.PushBack(p);
00587     SgPointSet counted;
00588 
00589     // Can never turn own stone into an eye
00590     if (board.IsColor(p, color))
00591         return false;
00592     
00593     // If opponent stone then it must be captured to make eye
00594     if (board.IsColor(p, SgOppBW(color)))
00595     {
00596         capturing = true;
00597     
00598         // If it is obviously safe then it can never be an eye
00599         if (SinglePointSafe2(board, p)) // Quick, naive safety test
00600             return false;
00601 
00602         for (GoBoard::LibertyIterator libit(board, p); libit; ++libit)
00603             counted.Include(*libit);
00604     }
00605 
00606     // Count immediate adjacencies
00607     for (SgNb4Iterator nb(p); nb; ++nb)
00608     {
00609         SgPoint adj = *nb;
00610         
00611         // Empty points must be filled
00612         if (board.IsColor(adj, SG_EMPTY))
00613         {
00614             counted.Include(adj);
00615         }
00616         
00617         // If adjacent opponent then can never be an eye
00618         else if (board.IsColor(adj, SgOppBW(color)))
00619         {
00620             if (capturing)
00621                 counted.Include(adj); // must capture and then fill
00622             else
00623                 return false;
00624         }
00625     }
00626 
00627     // Up to one diagonal can be ignored: estimate most costly
00628     SgPoint toignore = SG_NULLPOINT;
00629     int maxcost = 0;
00630     int infcost = 1000;
00631     if (board.Line(p) > 1)
00632     {
00633         for (SgNb4DiagIterator nbd(p); nbd; ++nbd)
00634         {
00635             SgPoint diag = *nbd;
00636             int cost = 0;
00637 
00638             if (  board.IsColor(diag, SG_EMPTY)
00639                && ! IsSinglePointEye2(board, diag, color, usedpoints))
00640             {
00641                 cost = 1;
00642             }
00643             
00644             else if (board.IsColor(diag, SgOppBW(color)))
00645             {
00646                 // quick safety test
00647                 if (SinglePointSafe2(board, diag))
00648                     cost = infcost;
00649                 else
00650                     cost = board.NumLiberties(diag);
00651             }
00652 
00653             if (cost > maxcost)
00654             {
00655                 maxcost = cost;
00656                 toignore = diag;
00657             }
00658         }
00659     }
00660 
00661     // Now mark points that must be played to secure diagonals
00662     for (SgNb4DiagIterator nbd(p); nbd; ++nbd)
00663     {
00664         SgPoint diag = *nbd;
00665         if (diag == toignore)
00666             continue;
00667         
00668         // Empty points must be filled (unless they are eyes)
00669         if (  board.IsColor(diag, SG_EMPTY)
00670            && ! IsSinglePointEye2(board, diag, color, usedpoints))
00671         {
00672             counted.Include(diag);
00673         }
00674         
00675         // Opponent stones on diagonals must be captured and filled
00676         else if (board.IsColor(diag, SgOppBW(color)))
00677         {
00678             if (SinglePointSafe2(board, diag))
00679                 return false;
00680             else
00681             {
00682                 counted.Include(diag);
00683                 for (GoBoard::LibertyIterator libit(board, diag); libit;
00684                      ++libit)
00685                     counted.Include(*libit);
00686             }
00687         }
00688     }
00689 
00690     nummoves = counted.Size();
00691     return true;
00692 }
00693 
00694 int GoEyeUtil::CountSinglePointEyes2(const GoBoard& board, SgPoint p)
00695 {
00696     if (! board.Occupied(p))
00697         return 0;
00698 
00699     SgBlackWhite color = board.GetColor(p);
00700     int numeyes = 0;
00701 
00702     for (GoBoard::LibertyIterator lib(board, p); lib; ++lib)
00703     {
00704         if (IsSinglePointEye2(board, *lib, color))
00705             numeyes++;
00706     }
00707     
00708     return numeyes;
00709 }
00710 
00711 bool GoEyeUtil::SinglePointSafe2(const GoBoard& board, SgPoint p)
00712 {
00713     int numeyes = CountSinglePointEyes2(board, p);
00714     return numeyes >= 2;
00715 }
00716 
00717 bool GoEyeUtil::IsLocalSplitPt(SgPoint p, const SgPointSet& set)
00718 {
00719     int nuNb = 0;
00720     for (SgNb4Iterator it(p); it; ++it)
00721     {
00722         if (set.Contains(*it))
00723         {
00724             ++nuNb;
00725             if (nuNb >= 2)
00726                 break;
00727         }
00728     }
00729     if (nuNb >= 2)
00730     {
00731         if (set[p - SG_NS])
00732         {
00733             if (set[p - SG_WE] && TestDiagonal(set, p, -SG_NS, -SG_WE))
00734                 return true;
00735             if (set[p + SG_NS] && TestOpposite(set, p, SG_NS, SG_WE))
00736                 return true;
00737             if (set[p + SG_WE] && TestDiagonal(set, p, -SG_NS, +SG_WE))
00738                 return true;
00739         }
00740         if (set[p + SG_NS])
00741         {
00742             if (set[p - SG_WE] && TestDiagonal(set, p, +SG_NS, -SG_WE))
00743                 return true;
00744             if (set[p + SG_WE] && TestDiagonal(set, p, +SG_NS, +SG_WE))
00745                 return true;
00746         }
00747         if (set[p - SG_WE] && set[p + SG_WE]
00748             && TestOpposite(set, p, SG_WE, SG_NS))
00749             return true;
00750     }
00751     return false; // no local split found.
00752 }
00753 
00754 bool GoEyeUtil::IsSplitPt(SgPoint p, const SgPointSet& points)
00755 {
00756     SG_ASSERT(points[p]);
00757     if (! IsLocalSplitPt(p, points))
00758         return false;
00759     SgPointSet s(points);
00760     s.Exclude(p);
00761     return ! s.IsConnected();
00762 }
00763 
00764 bool GoEyeUtil::CanBecomeSinglePointEye (const GoBoard& board, SgPoint p, 
00765                               const SgPointSet& oppSafe)
00766 {
00767     SG_ASSERT(! oppSafe[p]);
00768 
00769     for (SgNb4Iterator it(p); it; ++it)
00770     {
00771         if (oppSafe[*it])
00772             return false;
00773     }
00774 
00775     int nu = 0;
00776     for (SgNb4DiagIterator dit(p); dit; ++dit)
00777     {
00778         if (oppSafe[*dit])
00779         {
00780             if (board.Line(p) == 1)
00781                 return false;
00782             ++nu;
00783             if (nu > 1)
00784                 return false;
00785         }
00786     }
00787     
00788     return true;
00789 }
00790 
00791 
00792 void GoEyeUtil::TestNakade(const SgPointSet& points,
00793                            const GoBoard& bd,
00794                            SgBlackWhite color,
00795                            bool isFullyEnclosed,
00796                            bool& isNakade,
00797                            bool& makeNakade,
00798                            bool& makeFalse,
00799                            bool& maybeSeki,
00800                            bool& sureSeki,
00801                            SgPoint* vital)
00802 {   // handles case
00803     // of more than 1 point passing vital point test.
00804     // passes back vital point if exactly one is found.
00805     // @todo handle case where vital is illegal or suicide (eye within eye?).
00806     // also test bigger, would-be-alive shapes in atari.
00807     // @todo handle seki.
00808     
00809     SG_UNUSED(makeFalse);
00810     isNakade = makeNakade = maybeSeki = sureSeki = false;
00811     SgPoint vitalP(SG_NULLPOINT);
00812     const SgBlackWhite opp = SgOppBW(color);
00813     const int nuPoints = points.Size();
00814     
00815     SG_ASSERT(nuPoints >= 3); // don't call for smaller areas, no need,
00816     // and results in isNakade = false which is confusing.
00817     
00818     if (nuPoints == 4) // special case 4 point areas
00819     {
00820         if (IsBulkyFour(points))
00821         {
00822             if (   isFullyEnclosed
00823                 && TwoDiagonalStonesInBulkyFour(bd, points, opp)
00824                )
00825                 makeNakade = true;
00826             else
00827                 isNakade = true;
00828             /* */ return; /* */
00829         }
00830         else if (   isFullyEnclosed
00831                  && points.SubsetOf(bd.AllEmpty())
00832                  && IsBentFour(points, bd.Size(), vital)
00833                 )
00834         {
00835             makeNakade = true;
00836             /* */ return; /* */
00837         }
00838     }
00839     else if (isFullyEnclosed && nuPoints == 5) // special case 5 point areas
00840     {
00841         const GoEyeStatus status = BulkyFiveNakade(bd, points, opp);
00842         if (ProcessStatus(status, isNakade, makeNakade))
00843             /* */ return; /* */
00844     }
00845     else if (isFullyEnclosed && nuPoints == 6) // special case 6 point areas
00846     {
00847         if (Is2x3Area (points))
00848         {
00849             GoEyeStatus status = Special2x3Cases(bd, points, opp);
00850             if (ProcessStatus(status, isNakade, makeNakade))
00851                 /* */ return; /* */
00852         }   
00853     }
00854     if (   isFullyEnclosed
00855         && AlmostFilledByNakade(bd, points, opp)
00856        )
00857     {
00858         isNakade = true;
00859         /* */ return; /* */
00860     }
00861         
00862     if (   isFullyEnclosed
00863         && (   AlmostFilledByLivingShape(bd, points, opp)
00864             || ContainsLivingShape(bd, points, opp)
00865            )
00866        )
00867     {   // not nakade, 2 eyes
00868         /* */ return; /* */
00869     }
00870         
00871     int nuMakeNakade = 0;
00872     int nuVitalOccupied = 0;
00873     bool hasDivider = false;
00874     // counting number of nakade fixes bug with stretched 5 pt eye 
00875     // that had 3 vital pts,
00876     // one of them occupied. was classified as 'isNakade7 = 1 eye.
00877     // see sgf/ld200,#20
00878     for (SgSetIterator it(points); it; ++it)
00879     {
00880         SgPoint p(*it);
00881         if (IsVitalPt(points, p, opp, bd))
00882         {
00883             if (bd.IsEmpty(p))
00884             {
00885                 if (bd.IsLegal(p, opp))
00886                 {
00887                     ++nuMakeNakade;
00888                     vitalP = p;
00889                 }
00890                 else
00891                     hasDivider = true;
00892             }
00893             else
00894                 ++nuVitalOccupied;
00895         }
00896     }
00897     
00898     if (hasDivider)
00899     { // alive 
00900     }
00901     else if (nuMakeNakade == 1) // exactly one way to make nakade here.
00902     {
00903         makeNakade = true;
00904         *vital = vitalP;
00905     }
00906     else if (nuMakeNakade > 0) 
00907         isNakade = false;
00908     else if (nuVitalOccupied < 3)
00909         isNakade = true;
00910     else
00911     {
00912         maybeSeki = true;
00913         // @todo if (IsSureSekiShape(...)) sureSeki = true;
00914     }
00915 }
00916 
00917 bool GoEyeUtil::IsVitalPt(const SgPointSet& points, SgPoint p,
00918                 SgBlackWhite opp,
00919                 const GoBoard& bd)
00920 {
00921     // in corridors a vital point has 2 nbs, in big ones it may have 3 or 4.
00922     // but: 2 in following
00923     // example: unsettled nakade, non-flat points are all occupied by opp.
00924     // .a       a is vital Point.
00925     //  o.
00926     //  .
00927     int numNb = bd.NumEmptyNeighbors(p) + bd.NumNeighbors(p, opp);
00928     if (numNb >= 2)
00929     {
00930         if (numNb >= 4)
00931             /* */ return true; /* */
00932         int nu = IsTreeShape(points) ? 2 : 3;
00933         if (numNb >= nu)
00934         {
00935             if (numNb == 2 && bd.IsEmpty(p))
00936                 return IsSplitPt(p, points);
00937             else
00938                 return true;
00939         }
00940     }
00941     return false;
00942 }
00943 
00944 bool GoEyeUtil::CheckInterior(const GoBoard& bd, const SgPointSet& area,
00945                    SgBlackWhite opp, bool checkBlocks)
00946 {
00947     bool hasSingleNbPoint = false;
00948     int nuPoints = 0;
00949     for (SgSetIterator it(area); it; ++it)
00950     {
00951         const SgPoint p(*it);
00952         if (bd.IsEmpty(p))
00953         {
00954             int nuNbs = 0;
00955             if (area.Contains(p + SG_NS))
00956                 ++nuNbs;
00957             if (area.Contains(p - SG_NS))
00958                 ++nuNbs;
00959             if (area.Contains(p + SG_WE))
00960                 ++nuNbs;
00961             if (area.Contains(p - SG_WE))
00962                 ++nuNbs;
00963             if (nuNbs == 1)
00964                 hasSingleNbPoint = true;
00965             else if (nuNbs > 2)
00966                 return false;
00967         }
00968         else if (p == bd.Anchor(p))
00969         {
00970             if (bd.GetColor(p) != opp)
00971             // if own stones on inside: not a tree shape.
00972                 return false;
00973             int nuLibs = bd.NumLiberties(p);
00974             if (nuLibs == 1)
00975                 hasSingleNbPoint = true;
00976             else if (checkBlocks && nuLibs > 2)
00977                 return false;
00978         }
00979         ++nuPoints;
00980     }
00981     return nuPoints == 1 || hasSingleNbPoint;
00982 }
00983 
00984 bool GoEyeUtil::IsTreeShape(const SgPointSet& area)
00985 {
00986     for (SgSetIterator it(area); it; ++it)
00987     {
00988         const SgPoint p(*it);
00989         if (   area.Contains(p + SG_NS)
00990             && area.Contains(p + SG_WE)
00991             && area.Contains(p + SG_NS + SG_WE)
00992            )
00993            return false;
00994     }
00995     return true;
00996 }


Sun Mar 13 2011 Doxygen 1.7.1