Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoRegion.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoRegion.cpp
00003     See GoRegion.h.
00004 */
00005 //----------------------------------------------------------------------------
00006 
00007 #include "SgSystem.h"
00008 #include "GoRegion.h"
00009 
00010 #include <iostream>
00011 #include <cstdio>
00012 #include "GoBlock.h"
00013 #include "GoBoard.h"
00014 #include "GoChain.h"
00015 #include "GoEyeUtil.h"
00016 #include "GoRegionBoard.h"
00017 #include "GoRegionUtil.h"
00018 #include "GoSafetyUtil.h"
00019 #include "SgConnCompIterator.h"
00020 #include "SgDebug.h"
00021 #include "SgVector.h"
00022 #include "SgNbIterator.h"
00023 #include "SgPointArray.h"
00024 #include "SgStrategy.h"
00025 #include "SgWrite.h"
00026 
00027 using GoBoardUtil::ExpandToBlocks;
00028 using GoEyeUtil::IsSplitPt;
00029 using GoEyeUtil::TestNakade;
00030 using GoRegionUtil::IsSmallRegion;
00031 using GoSafetyUtil::Find2BestLibs;
00032 using GoSafetyUtil::Find2Libs;
00033 using GoSafetyUtil::MightMakeLife;
00034 
00035 //----------------------------------------------------------------------------
00036 
00037 static const bool CHECK = SG_CHECK && true;
00038 
00039 static const bool HEAVYCHECK = SG_HEAVYCHECK && CHECK && false;
00040 
00041 static const bool WRITEDEBUG = false;
00042 
00043 //----------------------------------------------------------------------------
00044 
00045 namespace {
00046 
00047 /** Is p adjacent to all blocks?
00048     GoRegionUtil has an identical function taking a list of anchorss.
00049 */
00050 inline bool IsAdjacentToAll(const GoBoard& board, SgPoint p,
00051                             const SgVectorOf<GoBlock>& blocks)
00052 {
00053     for (SgVectorIteratorOf<GoBlock> it(blocks); it; ++it)
00054         if (! board.IsLibertyOfBlock(p, (*it)->Anchor()))
00055             return false;
00056     return true;
00057 }
00058 
00059 /** Is p adjacent to all points? (not blocks) */
00060 inline bool AdjacentToAll(SgPoint p, const SgVector<SgPoint>& points)
00061 {
00062     if (points.IsEmpty())
00063         /* */ return true; /* */
00064 
00065     for (SgVectorIterator<SgPoint> it(points); it; ++it)
00066         if (! SgPointUtil::AreAdjacent(p, *it))
00067             return false;
00068 
00069     return true;
00070 }
00071 
00072 } // namespace
00073 
00074 //----------------------------------------------------------------------------
00075 
00076 GoRegion::GoRegion(const GoBoard& board, const SgPointSet& points,
00077                    SgBlackWhite color)
00078         : m_bd(board),
00079           m_points(points),
00080           m_color(color),
00081           m_eyes(),
00082           m_vitalPoint(SG_NULLMOVE),
00083           m_1vcDepth(0),
00084           m_miaiStrategy(color)
00085 {
00086 #ifndef NDEBUG
00087     ++s_alloc;
00088 #endif
00089 }
00090 
00091 
00092 bool GoRegion::StaticIs1VitalAndConnected() const
00093 { // checks for 1-vitality, as explained in[Mueller 95, p.****]
00094 
00095     // type 1: small region with two connection points for all blocks
00096     bool is1Vital = false;
00097     if (GetFlag(GO_REGION_SMALL))
00098     {
00099         // single block, connected.
00100         if (GetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY)) 
00101             /* */ return true; /* */
00102         else if (m_blocks.MinLength(5))
00103         // no way so many blocks can be connected.
00104             return false;
00105 
00106         int nuConn = 0;
00107         for (SgSetIterator it(Points()); it; ++it)
00108         {
00109             SgPoint p(*it);
00110             if (m_bd.IsEmpty(p) && IsAdjacentToAll(m_bd, p, m_blocks))
00111             {   // test if boundary stones can be connected by playing p
00112                 if (++nuConn >= 2)
00113                 {
00114                     is1Vital = true;
00115                     break;
00116                 }
00117             }
00118         }
00119     }
00120     return is1Vital;
00121 }
00122 
00123 bool GoRegion::AllEmptyAreLibs() const
00124 {
00125     for (SgSetIterator it(Points()); it; ++it)
00126     {
00127         SgPoint p(*it);
00128         if (m_bd.IsEmpty(p) && ! m_bd.HasNeighbors(p, Color()))
00129             return false;
00130     }
00131     return true;
00132 }
00133 
00134 SgVectorOf<GoBlock> GoRegion::InteriorBlocks() const
00135 {
00136     SgVectorOf<GoBlock> interior;
00137     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00138         if (IsInteriorBlock(*it))
00139             interior.PushBack(*it);
00140     return interior;
00141 }
00142 
00143 bool GoRegion::IsInteriorBlock(const GoBlock* block) const
00144 {
00145     SG_ASSERT(m_blocks.Contains(block));
00146     const SgBlackWhite opp = SgOppBW(block->Color());
00147     for (GoBoard::StoneIterator it(m_bd, block->Anchor()); it; ++it)
00148         for (SgNb4Iterator nb(*it); nb; ++nb)
00149         {
00150             const SgPoint p = *nb;
00151             if (   (m_bd.IsEmpty(p) || m_bd.IsColor(p, opp))
00152                 && ! m_points.Contains(p))
00153                 /* */ return false; /* */
00154         }
00155     return true;
00156 }
00157 
00158 SgPointSet GoRegion::PointsPlusInteriorBlocks() const
00159 {
00160     SgPointSet area = m_points;
00161     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00162         if (IsInteriorBlock(*it))
00163             area |= (*it)->Stones();
00164     return area;
00165 }
00166 
00167 void GoRegion::InteriorEmpty(SgVector<SgPoint>* interiorEmpty, 
00168                              int maxNu) const
00169 {
00170     for (SgSetIterator it(Points()); it; ++it)
00171     {
00172         SgPoint p(*it);
00173         if (m_bd.IsEmpty(p) && ! m_bd.HasNeighbors(p, Color()))
00174         {
00175             interiorEmpty->Include(p);
00176             if (--maxNu < 0)
00177                 /* */ return; /* */
00178         }
00179     }
00180 }
00181 
00182 bool GoRegion::Has2SureLibs(SgMiaiStrategy* miaiStrategy) const
00183 {
00184     // if empty board, (b/w) region without any boundary blocks
00185     if (m_blocks.IsEmpty())
00186         return false;
00187 
00188     SG_ASSERT(! m_blocks.IsEmpty());
00189     SgVector<SgPoint> interiorEmpty;
00190     InteriorEmpty(&interiorEmpty, 3);
00191     SgMiaiPair ips;
00192     bool    result1 = interiorEmpty.MaxLength(2)
00193          && Has2IPs(interiorEmpty, &ips);
00194 
00195     if (result1)
00196     {
00197         miaiStrategy->AddPair(ips);
00198         return true;
00199     }
00200 
00201     /** find all interior points connected to boundary
00202         recursively, that have 2 intersection points inside region
00203     */
00204     SgVector<SgPoint> usedLibs;
00205     bool result2 =    Find2ConnForAllInterior(miaiStrategy, usedLibs)
00206                    && Has2IntersectionPoints(usedLibs);
00207     return result2;
00208 }
00209 
00210 void GoRegion::InsideLibs(const GoBlock* b, SgVector<SgPoint>* libs) const
00211 {
00212     for (GoBoard::LibertyIterator it(m_bd, b->Anchor()); it; ++it)
00213         if (Points().Contains(*it))
00214             libs->PushBack(*it);
00215 }
00216 
00217 bool GoRegion::HasLibForAllBlocks() const
00218 {
00219     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00220         if (! HasBlockLibs(*it))
00221             return false;
00222     return true;
00223 }
00224 
00225 bool GoRegion::HasLibsForAllBlocks(int n) const
00226 {
00227     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00228         if (! (*it)->IsSafe() && ! HasLibsForBlock(*it, n))
00229             return false;
00230     return true;
00231 }
00232 
00233 bool GoRegion::HasBlockLibs(const GoBlock* b) const
00234 {
00235     for (GoBoard::LibertyIterator it(m_bd, b->Anchor()); it; ++it)
00236         if (Points().Contains(*it))
00237             return true;
00238     return false;
00239 }
00240 
00241 bool GoRegion::HasLibsForBlock(const GoBlock* b, int n) const
00242 {
00243     int counter = 0;
00244     for (GoBoard::LibertyIterator it(m_bd, b->Anchor()); it; ++it)
00245         if (Points().Contains(*it))
00246         {
00247             if (++counter >= n)
00248                 return true;
00249         }
00250     return false;
00251 }
00252 
00253 void GoRegion::JointLibs(SgVector<SgPoint>* libs) const
00254 {
00255     GoBlock* first = m_blocks.Front();
00256     if (GetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY))
00257     {
00258         InsideLibs(first, libs);
00259         return;
00260     }
00261 
00262     SG_ASSERT(m_blocks.MinLength(2));
00263     int minLib = INT_MAX;
00264     GoBlock* minB = 0;
00265 
00266     // find smallest #libs block; stop immediately if less than 2
00267     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00268     {   int nu = (*it)->NuLiberties();
00269         if (nu < 2)
00270             /* */ return; /* */
00271         else if (nu < minLib)
00272         {
00273             minLib = nu;
00274             minB = *it;
00275         }
00276     }
00277     SG_ASSERT(minB != 0);
00278 
00279     for (GoBoard::LibertyIterator it(m_bd, minB->Anchor()); it; ++it)
00280     {
00281         SgPoint lib(*it);
00282         if (Points().Contains(lib))
00283         {
00284             bool joint = true;
00285             for (SgVectorIteratorOf<GoBlock> itBlock(m_blocks); itBlock;
00286                  ++itBlock)
00287             {
00288                 if (! (*itBlock)->HasLiberty(lib))
00289                 {
00290                     joint = false;
00291                     break;
00292                 }
00293             }
00294             if (joint)
00295                 libs->PushBack(*it);
00296         }
00297     }
00298 }
00299 
00300 bool GoRegion::Has2IPs(const SgVector<SgPoint>& interiorEmpty,
00301                        SgMiaiPair* ips) const
00302 {
00303     SgVector<SgPoint> jointLibs;
00304     JointLibs(&jointLibs);
00305     if (jointLibs.MinLength(2))
00306     {
00307         // check if libs are intersection pts
00308         int nuIPs = 0;
00309         SgPoint ip1 = SG_NULLPOINT;
00310         for (SgVectorIterator<SgPoint> it(jointLibs); it; ++it)
00311         {
00312             if (AdjacentToAll(*it, interiorEmpty) && IsSplitPt(*it, Points()))
00313             {
00314                 ++nuIPs;
00315                 if (ip1 == SG_NULLPOINT)
00316                     ip1 = *it;
00317                 else
00318                 {
00319                     ips->first = ip1;
00320                     ips->second = *it;
00321                     return true;
00322                 }
00323             }
00324         }
00325     }
00326     return false;
00327 }
00328 
00329 bool GoRegion::Has2IntersectionPoints(const SgVector<SgPoint>& usedLibs) const
00330 {
00331     SgVector<SgPoint> jointLibs;
00332     JointLibs(&jointLibs);
00333     if (jointLibs.MinLength(2))
00334     {
00335         // check if libs are intersection pts
00336         int nuIPs = 0;
00337         // doesn't have to adjacent to all interior points! 2005/08
00338         for (SgVectorIterator<SgPoint> it(jointLibs); it; ++it)
00339         {
00340             if (IsSplitPt(*it, Points()) && ! usedLibs.Contains(*it))
00341             {
00342                 if (++nuIPs >= 2)
00343                     return true;
00344             }
00345         }
00346     }
00347     return false;
00348 }
00349 
00350 void GoRegion::GetIPs(SgVector<SgPoint>* ips) const
00351 {
00352     SgVector<SgPoint> jointLibs;
00353     JointLibs(&jointLibs);
00354     for (SgVectorIterator<SgPoint> it(jointLibs); it; ++it)
00355         if (IsSplitPt(*it, Points()))
00356             ips->PushBack(*it);
00357 }
00358 
00359 void GoRegion::GetDivideMiaiPairs(SgVector<SgMiaiPair>& pairs) const
00360 {
00361     SgVector<SgPoint> divPs;
00362 
00363     for (SgVectorIteratorOf<GoBlock> it(Blocks()); it; ++it)
00364     {
00365         SgVector<SgPoint> libs, temp;
00366         InsideLibs(*it, &libs);
00367         SgMiaiPair p1;
00368         SgPoint a = -1;
00369 
00370         // only find miaipairs from libs (empty points)
00371         for (SgVectorIterator<SgPoint> it2(libs); it2; ++it2)
00372         {
00373             if (IsSplitPt(*it2, Points()))
00374                 temp.PushBack(*it2);
00375         }
00376         temp.Sort();
00377         divPs.PushBackList(temp);
00378 
00379         for (SgVectorIterator<SgPoint> it2(temp); it2; ++it2)
00380         {
00381             if (a == -1)
00382                 a = (*it2);
00383             else
00384             {
00385                 if (SgPointUtil::AreAdjacent(a, *it2))
00386                 {
00387                     p1.first = a;
00388                     p1.second = *it2;
00389                     pairs.PushBack(p1);
00390                 }
00391                 a = *it2;
00392             }
00393         }
00394 
00395     } // found miaipairs for each block
00396 
00397     if (WRITEDEBUG)
00398     {
00399         SgDebug() << SgWritePointList(divPs, "divPs: ", true);
00400         for (SgVectorIterator<SgMiaiPair> it(pairs); it; ++it)
00401         {
00402             SgDebug() << "Pair(1: " << SgWritePoint((*it).first)
00403             << " 2: " << SgWritePoint((*it).second) << ")\n";
00404         }
00405     }
00406 }
00407 
00408 SgPointSet GoRegion::AllInsideLibs() const
00409 {
00410     const int size = m_bd.Size();
00411     return (m_points - Dep().Border(size)) & m_bd.AllEmpty();
00412 }
00413 
00414 bool GoRegion::Find2ConnForAll() const
00415 {
00416     if (GetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY))
00417     {
00418         const SgPointSet interior = AllInsideLibs();
00419         SgPointSet libs = (Points() & m_bd.AllEmpty()) - AllInsideLibs();
00420         // now try to find miai-paths to remaining interior empty points
00421         for (SgSetIterator it(interior); it; ++it)
00422         {
00423             if (! Find2Libs(*it, &libs))
00424                 return false;
00425         }
00426         return true;
00427     }
00428 
00429     return false;
00430 
00431 #if UNUSED
00432     single = GetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY);
00433     if (! single)
00434     {
00435         // is1Vital = false;
00436         // try to connect everything together with the first block.
00437         GoBlock* first = Blocks().Front();
00438         if (Find2ConnForAll(m_bd, Points(), first->Stones(), Color()))
00439             twoLibs = true;
00440         else
00441             is1Vital = false;
00442     }
00443     else if (Find2ConnForAll(m_bd, Points(), bd, Color()))
00444         twoLibs = true;
00445     else
00446         is1Vital = false;
00447 
00448 #endif
00449 }
00450 
00451 // improved by using recursive extension to find 2-conn paths.
00452 bool GoRegion::Find2ConnForAllInterior(SgMiaiStrategy* miaiStrategy,
00453                                        SgVector<SgPoint>& usedLibs) const
00454 {
00455     SgVector<SgMiaiPair> myStrategy;
00456     const int size = m_bd.Size();
00457     SgPointSet interior = AllInsideLibs();
00458     if (interior.IsEmpty())
00459     {
00460         return true;
00461     }
00462     //if (GetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY))
00463     {
00464         SgPointSet testSet = interior;
00465         SgPointSet originalLibs = testSet.Border(size) & Dep().Border(size)
00466                                 & m_bd.AllEmpty() & Points();
00467         SgPointSet updateLibs = originalLibs;
00468 
00469         // now try to find miai-paths to remaining interior points recursively
00470         bool changed = true;
00471         while (changed)
00472         {
00473             changed = false;
00474             if (testSet.IsEmpty())
00475             {
00476                 SgVector<SgPoint> jlibs;
00477                 JointLibs(&jlibs);
00478                 SgVector<SgPoint> ips;
00479                 GetIPs(&ips);
00480                 SgVector<SgMiaiPair> updateStrg;
00481 
00482                 for (SgSetIterator it(interior); it; ++it)
00483                 {
00484                     SgPoint p = *it;
00485                     SgPointSet s1;
00486                     s1.Include(p);
00487                     SgPointSet rest = s1.Border(size) & updateLibs;
00488                     if (! rest.IsEmpty())
00489                     {
00490                         for (SgVectorIterator<SgMiaiPair> it2(myStrategy);
00491                              it2; ++it2)
00492                         {
00493                             SgMiaiPair x = *it2;
00494                             if (   SgPointUtil::AreAdjacent(p, x.first)
00495                                 && SgPointUtil::AreAdjacent(p, x.second)
00496                                )
00497                             {
00498                                 if (ips.Contains(x.first))
00499                                 {
00500                                     updateLibs.Include(x.first);
00501                                     usedLibs.Exclude(x.first);
00502                                     SgPoint t = rest.PointOf();
00503                                     x.first = t;
00504                                     updateLibs.Exclude(t);
00505                                     rest.Exclude(t);
00506                                     usedLibs.Include(t);
00507                                 }
00508                                 if (  ips.Contains(x.second)
00509                                    && ! rest.IsEmpty()
00510                                    )
00511                                 {
00512                                     updateLibs.Include(x.second);
00513                                     usedLibs.Exclude(x.second);
00514                                     SgPoint t = rest.PointOf();
00515                                     x.second = t;
00516                                     updateLibs.Exclude(t);
00517                                     rest.Exclude(t);
00518                                     usedLibs.Include(t);
00519                                 }
00520                                 updateStrg.Include(x);
00521                             }
00522                         }
00523                     }
00524                 }
00525                 miaiStrategy->SetStrategy(updateStrg);
00526                 /* */ return true; /* */
00527             }
00528             for (SgSetIterator it(interior); it; ++it)
00529             {
00530                 SgMiaiPair miaiPair;
00531                 if (Find2BestLibs(*it, updateLibs, testSet, &miaiPair))
00532                 {
00533                     if (miaiPair.first == miaiPair.second)
00534                     {
00535                         SgDebug() <<"\nmiaipair are same: "
00536                                   << SgWritePoint(miaiPair.first)
00537                                   << SgWritePoint(miaiPair.second);
00538                         SgDebug() <<"\ncurrent region is:\n";
00539                         Points().Write(SgDebug(), size);
00540                         SG_ASSERT(false);
00541                     }
00542                     myStrategy.PushBack(miaiPair);
00543                     usedLibs.PushBack(miaiPair.first);
00544                     usedLibs.PushBack(miaiPair.second);
00545                     updateLibs.Exclude(miaiPair.first);
00546                     updateLibs.Exclude(miaiPair.second);
00547                     updateLibs.Include(*it);
00548                     testSet.Exclude(*it);
00549                     changed = true;
00550                 }
00551             }
00552         } // while  loop for recursive finding
00553     }
00554     miaiStrategy->Clear();
00555     return false;
00556 }
00557 
00558 bool GoRegion::ComputeIs1Vital() const
00559 {
00560     if (GetFlag(GO_REGION_STATIC_1VC))
00561         /* */ return true; /* */
00562     else if (ComputedFlag(GO_REGION_STATIC_1VITAL))
00563         /* */ return GetFlag(GO_REGION_STATIC_1VITAL); /* */
00564 
00565     bool is1Vital = true;
00566 
00567     if (! HasLibForAllBlocks()) // no lib here
00568         is1Vital = false;
00569     else
00570     {
00571         if (const_cast<GoRegion*>(this)
00572             ->ComputeAndGetFlag(GO_REGION_PROTECTED_CUTS))
00573         {
00574             is1Vital = true;
00575         }
00576         else if (! Find2ConnForAll())
00577             is1Vital = false;
00578     }
00579 
00580     return is1Vital;
00581 }
00582 
00583 
00584 bool GoRegion::IsCorridor() const
00585 
00586 {
00587     SG_ASSERT(! m_computedFlags.test(GO_REGION_CORRIDOR));
00588     for (SgSetIterator it(Points()); it; ++it)
00589     {
00590         if ((m_bd.NumNeighbors(*it, SgOppBW(Color()))
00591              + m_bd.NumEmptyNeighbors(*it)) > 2)
00592             return false;
00593         if (m_bd.NumNeighbors(*it, Color()) == 0)
00594             // e.g. 1-1 point in 2x2 corner area
00595             return false;
00596     }
00597     return true;
00598 }
00599 
00600 
00601 bool GoRegion::ReplaceChain(const GoChain* old, const GoChain* newChain)
00602 {
00603     SG_ASSERT(old != newChain);
00604     SG_ASSERT(Color() == old->Color());
00605     if (m_chains.Contains(old))
00606     {
00607         m_computedFlags.reset();
00608         m_chains.Exclude(old);
00609         m_chains.Include(newChain);
00610         if (HEAVYCHECK)
00611             SG_ASSERT(m_chains.UniqueElements());
00612 
00613         /* */ return true; /* */
00614     }
00615 
00616     return false;
00617 }
00618 
00619 bool GoRegion::Find2Mergable(GoChain** c1, GoChain** c2) const
00620 {
00621     GoChain* test1; GoChain* test2;
00622     for (SgVectorPairIteratorOf<GoChain> it(m_chains);
00623          it.NextPair(test1, test2);)
00624     {
00625         if (Has2ConnForChains(test1, test2))
00626         {
00627             *c1 = test1;
00628             *c2 = test2;
00629             return true;
00630         }
00631     }
00632     return false;
00633 }
00634 
00635 void GoRegion::Find2FreeLibs(const GoChain* c1, const GoChain* c2,
00636                              SgPoint* lib1, SgPoint* lib2) const
00637 {
00638     SgPointSet libs = Points() & c1->FreeLiberties() & c2->FreeLiberties();
00639     if (CHECK)
00640         SG_ASSERT(libs.MinSetSize(2));
00641     SgSetIterator it(libs);
00642     *lib1 = *it;
00643     ++it;
00644     *lib2 = *it;
00645 }
00646 
00647 void GoRegion::ReInitialize()
00648 {
00649     m_computedFlags.reset();
00650     m_computedFlags.set(GO_REGION_COMPUTED_BLOCKS);
00651     m_flags.reset();
00652     m_miaiStrategy.Clear();
00653     ComputeBasicFlags();
00654 }
00655 
00656 void GoRegion::WriteID(std::ostream& stream) const
00657 {
00658     stream << SgBW(Color()) << " Region "
00659            << SgWritePoint(Points().Center());
00660 }
00661 
00662 const char* kRegionFlagStrings[_GO_REGION_FLAG_COUNT + 1] =
00663 {
00664     "isSmall", "GO_REGION_CORRIDOR", "GO_REGION_STATIC_1VC",
00665     "GO_REGION_1VC", "GO_REGION_STATIC_2V", "GO_REGION_2V",
00666     "GO_REGION_SINGLE_BLOCK_BOUNDARY",
00667     "GO_REGION_OPP_CAN_LIVE_INSIDE",
00668     "GO_REGION_AT_LEAST_SEKI",
00669     "isSafe",
00670     "GO_REGION_PROTECTED_CUTS", "GO_REGION_STATIC_1VITAL",
00671     "is1Vital",
00672     "GO_REGION_USED_FOR_MERGE",
00673     "GO_REGION_VALID",
00674     "GO_REGION_COMPUTED_BLOCKS",
00675     "GO_REGION_COMPUTED_CHAINS",
00676     "GO_REGION_COMPUTED_NAKADE",
00677     "_GO_REGION_FLAG_COUNT"
00678 };
00679 
00680 void GoRegion::Write(std::ostream& stream) const
00681 {
00682     WriteID(stream);
00683     stream << ", "  << Points().Size()
00684            << " Points\nBlocks:" ;
00685     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00686     {
00687         (*it)->WriteID(stream);
00688         if ((*it)->ContainsHealthy(this))
00689             stream << ":Healthy";
00690     }
00691 
00692     stream << "\nInterior Blocks: ";
00693     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
00694     {
00695         if (IsInteriorBlock(*it))
00696             (*it)->WriteID(stream);
00697     }
00698     stream << "\nChains: ";
00699     for (SgVectorIteratorOf<GoChain> it(m_chains); it; ++it)
00700     {
00701         (*it)->WriteID(stream);
00702         if ((*it)->ContainsHealthy(this))
00703             stream << ":Healthy";
00704     }
00705 
00706     stream << "\ncomputed region flags:\n";
00707 
00708     for (int f = 0; f < _GO_REGION_FLAG_COUNT; ++f)
00709     {
00710         if (m_computedFlags.test(f))
00711         {
00712             stream << SgWriteLabel(kRegionFlagStrings[f])
00713                    << SgWriteBoolean(m_flags.test(f))
00714                    << '\n';
00715         }
00716     }
00717     stream << SgWriteLabel("not computed:");
00718     bool first = true;
00719     for (int f = 0; f < _GO_REGION_FLAG_COUNT; ++f)
00720     {
00721         if (! m_computedFlags.test(f))
00722         {
00723             if (first)
00724                 first = false;
00725             else
00726                 stream << ", ";
00727 
00728             stream << kRegionFlagStrings[f];
00729         }
00730     }
00731     stream << '\n'
00732            << SgWriteLabel("eyes:")
00733            << m_eyes
00734            << SgWriteLabel("Miai strategy:")
00735            << m_miaiStrategy
00736            << '\n';
00737 }
00738 
00739 bool GoRegion::GetFlag(GoRegionFlag flag) const
00740 {
00741     SG_ASSERT(IsValid());
00742     SG_ASSERT(m_computedFlags.test(flag));
00743     return m_flags.test(flag);
00744 }
00745 
00746 bool GoRegion::ComputeAndGetFlag(GoRegionFlag flag)
00747 {
00748     SG_ASSERT(IsValid());
00749     ComputeFlag(flag);
00750     return m_flags.test(flag);
00751 }
00752 
00753 bool GoRegion::ComputedFlag(GoRegionFlag flag) const
00754 {
00755     return m_computedFlags.test(flag);
00756 }
00757 
00758 void GoRegion::ComputeFlag(GoRegionFlag flag)
00759 {
00760     if (! m_computedFlags.test(flag))
00761         DoComputeFlag(flag);
00762 }
00763 
00764 void GoRegion::DoComputeFlag(GoRegionFlag flag)
00765 {
00766     SG_ASSERT(! m_computedFlags.test(flag));
00767     switch(flag)
00768     {
00769     case GO_REGION_SMALL:
00770         SetFlag(GO_REGION_SMALL,
00771                 IsSmallRegion(m_bd, Points(), SgOppBW(Color())));
00772         break;
00773     case GO_REGION_CORRIDOR:
00774         SetFlag(GO_REGION_CORRIDOR, IsCorridor());
00775         break;
00776     case GO_REGION_1VC:
00777         SG_ASSERT(false);
00778         break;
00779     case GO_REGION_1VITAL:
00780         SG_ASSERT(false);
00781         break;
00782     case GO_REGION_STATIC_1VITAL:
00783         {
00784             bool is = ComputeIs1Vital();
00785             SetFlag(GO_REGION_STATIC_1VITAL, is);
00786             if (is)
00787                 SetFlag(GO_REGION_1VITAL, true);
00788         }
00789         break;
00790     case GO_REGION_STATIC_1VC:
00791         {
00792             bool is = StaticIs1VitalAndConnected();
00793             SetFlag(GO_REGION_STATIC_1VC, is);
00794             if (is)
00795                 SetFlag(GO_REGION_1VC, true);
00796         }
00797         break;
00798     case GO_REGION_2V:
00799         SG_ASSERT(false);
00800         break;
00801     case GO_REGION_STATIC_2V:
00802         {
00803             bool is = Has2SureLibs(&m_miaiStrategy);
00804             SetFlag(GO_REGION_STATIC_2V, is);
00805             if (is)
00806             {
00807                 SetFlag(GO_REGION_2V, true);
00808             }
00809         }
00810         break;
00811     case GO_REGION_SINGLE_BLOCK_BOUNDARY:
00812         SG_ASSERT(m_computedFlags.test(GO_REGION_COMPUTED_BLOCKS));
00813         SetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY, m_blocks.MaxLength(1));
00814         // can have empty m_blocks list on empty board.
00815         //  SgPointSet boundary = pts.Border(board);
00816         //  boundary.ExpandToBlocks(board);
00817         //  SG_ASSERT(boundary.SubsetOf(board.All(color)));
00818         //      if (IsSingleBlock(board, boundary, color))
00819         break;
00820     case GO_REGION_OPP_CAN_LIVE_INSIDE: // assuming Dep() is safe.
00821         SetFlag(GO_REGION_OPP_CAN_LIVE_INSIDE,
00822                 MightMakeLife(m_bd, Points(), Dep(), SgOppBW(Color())));
00823         break;
00824     case GO_REGION_AT_LEAST_SEKI:
00825         SG_ASSERT(false);
00826         break;
00827     case GO_REGION_SAFE:
00828         SG_ASSERT(false);
00829         break;
00830     case GO_REGION_PROTECTED_CUTS:
00831         SetFlag(GO_REGION_PROTECTED_CUTS, ProtectedCuts(m_bd));
00832         break;
00833         break;
00834     case GO_REGION_COMPUTED_NAKADE:
00835         ComputeNakade();
00836         SetFlag(GO_REGION_COMPUTED_NAKADE, true);
00837         break;
00838     default:
00839         SG_ASSERT(false);
00840     }
00841     m_computedFlags.set(flag);
00842 }
00843 
00844 void GoRegion::ComputeSingleBlockEyeSpace()
00845 {
00846     SG_ASSERT(m_blocks.IsLength(1));
00847     const int nu = Points().Size();
00848     SG_ASSERT(nu > 0);
00849 
00850     if (nu <= 2)
00851     {
00852         m_eyes.SetEyes(1, 1);
00853     }
00854     else
00855     {
00856         bool isNakade = false, makeNakade = false, maybeSeki = false;
00857         bool sureSeki = false;
00858         bool makeFalse = false;
00859         if (nu <= 7)
00860         {
00861             // test nakade shape. this may set the m_vitalPoint of the zone
00862             SgPoint vitalP(SG_NULLPOINT);
00863             TestNakade(Points(), m_bd, m_color, true,
00864                        isNakade, makeNakade, makeFalse,
00865                        maybeSeki, sureSeki,
00866                        &vitalP);
00867             if (makeNakade || makeFalse)
00868                 m_vitalPoint = vitalP;
00869         }
00870         if (sureSeki)
00871             m_eyes.SetLocalSeki();
00872         else if (maybeSeki)
00873         {
00874             m_eyes.SetMinEyes(0);
00875             m_eyes.SetMaxEyes(2);
00876             int potEyes = isNakade ? 1 : 2;
00877             m_eyes.SetExactPotEyes(potEyes);
00878             m_eyes.SetMaybeLocalSeki();
00879         }
00880         else if (isNakade || makeNakade)
00881         {
00882             int potEyes = isNakade ? 1 : 2;
00883             m_eyes.SetEyes(1, potEyes);
00884         }
00885         else if (makeFalse)
00886             m_eyes.SetEyes(0, 1);
00887 
00888         else // @todo: huge areas without opp. are alive, at least seki,
00889              // possible eye space if filled by safe opp, etc.
00890         {
00891             m_eyes.SetMinEyes(0);
00892             m_eyes.SetMaxEyes(2);
00893             m_eyes.SetExactPotEyes(2);
00894         }
00895     }
00896 }
00897 
00898 void GoRegion::ComputeMultipleBlockEyeSpace()
00899 {
00900     SG_ASSERT(m_blocks.MinLength(2));
00901     const int nu = m_points.Size();
00902     SG_ASSERT (nu > 0);
00903 
00904     int minNuEyes = 0;
00905     //if (m_blocks.IsLength(2) && TwoBlockEyeIsSafe())
00906     //        minNuEyes = 1;
00907 
00908     bool isNakade = false;
00909     bool makeNakade = false;
00910     bool makeFalse = false;
00911 
00912     if (nu <= 2)
00913     {
00914         if (minNuEyes == 1)
00915         {
00916             m_eyes.SetEyes(1, 1);
00917             /* */ return; /* */
00918         }
00919         /*
00920         bool eyeThreatened = false, eyeSafe = false;
00921         bool isFalse = FalseEye(eyeThreatened, eyeSafe);
00922         if (isFalse)
00923         {
00924             SG_ASSERT(minNuEyes == 0);
00925             m_eyes.SetEyes(0, 0);
00926         }
00927         else if (eyeThreatened)
00928         {
00929             SG_ASSERT(minNuEyes == 0);
00930             m_eyes.SetEyes(0, 1);
00931         }
00932         else
00933             m_eyes.SetEyes(1, 1);
00934         */
00935         /* */ return; /* */
00936     }
00937     else if (nu <= 7)
00938     {
00939         // test nakade shape. this may set the m_vitalPoint of the zone
00940         SgPoint vitalP(SG_NULLPOINT);
00941         bool maybeSeki = false, sureSeki = false;
00942         TestNakade(m_points, m_bd, m_color, true,
00943                    isNakade, makeNakade,
00944                    makeFalse,
00945                    maybeSeki, sureSeki,
00946                    &vitalP);
00947         if (makeNakade)
00948             m_vitalPoint = vitalP;
00949     }
00950     if (makeFalse)
00951         m_eyes.SetEyes(0, 1);
00952     else if (isNakade)
00953         m_eyes.SetEyes(1, 1);
00954     else if (makeNakade)
00955         m_eyes.SetEyes(1, 2);
00956     else // todo: huge areas without opp. are alive, at least seki,
00957          // possible eye space if filled by safe opp, etc.
00958     {
00959         m_eyes.SetMinEyes(minNuEyes);
00960         m_eyes.SetMaxEyes(2);
00961         m_eyes.SetExactPotEyes(2);
00962     }
00963 
00964 }
00965 
00966 void GoRegion::ComputeEyeSpace()
00967 {
00968     if (m_blocks.IsLength(1))
00969         ComputeSingleBlockEyeSpace();
00970     else
00971         ComputeMultipleBlockEyeSpace();
00972 
00973     /* */ return; /* */
00974 }
00975 
00976 void GoRegion::ComputeNakade()
00977 {
00978 
00979     if (GetFlag(GO_REGION_STATIC_2V))
00980     {
00981         m_eyes.SetMinEyes(2);
00982     }
00983     else
00984     {
00985         m_eyes.SetUnknown();
00986 
00987         bool is1vital = ComputeAndGetFlag(GO_REGION_STATIC_1VITAL);
00988         if (is1vital)
00989             m_eyes.SetMinEyes(1);
00990 
00991         bool isNakade = false, makeNakade = false;
00992         bool maybeSeki = false, sureSeki = false;
00993         bool makeFalse = false;
00994         int nu = Points().Size();
00995 
00996         if (SgUtil::InRange(nu, 3, 7))
00997         {
00998             SgPoint vitalP(SG_NULLPOINT);
00999             TestNakade(m_points, m_bd, m_color, true,
01000                        isNakade, makeNakade,
01001                        makeFalse,
01002                        maybeSeki, sureSeki, &vitalP);
01003             if (isNakade)
01004             {
01005                 m_eyes.SetMaxEyes(1);
01006                 m_eyes.SetMaxPotEyes(1);
01007             }
01008             else if (makeNakade)
01009             {
01010                 m_vitalPoint = vitalP;
01011                 m_eyes.SetMinPotEyes(2);
01012             }
01013             // @todo handle seki.
01014             // @todo handle makeFalse.
01015         }
01016     }
01017 
01018     m_eyes.Normalize();
01019 }
01020 
01021 void GoRegion::Fini()
01022 {
01023     GoChain::Fini();
01024     GoBlock::Fini();
01025     GoRegionBoard::Fini();
01026 #ifndef NDEBUG
01027     SG_ASSERT(s_alloc == s_free);
01028 #endif
01029 }
01030 
01031 #ifndef NDEBUG
01032 int GoRegion::s_alloc = 0;
01033 
01034 int GoRegion::s_free = 0;
01035 #endif
01036 
01037 void GoRegion::ComputeBasicFlags()
01038 {
01039     SG_ASSERT(! IsValid());
01040     m_flags.set(GO_REGION_VALID);
01041     DoComputeFlag(GO_REGION_SMALL);
01042     DoComputeFlag(GO_REGION_CORRIDOR);
01043     DoComputeFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY);
01044     DoComputeFlag(GO_REGION_STATIC_1VC);
01045     DoComputeFlag(GO_REGION_STATIC_2V);
01046     SetFlag(GO_REGION_USED_FOR_MERGE, false);
01047 }
01048 
01049 bool GoRegion::Has2Conn() const
01050 {
01051     SG_ASSERT(m_chains.IsLength(2));
01052     const GoChain* c1 = m_chains.Front();
01053     const GoChain* c2 = m_chains.Back();
01054     return Has2ConnForChains(c1, c2);
01055 }
01056 
01057 bool GoRegion::Has2ConnForChains(const GoChain* c1,
01058                                  const GoChain* c2) const
01059 {
01060     return (  Points()
01061             & c1->FreeLiberties()
01062             & c2->FreeLiberties()
01063            ).MinSetSize(2);
01064 }
01065 
01066 bool GoRegion::Safe2Cuts(const GoBoard& board) const
01067 {
01068     SG_ASSERT(m_blocks.IsLength(2));
01069     const int size = board.Size();
01070     GoBlock* block1 = m_blocks.Front();
01071     GoBlock* block2 = m_blocks.Back();
01072     SgPointSet cuts(Points());
01073     cuts -= board.AllEmpty();
01074     if (cuts.IsEmpty())
01075         /* */ return true; /* */
01076     cuts &= block1->Stones().Border(size);
01077     cuts &= block2->Stones().Border(size);
01078     return cuts.IsEmpty();
01079 }
01080 
01081 bool GoRegion::ProtectedCuts(const GoBoard& board) const
01082 {
01083     if (! GetFlag(GO_REGION_CORRIDOR))
01084         return false;
01085     if (m_blocks.IsLength(2))
01086         /* */ return Safe2Cuts(board); /* */ // easy case of only 2 blocks
01087 
01088     bool prot = true;
01089     SgPointSet allCuts;
01090     const int size = board.Size();
01091     GoBlock* block1, *block2;
01092     for (SgVectorPairIteratorOf<GoBlock> it(m_blocks);
01093          it.NextPair(block1, block2);)
01094     {
01095         SgPointSet lib1(block1->Stones().Border(size));
01096         SgPointSet lib2(block2->Stones().Border(size));
01097         SgPointSet cuts(lib1 & lib2 & Points());
01098         if (! cuts.SubsetOf(board.AllEmpty()))
01099         // cut occupied by opponent. Bad for us.
01100             return false;
01101         else
01102             allCuts |= cuts;
01103     }
01104     // no eye space left ? hard to distinguish false eyes from ok
01105     // AR why must this be checked??? Should not matter for flat regions.
01106     // Try to take it out.
01107     //if (Points().SubsetOf(allCuts | allCuts.Border()))
01108     //  prot = false;
01109 
01110     return prot;
01111 }
01112 
01113 void GoRegion::FindBlocks(const GoRegionBoard& ra)
01114 {
01115     SG_ASSERT(m_blocks.IsEmpty());
01116     const int size = m_bd.Size();
01117     SgPointSet area(Points().Border(size));
01118 
01119     for (SgVectorIteratorOf<GoBlock> it(ra.AllBlocks(Color())); it; ++it)
01120     {
01121         if ((*it)->Stones().Overlaps(area))
01122             m_blocks.PushBack(*it);
01123     }
01124     m_computedFlags.set(GO_REGION_COMPUTED_BLOCKS);
01125 }
01126 
01127 SgPointSet GoRegion::BlocksPoints() const
01128 {
01129     SgPointSet points;
01130     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
01131         points |= (*it)->Stones();
01132     return points;
01133 }
01134 
01135 void GoRegion::SetBlocks(const SgVectorOf<GoBlock>& blocks)
01136 {
01137     SG_ASSERT(m_blocks.IsEmpty());
01138     SG_ASSERT(! blocks.IsEmpty());
01139     const int size = m_bd.Size();
01140     SgPointSet area(Points().Border(size));
01141     for (SgVectorIteratorOf<GoBlock> it(blocks); it; ++it)
01142     {
01143         if ((*it)->Stones().Overlaps(area))
01144             m_blocks.PushBack(*it);
01145     }
01146     m_computedFlags.set(GO_REGION_COMPUTED_BLOCKS);
01147 }
01148 
01149 void GoRegion::FindChains(const GoRegionBoard& ra)
01150 {
01151     SG_ASSERT(m_chains.IsEmpty());
01152     const int size = m_bd.Size();
01153     SgPointSet area(Points().Border(size));
01154     for (SgVectorIteratorOf<GoChain> it(ra.AllChains(Color())); it; ++it)
01155     {
01156         if ((*it)->Stones().Overlaps(area))
01157             m_chains.PushBack(*it);
01158     }
01159     m_computedFlags.set(GO_REGION_COMPUTED_CHAINS);
01160 }
01161 
01162 bool GoRegion::IsSurrounded(const SgVectorOf<GoBlock>& blocks) const
01163 {
01164     const int size = m_bd.Size();
01165     SgPointSet adj(Points().Border(size));
01166     for (SgVectorIteratorOf<GoBlock> it(blocks); it; ++it)
01167         adj -= (*it)->Stones();
01168     return adj.IsEmpty();
01169 }
01170 
01171 bool GoRegion::HealthyForSomeBlock(const SgVectorOf<GoBlock>& blocks) const
01172 {
01173     for (SgVectorIteratorOf<GoBlock> it(blocks); it; ++it)
01174         if ((*it)->ContainsHealthy(this))
01175             /* */ return true; /* */
01176     return false;
01177 }
01178 
01179 bool GoRegion::SomeBlockIsSafe() const
01180 {
01181     for (SgVectorIteratorOf<GoBlock> it(Blocks()); it; ++it)
01182         if ((*it)->IsSafe())
01183             return true;
01184     return false;
01185 }
01186 
01187 bool GoRegion::AllBlockIsSafe() const
01188 {
01189     for (SgVectorIteratorOf<GoBlock> it(Blocks()); it; ++it)
01190         if (!(*it)->IsSafe())
01191             return false;
01192     return true;
01193 }
01194 
01195 bool GoRegion::ComputedVitalForDepth(int depth) const
01196 {
01197     return    GetFlag(GO_REGION_STATIC_1VC)
01198            || (ComputedFlag(GO_REGION_1VC) && m_1vcDepth >= depth);
01199 }
01200 
01201 
01202 void GoRegion::CheckConsistency() const
01203 {
01204     SG_ASSERT(Points().Disjoint(m_bd.All(Color())));
01205     SG_ASSERT(Points().Border(m_bd.Size()).SubsetOf(m_bd.All(Color())));
01206     SG_ASSERT(Points().IsConnected());
01207     SgPointSet blockPts;
01208     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
01209     {
01210         SG_ASSERT(AdjacentToBlock((*it)->Anchor()));
01211         blockPts |= (*it)->Stones();
01212     }
01213     SG_ASSERT(Points().Border(m_bd.Size()).SubsetOf(blockPts));
01214 }
01215 
01216 
01217 void GoRegion::RemoveBlock(const GoBlock* b)
01218 {
01219     bool found = m_blocks.Exclude(b);
01220     SG_UNUSED(found);
01221     SG_ASSERT(found);
01222     ResetNonBlockFlags();
01223 }
01224 
01225 bool GoRegion::AdjacentToSomeBlock(const SgVector<SgPoint>& anchors) const
01226 {
01227     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
01228     {
01229         if (anchors.Contains((*it)->Anchor()))
01230             /* */ return true; /* */
01231     }
01232     return false;
01233 }
01234 
01235 bool GoRegion::AdjacentToBlock(SgPoint anchor) const
01236 {
01237     for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
01238     {
01239         if ((*it)->Anchor() == anchor)
01240             /* */ return true; /* */
01241     }
01242     return false;
01243 }
01244 
01245 void GoRegion::OnAddStone(SgPoint p)
01246 {
01247     SG_ASSERT(m_points.Contains(p));
01248     m_points.Exclude(p);
01249     ResetNonBlockFlags();
01250 }
01251 
01252 void GoRegion::OnRemoveStone(SgPoint p)
01253 {
01254     SG_ASSERT(! m_points.Contains(p));
01255     m_points.Include(p);
01256     ResetNonBlockFlags();
01257 }
01258 
01259 std::ostream& operator<<(std::ostream& stream, const GoRegion& r)
01260 {
01261     r.Write(stream);
01262     return stream;
01263 }
01264 


17 Jun 2010 Doxygen 1.4.7