Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoSafetyUtil.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoSafetyUtil.cpp
00003     See GoSafetyUtil.h.
00004 */
00005 //----------------------------------------------------------------------------
00006 
00007 #include "SgSystem.h"
00008 #include "GoSafetyUtil.h"
00009 
00010 #include "GoBlock.h"
00011 #include "GoBoard.h"
00012 #include "GoBoardUtil.h"
00013 #include "GoEyeUtil.h"
00014 #include "GoRegion.h"
00015 #include "GoRegionBoard.h"
00016 #include "SgBWSet.h"
00017 #include "SgVector.h"
00018 #include "SgPointSet.h"
00019 #include "SgPointSetUtil.h"
00020 #include "SgWrite.h"
00021 
00022 //----------------------------------------------------------------------------
00023 
00024 namespace {
00025 const bool DEBUG_SAFETY = false;
00026 const bool DEBUG_MIGHT_MAKE_LIFE = false;
00027 const bool DEBUG_EXTENDED_MIGHT_MAKE_LIFE = false;
00028 
00029 /** find 2 libs which would connect block to safe.
00030     if found, update libs and safe to indicate that the block is safe now:
00031     add block to safe, add block libs to libs, remove the two libs.
00032 */
00033 bool Find2Connections(const GoBoard& bd, SgPoint block, SgPointSet* libs,
00034                     SgPointSet* usedLibs, SgPointSet* safe)
00035 {
00036     SG_ASSERT(libs->Disjoint(*usedLibs));
00037 
00038     int nuLibs(0);
00039     SgVector<SgPoint> blockLibs;
00040     for (GoBoard::LibertyIterator it(bd, block); it; ++it)
00041     {
00042         if (libs->Contains(*it))
00043         {
00044             blockLibs.PushBack(*it);
00045             ++nuLibs;
00046             if (nuLibs >= 2)
00047                 break;
00048         }
00049     }
00050     if (nuLibs >= 2) // found it!
00051     {
00052         for (GoBoard::StoneIterator it(bd, block); it; ++it)
00053             safe->Include(*it);
00054         for (GoBoard::LibertyIterator it(bd, block); it; ++it)
00055             libs->Include(*it);
00056         for (SgVectorIterator<SgPoint> it(blockLibs); it; ++it)
00057             usedLibs->Include(*it);
00058         *libs -= *usedLibs; // can never re-use such a lib.
00059     }
00060 
00061     return nuLibs >= 2;
00062 }
00063 
00064 /** Find connections for unsafe boundary and interior stones,
00065     and interior empty points.
00066 
00067     Can omit maxNuOmissions (0 or 1) empty points from checking.
00068     maxNuOmissions = 1 if testing whether opponent can make 2 eyes here,
00069     0 otherwise. Returns bool whether connections were found.
00070 */
00071 bool Find2ConnectionsForAll(const GoBoard& bd, const SgPointSet& pts,
00072                 const SgPointSet& inSafe, SgBlackWhite color,
00073                 int maxNuOmissions = 0)
00074 {
00075     if (DEBUG_SAFETY)
00076         SgDebug() << "Find2ConnectionsForAll " << pts
00077                   << "safe points - input: " << inSafe;
00078     SgPointSet safe(inSafe);
00079     SgVector<SgPoint> unsafe;
00080     const int size = bd.Size();
00081     GoSafetyUtil::ReduceToAnchors(bd, pts.Border(size) - safe, &unsafe);
00082     // AR sort by # empty nbs in pts
00083 
00084     if (DEBUG_SAFETY)
00085         SgDebug() << SgWritePointList(unsafe, "unsafe anchors: ");
00086 
00087     SgPointSet libs = pts & bd.AllEmpty() & safe.Border(size);
00088     SgPointSet interior = pts - libs;
00089     interior -= bd.All(SgOppBW(color)); // remove opp. stones.
00090     SgVector<SgPoint> unsafeInterior;
00091     GoSafetyUtil::ReduceToAnchors(bd, interior & bd.All(color),
00092                                   &unsafeInterior);
00093     unsafe.Concat(&unsafeInterior);
00094 
00095     SgPointSet usedLibs;
00096     bool change = true;
00097     while (change && unsafe.NonEmpty() && libs.MinSetSize(2))
00098     {
00099         SgVector<SgPoint> newSafe;
00100         for (SgVectorIterator<SgPoint> it(unsafe); it; ++it)
00101             if (Find2Connections(bd, *it, &libs, &usedLibs, &safe))
00102             {
00103                 newSafe.PushBack(*it);
00104             }
00105 
00106         unsafe.Exclude(newSafe);
00107         change = newSafe.NonEmpty();
00108     }
00109 
00110     if (unsafe.NonEmpty()) // could not connect everything.
00111     {
00112         if (DEBUG_SAFETY)
00113             SgDebug()
00114                 << SgWritePointList(unsafe, "could not connect unsafe: ");
00115         return false;
00116     }
00117     // now we know all blocks are safe. try to prove territory safe, too.
00118     // AR if terr. safety fails, still declare blocks safe, but put
00119     // miai strategy commitment on region.
00120 
00121     interior = (pts & bd.AllEmpty()) - safe.Border(size);
00122     // new safe set after Find2Connections.
00123 
00124     // try to prove opp. can't live inside.
00125     if (maxNuOmissions == 1)
00126     {
00127         SgBlackWhite opp(SgOppBW(color));
00128         if (! GoSafetyUtil::MightMakeLife(bd, interior, safe, opp))
00129             /* */ return true; /* */
00130     }
00131 
00132     // now try to find miai-paths to remaining interior empty points
00133     // AR shortcut failure if maxNuOmissions = 0 and opp has eye:
00134     // then this will never find anything.
00135 
00136     for (SgSetIterator it(interior); it; ++it)
00137     {
00138         if (! GoSafetyUtil::Find2Libs(*it, &libs))
00139         {
00140             if (--maxNuOmissions < 0)
00141                 return false;
00142         }
00143     }
00144 
00145     return true;
00146 }
00147 
00148 void TestLiberty(SgPoint lib, const SgPointSet& libs,
00149                  SgVector<SgPoint>* foundLibs,
00150                  int* nuLibs)
00151 {
00152     if (libs.Contains(lib))
00153     {
00154         foundLibs->PushBack(lib);
00155         ++(*nuLibs);
00156     }
00157 }
00158 
00159 /** write part and total and rounded percentage of part in total */
00160 void WriteSafeTotal(std::ostream& stream, std::string text,
00161                     int partCount, int totalCount)
00162 {
00163     stream << partCount << " / " << totalCount
00164            << " (" 
00165            << (partCount * 100 + totalCount / 2) / totalCount
00166            << "%) " << text << '\n';
00167 }
00168 
00169 } // namespace
00170 
00171 //----------------------------------------------------------------------------
00172 
00173 void GoSafetyUtil::AddToSafe(const GoBoard& board, const SgPointSet& pts,
00174                              SgBlackWhite color, SgBWSet* safe,
00175                              const char* reason, int depth, bool addBoundary)
00176 {
00177     SG_UNUSED(reason);
00178     SG_UNUSED(depth);
00179 
00180     (*safe)[color] |= pts;
00181     safe->AssertDisjoint();
00182     SgPointSet empty = board.AllEmpty();
00183 
00184     const int size = board.Size();
00185     if (addBoundary)
00186     {
00187         SgPointSet dep(pts.Border(size) - empty); // pts can be zone (open!)
00188         GoBoardUtil::ExpandToBlocks(board, dep);
00189         SG_ASSERT(dep.SubsetOf(board.All(color)));
00190         (*safe)[color] |= dep;
00191         safe->AssertDisjoint();
00192     }
00193 
00194     if (DEBUG_SAFETY)
00195         SgDebug() << "AddToSafe " << reason
00196                   << " depth = " << depth << " points = "
00197                   << SgWritePointSetID(pts) << '\n';
00198 }
00199 
00200 bool GoSafetyUtil::ExtendedMightMakeLife(const GoBoard& board,
00201                                          GoRegionBoard* regions,
00202                                          const SgPointSet& area,
00203                                          const SgPointSet& safe,
00204                                          SgBlackWhite color)
00205 {
00206     const GoRegion* nakadeRegion = 0;
00207 
00208     if (DEBUG_EXTENDED_MIGHT_MAKE_LIFE)
00209         SgDebug() << "ExtendedMightMakeLife for " << SgBW(color)
00210                   << " area " << area << '\n';
00211 
00212     // Check if region is a nakade shape that fills all potential eye space
00213     for (SgVectorIteratorOf<GoRegion> it(regions->AllRegions(color));
00214          it; ++it)
00215     {
00216         if (  area.SupersetOf((*it)->Points())
00217            && area.SupersetOf((*it)->BlocksPoints())
00218            )
00219         {
00220             if (DEBUG_EXTENDED_MIGHT_MAKE_LIFE)
00221             {
00222                 SgDebug() << "contains region ";
00223                 (*it)->WriteID(SgDebug());
00224                 SgDebug() << '\n';
00225             }
00226 
00227             if (! (*it)->ComputedFlag(GO_REGION_COMPUTED_NAKADE))
00228                 (*it)->DoComputeFlag(GO_REGION_COMPUTED_NAKADE); 
00229                 // sets MaxPotEyes()
00230             if ((*it)->MaxPotEyes() > 1)
00231                 return true;
00232             else if (nakadeRegion == 0)
00233                 nakadeRegion = *it;
00234             else // at least 2 regions - might make eyes
00235                 return true;
00236         }
00237     }
00238 
00239     if (DEBUG_EXTENDED_MIGHT_MAKE_LIFE)
00240         SgDebug() << "case 2\n";
00241     SgPointSet rest = area;
00242     if (nakadeRegion == 0) // classical case. Call previous function
00243         return GoSafetyUtil::MightMakeLife(board, area, safe, color);
00244     else
00245     {
00246         if (DEBUG_EXTENDED_MIGHT_MAKE_LIFE)
00247             SgDebug() << "ExtendedMightMakeLife for " << area
00248                       << ": inside opp region " 
00249                       << *nakadeRegion << '\n';
00250         if (nakadeRegion->MaxPotEyes() <= 1)
00251         // what if 0 eyes??? Can allow 1 eye elsewhere?
00252         {
00253             rest -= nakadeRegion->Points();
00254             rest -= nakadeRegion->BlocksPoints();
00255         }
00256     }
00257 
00258     const int size = board.Size();
00259     rest -= safe.Border(size);
00260     rest -= board.All(color);
00261 
00262     if (DEBUG_EXTENDED_MIGHT_MAKE_LIFE)
00263         SgDebug() << "rest = " << rest << "\n";
00264     for (SgSetIterator it(rest); it; ++it)
00265     {
00266         SgPoint p(*it);
00267         if (GoEyeUtil::CanBecomeSinglePointEye(board, p, safe))
00268             return true;
00269     }
00270 
00271     return false;
00272 }
00273 
00274 SgPointSet GoSafetyUtil::FindDamePoints(const GoBoard& bd,
00275                                         const SgPointSet& empty,
00276                                         const SgBWSet& safe)
00277 {
00278     SgPointSet dame, unsurroundable;
00279     FindDameAndUnsurroundablePoints(bd, empty, safe, &dame, &unsurroundable);
00280     return dame;
00281 }
00282 
00283 void GoSafetyUtil::FindDameAndUnsurroundablePoints(const GoBoard& bd,
00284                                                    const SgPointSet& empty,
00285                                                    const SgBWSet& safe,
00286                                                    SgPointSet* dame,
00287                                                    SgPointSet* unsurroundable)
00288 {
00289     SG_ASSERT(dame->IsEmpty());
00290     SG_ASSERT(unsurroundable->IsEmpty());
00291     const int size = bd.Size();
00292     *unsurroundable =   safe[SG_BLACK].Border(size)
00293                       & safe[SG_WHITE].Border(size)
00294                       & empty;
00295     SgPointSet maybeDame(*unsurroundable);
00296     SgBWSet unsafe; // must exclude these
00297     unsafe[SG_BLACK] = bd.All(SG_BLACK) - safe[SG_BLACK];
00298     unsafe[SG_WHITE] = bd.All(SG_WHITE) - safe[SG_WHITE];
00299     maybeDame -= unsafe[SG_BLACK].Border(size);
00300     maybeDame -= unsafe[SG_WHITE].Border(size);
00301     for (SgSetIterator it(maybeDame); it; ++it)
00302     {
00303         SgPoint p(*it);
00304         bool isDame = true;
00305         for (SgNb4Iterator it(p); it; ++it)
00306         {
00307             SgPoint nb(*it);
00308             if (empty[nb] && ! unsurroundable->Contains(nb))
00309             {
00310             // can use unsurroundable instead of smaller set maybeDame
00311                 isDame = false;
00312                 break;
00313             }
00314         }
00315         if (isDame)
00316             dame->Include(p);
00317     }
00318 }
00319 
00320 bool GoSafetyUtil::MightMakeLife(const GoBoard& board,
00321                                  const SgPointSet& area,
00322                                  const SgPointSet& safe, SgBlackWhite color)
00323 {
00324     const int size = board.Size();
00325     SgPointSet eyePts = (area - safe.Border(size)) - board.All(color);
00326     if (eyePts.MaxSetSize(1))
00327         return false;
00328 
00329     if (DEBUG_MIGHT_MAKE_LIFE)
00330         SgDebug() << "GoSafetyUtil::MightMakeLife\n";
00331 
00332     SgPoint eye(SG_NULLPOINT), adjToEye(SG_NULLPOINT);
00333     for (SgSetIterator it(eyePts); it; ++it)
00334     {
00335         const SgPoint p(*it);
00336         if (GoEyeUtil::CanBecomeSinglePointEye(board, p, safe))
00337         {
00338             if (eye == SG_NULLPOINT)
00339             {
00340                 eye = p;
00341                 if (DEBUG_MIGHT_MAKE_LIFE)
00342                     SgDebug() << "eye = " << SgWritePoint(eye) << "\n";
00343             }
00344             else if (  adjToEye == SG_NULLPOINT
00345                     && SgPointUtil::AreAdjacent(eye, p)
00346                     )
00347                 adjToEye = p;
00348             else
00349             {
00350                 if (DEBUG_MIGHT_MAKE_LIFE)
00351                     SgDebug() << "second eye = " << SgWritePoint(p) << "\n";
00352                /* */ return true; /* */
00353             }
00354         }
00355     }
00356 
00357     return false;
00358 }
00359 
00360 bool GoSafetyUtil::Find2Libs(SgPoint p, SgPointSet* libs)
00361 {
00362     int nuLibs = 0;
00363     SgVector<SgPoint> foundLibs;
00364     TestLiberty(p + SG_NS, *libs, &foundLibs, &nuLibs);
00365     TestLiberty(p + SG_WE, *libs, &foundLibs, &nuLibs);
00366     if (nuLibs < 2)
00367     {
00368         TestLiberty(p - SG_NS, *libs, &foundLibs, &nuLibs);
00369         if (nuLibs < 2)
00370             TestLiberty(p - SG_WE, *libs, &foundLibs, &nuLibs);
00371     }
00372     if (nuLibs >= 2)
00373     {
00374         SG_ASSERT(nuLibs == 2 && foundLibs.IsLength(2));
00375         libs->Exclude(foundLibs.Front());
00376         libs->Exclude(foundLibs.Back());
00377     }
00378 
00379     return nuLibs >= 2;
00380 }
00381 
00382 bool GoSafetyUtil::Find2BestLibs(SgPoint p, const SgPointSet& libs,
00383                                  SgPointSet interior, SgMiaiPair* miaiPair)
00384 {
00385     int nuLibs = 0;
00386     SgVector<SgPoint> allLibs; // liberties of point p
00387 
00388     TestLiberty(p + SG_NS, libs, &allLibs, &nuLibs);
00389     TestLiberty(p + SG_WE, libs, &allLibs, &nuLibs);
00390     TestLiberty(p - SG_NS, libs, &allLibs, &nuLibs);
00391     TestLiberty(p - SG_WE, libs, &allLibs, &nuLibs);
00392 
00393     if (allLibs.MaxLength(1))
00394         return false;
00395     else if (allLibs.IsLength(2))
00396     {
00397         SG_ASSERT(nuLibs == 2 && allLibs.IsLength(2));
00398         miaiPair->first = allLibs[0];
00399         miaiPair->second = allLibs[1];
00400         /* */ return true; /* */
00401     }
00402     else
00403     {
00404         SgVector<SgPoint> shared, not_shared;
00405         SgPointSet others = interior;
00406         others.Exclude(p);
00407 
00408         for (SgVectorIterator<SgPoint> it(allLibs); it; ++it)
00409         {
00410             bool share = false;
00411             for (SgSetIterator it2(others); it2; ++it2)
00412             {
00413                 if (SgPointUtil::AreAdjacent(*it, *it2))
00414                 {
00415                     share = true;
00416                     break;
00417                 }
00418             }
00419             if (share)
00420                 shared.PushBack(*it);
00421                 // this lib is shared with other interior points
00422             else
00423                 not_shared.PushBack(*it);
00424         }
00425         // if has >= 2 not_shared libs, then try to find 2 original libs (not
00426         // new-created libs (because the new one might be ip points)
00427         if (not_shared.MinLength(2))
00428         {
00429             miaiPair->first = not_shared[0];
00430             miaiPair->second = not_shared[1];
00431         }
00432         // if only 1 not_shared lib, use this first, then another shared lib
00433         else if (not_shared.IsLength(1))
00434         {
00435             miaiPair->first = not_shared[0];
00436             miaiPair->second = shared[0];
00437         }
00438         // zero not_shared libs, then use two shared libs
00439         else
00440         {
00441             miaiPair->first = shared[0];
00442             miaiPair->second = shared[1];
00443         }
00444         return true;
00445     }
00446 }
00447 
00448 bool GoSafetyUtil::ExtendedIsTerritory(const GoBoard& board,
00449                                        GoRegionBoard* regions,
00450                                        const SgPointSet& pts,
00451                                        const SgPointSet& safe,
00452                                        SgBlackWhite color)
00453 {
00454     SG_ASSERT(! pts.Overlaps(safe));
00455     const int size = board.Size();
00456     SgPointSet boundary(pts.Border(size));
00457     if (boundary.SubsetOf(safe))
00458     {
00459         SgBlackWhite opp = SgOppBW(color);
00460         if (! ExtendedMightMakeLife(board, regions, pts, safe, opp))
00461             /* */ return true; /* */
00462     }
00463 
00464     return IsTerritory(board, pts, safe, color);
00465 }
00466 
00467 bool GoSafetyUtil::IsTerritory(const GoBoard& board, const SgPointSet& pts,
00468                                const SgPointSet& safe, SgBlackWhite color)
00469 {
00470     SG_ASSERT(! pts.Overlaps(safe));
00471     const int size = board.Size();
00472     SgPointSet boundary(pts.Border(size));
00473     if (boundary.SubsetOf(safe))
00474     {
00475         SgBlackWhite opp = SgOppBW(color);
00476         if (! GoSafetyUtil::MightMakeLife(board, pts, safe, opp))
00477             /* */ return true; /* */
00478     }
00479 
00480     if (  boundary.SubsetOf(board.All(color))
00481        && Find2ConnectionsForAll(board, pts, safe, color, 1)
00482        )
00483        /* */ return true; /* */
00484     return false;
00485 }
00486 
00487 void GoSafetyUtil::ReduceToAnchors(const GoBoard& board,
00488                                    const SgPointSet& stones,
00489                                    SgVector<SgPoint>* anchors)
00490 {
00491     SG_ASSERT(anchors->IsEmpty());
00492     for (SgSetIterator it(stones); it; ++it)
00493     {
00494         SG_ASSERT(board.Occupied(*it));
00495         anchors->Insert(board.Anchor(*it));
00496     }
00497 }
00498 
00499 void GoSafetyUtil::WriteStatistics(const std::string& heading,
00500                       const GoRegionBoard* regions,
00501                       const SgBWSet* safe)
00502 {    
00503     const SgPointSet allSafe = safe->Both();
00504     int totalRegions = 0;
00505     int safeRegions = 0;
00506     int totalBlocks = 0;
00507     int safeBlocks = 0;  
00508 
00509     for (SgBWIterator it; it; ++it)
00510     {
00511         SgBlackWhite color(*it);
00512         for (SgVectorIteratorOf<GoRegion> it(regions->AllRegions(color));
00513                                            it; ++it)
00514         {
00515             ++totalRegions;
00516             if ((*it)->Points().SubsetOf(allSafe))
00517                 ++safeRegions;
00518         }
00519         for (SgVectorIteratorOf<GoBlock> it(regions->AllBlocks(color));
00520                                         it; ++it)
00521         {
00522             ++totalBlocks;
00523             if (allSafe.Overlaps((*it)->Stones()))
00524                 ++safeBlocks;
00525         }
00526     }
00527     
00528     const int bdSize = regions->Board().Size();
00529     SgDebug() << heading << "\n";
00530     WriteSafeTotal(SgDebug(), "points", allSafe.Size(), bdSize * bdSize);
00531     WriteSafeTotal(SgDebug(), "regions", safeRegions, totalRegions);
00532     WriteSafeTotal(SgDebug(), "blocks", safeBlocks, totalBlocks);
00533 }
00534 
00535 
00536 //----------------------------------------------------------------------------


17 Jun 2010 Doxygen 1.4.7