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