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