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