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


Sun Mar 13 2011 Doxygen 1.7.1