00001
00002
00003
00004
00005
00006 #include "SgSystem.h"
00007 #include "GoLadder.h"
00008
00009 #include <algorithm>
00010 #include <memory>
00011 #include "GoBoard.h"
00012 #include "GoBoardUtil.h"
00013 #include "GoModBoard.h"
00014 #include "SgVector.h"
00015 #include "SgStack.h"
00016
00017 using namespace std;
00018 using GoBoardUtil::NeighborsOfColor;
00019 using GoBoardUtil::PlayIfLegal;
00020
00021
00022
00023 namespace {
00024
00025 const int GOOD_FOR_PREY = 1000;
00026
00027 const int GOOD_FOR_HUNTER = -1000;
00028
00029 }
00030
00031
00032
00033 GoLadder::GoLadder()
00034 {
00035 }
00036
00037 inline bool GoLadder::CheckMoveOverflow() const
00038 {
00039 return (m_bd->MoveNumber() >= m_maxMoveNumber);
00040 }
00041
00042 void GoLadder::InitMaxMoveNumber()
00043 {
00044
00045
00046 const int RESERVE = 5;
00047 m_maxMoveNumber = min(m_bd->MoveNumber() + MAX_LADDER_MOVES,
00048 GO_MAX_NUM_MOVES - RESERVE);
00049 }
00050
00051
00052
00053
00054 void GoLadder::MarkStonesAsPrey(SgPoint p, SgVector<SgPoint>* stones)
00055 {
00056 SG_ASSERT(m_bd->IsValidPoint(p));
00057 if (m_bd->Occupied(p))
00058 {
00059 for (GoBoard::StoneIterator it(*m_bd, p); it; ++it)
00060 {
00061 SgPoint p = *it;
00062 m_partOfPrey.Include(p);
00063 if (stones)
00064 stones->PushBack(p);
00065 }
00066 }
00067 }
00068
00069
00070
00071
00072
00073
00074
00075
00076 void GoLadder::FilterAdjacent(GoPointList& adjBlocks)
00077 {
00078 GoPointList temp;
00079 for (GoPointList::Iterator it(adjBlocks); it; ++it)
00080 {
00081 SgPoint block = *it;
00082 if (m_bd->IsColor(block, m_hunterColor)
00083 && m_bd->InAtari(block)
00084 && BlockIsAdjToPrey(block, 1))
00085 temp.PushBack(block);
00086 }
00087 ReduceToBlocks(temp);
00088 adjBlocks = temp;
00089 }
00090
00091 bool GoLadder::PointIsAdjToPrey(SgPoint p)
00092 {
00093 return m_partOfPrey[p - SG_NS]
00094 || m_partOfPrey[p - SG_WE]
00095 || m_partOfPrey[p + SG_WE]
00096 || m_partOfPrey[p + SG_NS];
00097 }
00098
00099 bool GoLadder::BlockIsAdjToPrey(SgPoint p, int numAdj)
00100 {
00101 SG_ASSERT(m_bd->IsColor(p, m_hunterColor));
00102 for (GoBoard::StoneIterator it(*m_bd, p); it; ++it)
00103 if (PointIsAdjToPrey(*it) && --numAdj == 0)
00104 return true;
00105 return false;
00106 }
00107
00108
00109
00110
00111 int GoLadder::PlayHunterMove(int depth, SgPoint move, SgPoint lib1,
00112 SgPoint lib2, const GoPointList& adjBlk,
00113 SgVector<SgPoint>* sequence)
00114 {
00115 SG_ASSERT(move == lib1 || move == lib2);
00116
00117 int result = 0;
00118 if (PlayIfLegal(*m_bd, move, m_hunterColor))
00119 {
00120
00121
00122
00123
00124
00125
00126
00127
00128 GoPointList newAdj;
00129 if (m_bd->InAtari(move))
00130 newAdj.PushBack(move);
00131 for (GoPointList::Iterator it(adjBlk); it; ++it)
00132 {
00133 SgPoint block = *it;
00134 if (! m_bd->AreInSameBlock(block, move))
00135 {
00136 if (! m_bd->CapturingMove() || m_bd->InAtari(block))
00137 newAdj.PushBack(block);
00138 }
00139 }
00140 if (move == lib1)
00141 lib1 = lib2;
00142 result = PreyLadder(depth + 1, lib1, newAdj, sequence);
00143 if (sequence)
00144 sequence->PushBack(move);
00145 m_bd->Undo();
00146 }
00147 else
00148 {
00149 if (sequence)
00150 sequence->Clear();
00151 result = GOOD_FOR_PREY - depth;
00152 }
00153 return result;
00154 }
00155
00156
00157
00158
00159
00160 int GoLadder::PlayPreyMove(int depth, SgPoint move, SgPoint lib1,
00161 const GoPointList& adjBlk,
00162 SgVector<SgPoint>* sequence)
00163 {
00164 int result = 0;
00165 GoPointList newAdj(adjBlk);
00166 SgVector<SgPoint> newLib;
00167 SgVector<SgPoint> newStones;
00168 SgVector<SgPoint> neighbors;
00169 if (move == lib1)
00170 {
00171 NeighborsOfColor(*m_bd, move, m_preyColor, &neighbors);
00172 for (SgVectorIterator<SgPoint> iter(neighbors); iter; ++iter)
00173 {
00174 SgPoint block = *iter;
00175 if (! m_partOfPrey[block])
00176 {
00177 MarkStonesAsPrey(block, &newStones);
00178 GoPointList temp =
00179 GoBoardUtil::AdjacentStones(*m_bd, block);
00180 newAdj.PushBackList(temp);
00181 for (GoBoard::LibertyIterator it(*m_bd, block); it; ++it)
00182 newLib.Include(*it);
00183 }
00184 }
00185 m_partOfPrey.Include(move);
00186 }
00187 if (PlayIfLegal(*m_bd, move, m_preyColor))
00188 {
00189 if (move == lib1)
00190 {
00191 NeighborsOfColor(*m_bd, move, SG_EMPTY, &neighbors);
00192 for (SgVectorIterator<SgPoint> iter(newLib); iter; ++iter)
00193 {
00194 SgPoint point = *iter;
00195
00196
00197 if (m_bd->IsEmpty(point))
00198 neighbors.Include(point);
00199 }
00200 }
00201 else
00202 {
00203 neighbors.PushBack(lib1);
00204 }
00205 if (m_bd->CapturingMove())
00206 {
00207
00208 for (GoPointList::Iterator it(m_bd->CapturedStones()); it;
00209 ++it)
00210 {
00211 SgPoint stone = *it;
00212 if (PointIsAdjToPrey(stone))
00213 neighbors.Include(stone);
00214 }
00215 }
00216 SG_ASSERT(! neighbors.IsEmpty());
00217 lib1 = neighbors[0];
00218 SG_ASSERT(m_bd->IsEmpty(lib1));
00219 SgSList<SgPoint,4> temp =
00220 NeighborsOfColor(*m_bd, move, m_hunterColor);
00221 newAdj.PushBackList(temp);
00222 FilterAdjacent(newAdj);
00223
00224 if (neighbors.Length() == 1)
00225 result = HunterLadder(depth + 1, lib1, newAdj, sequence);
00226 else if (neighbors.Length() == 2)
00227 {
00228 SgPoint lib2 = neighbors[1];
00229 SG_ASSERT(m_bd->IsEmpty(lib2));
00230 result = HunterLadder(depth + 1, lib1, lib2, newAdj, sequence);
00231 }
00232 else
00233 {
00234 if (sequence)
00235 sequence->Clear();
00236 result = GOOD_FOR_PREY - (depth + 1);
00237 }
00238 if (sequence)
00239 sequence->PushBack(move);
00240 m_bd->Undo();
00241 }
00242 else
00243 {
00244 if (sequence)
00245 sequence->Clear();
00246 result = GOOD_FOR_HUNTER + depth;
00247 }
00248 m_partOfPrey.Exclude(move);
00249 m_partOfPrey.Exclude(newStones);
00250
00251 return result;
00252 }
00253
00254 int GoLadder::PreyLadder(int depth, SgPoint lib1,
00255 const GoPointList& adjBlk,
00256 SgVector<SgPoint>* sequence)
00257 {
00258 if (CheckMoveOverflow())
00259 return GOOD_FOR_PREY;
00260 int result = 0;
00261 for (GoPointList::Iterator iter(adjBlk); iter; ++iter)
00262 {
00263 SgPoint block = *iter;
00264 SgPoint move = *GoBoard::LibertyIterator(*m_bd, block);
00265 if (BlockIsAdjToPrey(block, 2))
00266 {
00267 if (sequence)
00268 sequence->SetTo(move);
00269 result = GOOD_FOR_PREY - depth;
00270 }
00271 else if (move != lib1)
00272 {
00273 result = PlayPreyMove(depth, move, lib1, adjBlk, sequence);
00274 }
00275 if (0 < result)
00276 break;
00277 }
00278 if (result <= 0)
00279 {
00280 if (sequence)
00281 {
00282 SgVector<SgPoint> seq2;
00283 int result2 = PlayPreyMove(depth, lib1, lib1, adjBlk, &seq2);
00284 if (result < result2 || result == 0)
00285 {
00286 result = result2;
00287 sequence->SwapWith(&seq2);
00288 }
00289 }
00290 else
00291 {
00292 int result2 = PlayPreyMove(depth, lib1, lib1, adjBlk, 0);
00293 if (result < result2 || result == 0)
00294 result = result2;
00295 }
00296 }
00297 return result;
00298 }
00299
00300 int GoLadder::HunterLadder(int depth, SgPoint lib1, const GoPointList& adjBlk,
00301 SgVector<SgPoint>* sequence)
00302 {
00303 SG_UNUSED(adjBlk);
00304 if (CheckMoveOverflow())
00305 return GOOD_FOR_PREY;
00306 if (sequence)
00307 sequence->SetTo(lib1);
00308
00309
00310 return GOOD_FOR_HUNTER + depth;
00311 }
00312
00313 int GoLadder::HunterLadder(int depth, SgPoint lib1, SgPoint lib2,
00314 const GoPointList& adjBlk,
00315 SgVector<SgPoint>* sequence)
00316 {
00317 if (CheckMoveOverflow())
00318 return GOOD_FOR_PREY;
00319 int result = 0;
00320 if (m_bd->NumEmptyNeighbors(lib1) < m_bd->NumEmptyNeighbors(lib2))
00321 {
00322 swap(lib1, lib2);
00323 }
00324 if (m_bd->NumEmptyNeighbors(lib1) == 3
00325 && ! SgPointUtil::AreAdjacent(lib1, lib2))
00326 {
00327
00328
00329 m_bd->Play(lib1, m_hunterColor);
00330 result = PreyLadder(depth + 1, lib2, adjBlk, sequence);
00331 if (sequence)
00332 sequence->PushBack(lib1);
00333 m_bd->Undo();
00334 }
00335 else
00336 {
00337
00338 if (! adjBlk.IsEmpty()
00339 && *GoBoard::LibertyIterator(*m_bd, adjBlk[0]) == lib2)
00340 {
00341 swap(lib1, lib2);
00342 }
00343 result = PlayHunterMove(depth, lib1, lib1, lib2,
00344 adjBlk, sequence);
00345 if (0 <= result)
00346 {
00347 if (sequence)
00348 {
00349 SgVector<SgPoint> seq2;
00350 int result2 = PlayHunterMove(depth, lib2, lib1, lib2,
00351 adjBlk, &seq2);
00352 if (result2 < result)
00353 { result = result2;
00354 sequence->SwapWith(&seq2);
00355 }
00356 }
00357 else
00358 {
00359 int result2 = PlayHunterMove(depth, lib2, lib1, lib2,
00360 adjBlk, 0);
00361 if (result2 < result)
00362 result = result2;
00363 }
00364 }
00365 }
00366 return result;
00367 }
00368
00369 void GoLadder::ReduceToBlocks(GoPointList& stones)
00370 {
00371
00372 if (stones.IsEmpty())
00373 ;
00374 else if (stones.Length() <= 1)
00375 {
00376 if (m_bd->IsEmpty(stones[0]))
00377 stones.Clear();
00378 }
00379 else
00380 {
00381 GoPointList visited;
00382
00383
00384 GoPointList result;
00385 for (GoPointList::Iterator it(stones); it; ++it)
00386 {
00387 SgPoint stone = *it;
00388 if (m_bd->Occupied(stone) && ! visited.Contains(stone))
00389 {
00390 result.PushBack(stone);
00391 for (GoBoard::StoneIterator it(*m_bd, stone); it; ++it)
00392 visited.PushBack(*it);
00393 }
00394 }
00395 stones = result;
00396 }
00397 }
00398
00399
00400 int GoLadder::Ladder(const GoBoard& bd, SgPoint prey, SgBlackWhite toPlay,
00401 SgVector<SgPoint>* sequence, bool twoLibIsEscape)
00402 {
00403 GoModBoard modBoard(bd);
00404 m_bd = &modBoard.Board();
00405 InitMaxMoveNumber();
00406 if (sequence)
00407 sequence->Clear();
00408 if (! m_bd->Occupied(prey))
00409 return 0;
00410 if (CheckMoveOverflow())
00411 return GOOD_FOR_PREY;
00412 int result = 0;
00413 m_preyColor = m_bd->GetStone(prey);
00414 m_hunterColor = SgOppBW(m_preyColor);
00415 int numLib = m_bd->NumLiberties(prey);
00416 if (2 < numLib)
00417 result = GOOD_FOR_PREY;
00418 else
00419 {
00420 GoBoard::LibertyIterator libit(*m_bd, prey);
00421 SgPoint lib1 = *libit;
00422 m_partOfPrey.Clear();
00423 MarkStonesAsPrey(prey);
00424 GoPointList adjBlk = GoBoardUtil::AdjacentStones(*m_bd, prey);
00425 FilterAdjacent(adjBlk);
00426 if (toPlay == m_preyColor)
00427 {
00428 if (numLib == 1)
00429 result = PreyLadder(0, lib1, adjBlk, sequence);
00430 else if (twoLibIsEscape)
00431
00432
00433 result = GOOD_FOR_PREY;
00434 else
00435 {
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446 SgVector<SgPoint> movesToTry;
00447
00448
00449 adjBlk = GoBoardUtil::AdjacentStones(*m_bd, prey);
00450 ReduceToBlocks(adjBlk);
00451 for (GoPointList::Iterator iterAdj(adjBlk); iterAdj;
00452 ++iterAdj)
00453 {
00454 SgPoint block = *iterAdj;
00455 SG_ASSERT(m_bd->IsColor(block, m_hunterColor));
00456 SG_ASSERT(BlockIsAdjToPrey(block, 1));
00457 if (m_bd->NumLiberties(block) <= 2)
00458 for (GoBoard::LibertyIterator it(*m_bd, block); it;
00459 ++it)
00460 movesToTry.PushBack(*it);
00461 }
00462
00463
00464 ++libit;
00465 SgPoint lib2 = *libit;
00466 movesToTry.PushBack(lib1);
00467 movesToTry.PushBack(lib2);
00468
00469
00470 SgVector<SgPoint> neighbors;
00471 NeighborsOfColor(*m_bd, lib1, SG_EMPTY, &neighbors);
00472 movesToTry.Concat(&neighbors);
00473 NeighborsOfColor(*m_bd, lib2, SG_EMPTY, &neighbors);
00474 movesToTry.Concat(&neighbors);
00475
00476
00477 for (SgVectorIterator<SgPoint> it(movesToTry); it; ++it)
00478 {
00479 if (PlayIfLegal(*m_bd, *it, m_preyColor))
00480 {
00481 if (Ladder(bd, prey, m_hunterColor, 0, twoLibIsEscape)
00482 > 0)
00483 {
00484 if (sequence)
00485 sequence->PushBack(*it);
00486 result = GOOD_FOR_PREY;
00487 }
00488 m_bd->Undo();
00489 }
00490 if (result != 0)
00491 break;
00492 }
00493
00494
00495
00496
00497
00498 if (result == 0)
00499 result = GOOD_FOR_HUNTER;
00500 }
00501 }
00502 else
00503 {
00504 if (IsSnapback(prey))
00505 result = GOOD_FOR_PREY;
00506 else
00507 {
00508 ++libit;
00509 if (libit)
00510 result = HunterLadder(0, lib1, *libit, adjBlk, sequence);
00511 else
00512 result = HunterLadder(0, lib1, adjBlk, sequence);
00513 }
00514 }
00515 }
00516 if (sequence)
00517 sequence->Reverse();
00518 return result;
00519 }
00520
00521
00522
00523
00524
00525 bool GoLadder::IsSnapback(SgPoint prey)
00526 {
00527 bool isSnapback = false;
00528 if (m_bd->IsSingleStone(prey) && m_bd->InAtari(prey))
00529 {
00530 SgPoint liberty = *GoBoard::LibertyIterator(*m_bd, prey);
00531 if (PlayIfLegal(*m_bd, liberty, SgOppBW(m_bd->GetStone(prey))))
00532 {
00533 isSnapback = (m_bd->InAtari(liberty)
00534 && ! m_bd->IsSingleStone(liberty));
00535 m_bd->Undo();
00536 }
00537 }
00538 return isSnapback;
00539 }
00540
00541
00542
00543 bool GoLadderUtil::Ladder(const GoBoard& bd, SgPoint prey,
00544 SgBlackWhite toPlay, bool twoLibIsEscape,
00545 SgVector<SgPoint>* sequence)
00546 {
00547 SG_ASSERT(bd.IsValidPoint(prey));
00548 SG_ASSERT(bd.Occupied(prey));
00549
00550
00551
00552
00553
00554 #ifndef NDEBUG
00555 SgHashCode oldHash = bd.GetHashCode();
00556 #endif
00557 GoLadder ladder;
00558 int result = ladder.Ladder(bd, prey, toPlay, sequence, twoLibIsEscape);
00559 #ifndef NDEBUG
00560
00561 SG_ASSERT(oldHash == bd.GetHashCode());
00562 #endif
00563 SG_ASSERT(result != 0);
00564 return (result < 0);
00565 }
00566
00567 GoLadderStatus GoLadderUtil::LadderStatus(const GoBoard& bd, SgPoint prey,
00568 bool twoLibIsEscape,
00569 SgPoint* toCapture,
00570 SgPoint* toEscape)
00571 {
00572 SG_ASSERT(bd.IsValidPoint(prey));
00573 SG_ASSERT(bd.Occupied(prey));
00574 #ifndef NDEBUG
00575 SgHashCode oldHash = bd.GetHashCode();
00576 #endif
00577
00578
00579 GoLadder ladder;
00580 SgBlackWhite preyColor = bd.GetStone(prey);
00581 SgVector<SgPoint> captureSequence;
00582 GoLadderStatus status = GO_LADDER_ESCAPED;
00583 if (ladder.Ladder(bd, prey, SgOppBW(preyColor), &captureSequence,
00584 twoLibIsEscape) < 0)
00585 {
00586 SgVector<SgPoint> escapeSequence;
00587 if (ladder.Ladder(bd, prey, preyColor, &escapeSequence,
00588 twoLibIsEscape) < 0)
00589 status = GO_LADDER_CAPTURED;
00590 else
00591 {
00592 status = GO_LADDER_UNSETTLED;
00593
00594
00595 SG_ASSERT(captureSequence.NonEmpty());
00596
00597 SG_ASSERT(twoLibIsEscape || escapeSequence.NonEmpty());
00598 if (toCapture)
00599 *toCapture = captureSequence.Front();
00600 if (toEscape)
00601 *toEscape = escapeSequence.IsEmpty() ? SG_PASS :
00602 escapeSequence.Front();
00603 }
00604 }
00605 #ifndef NDEBUG
00606
00607 SG_ASSERT(oldHash == bd.GetHashCode());
00608 #endif
00609 return status;
00610 }
00611
00612 bool GoLadderUtil::IsProtectedLiberty(const GoBoard& bd, SgPoint liberty,
00613 SgBlackWhite color)
00614 {
00615 bool ignoreLadder;
00616 bool ignoreKo;
00617 return IsProtectedLiberty(bd, liberty, color, ignoreLadder, ignoreKo,
00618 true);
00619 }
00620
00621 bool GoLadderUtil::IsProtectedLiberty(const GoBoard& bd1, SgPoint liberty,
00622 SgBlackWhite col, bool& byLadder,
00623 bool& isKoCut, bool tryLadder)
00624 {
00625 byLadder = false;
00626 isKoCut = false;
00627 GoModBoard mbd(bd1);
00628 GoBoard& bd = mbd.Board();
00629
00630 const SgBlackWhite toPlay = bd1.ToPlay();
00631 bd.SetToPlay(SgOppBW(col));
00632 bool isProtected;
00633 if (! PlayIfLegal(bd, liberty))
00634 isProtected = bd.LastMoveInfo(GO_MOVEFLAG_SUICIDE);
00635
00636 else
00637 {
00638 if (bd.LastMoveInfo(GO_MOVEFLAG_SUICIDE))
00639 isProtected = true;
00640 else
00641 {
00642 if (bd.InAtari(liberty))
00643 {
00644 if (bd.NumStones(liberty) > 1)
00645 isProtected = true;
00646 else
00647 {
00648 SgPoint p = bd.TheLiberty(liberty);
00649 if (PlayIfLegal(bd, p))
00650 {
00651 isProtected = (bd.NumStones(p) != 1)
00652 || (bd.NumLiberties(p) != 1);
00653
00654 bd.Undo();
00655 }
00656 else
00657 isProtected = false;
00658
00659 if (! isProtected)
00660 isKoCut = true;
00661 }
00662 }
00663 else if (tryLadder)
00664 {
00665 isProtected = Ladder(bd, liberty, bd.ToPlay(), true);
00666 if (isProtected)
00667 byLadder = true;
00668 }
00669 else
00670 isProtected = false;
00671 }
00672 bd.Undo();
00673 }
00674 bd.SetToPlay(toPlay);
00675 return isProtected;
00676 }
00677
00678
00679
00680
00681
00682
00683
00684 SgPoint GoLadderUtil::TryLadder(const GoBoard& bd, SgPoint prey,
00685 SgBlackWhite firstPlayer)
00686 {
00687 SgVector<SgPoint> sequence;
00688 bool isCaptured = Ladder(bd, prey, firstPlayer, true, &sequence);
00689
00690
00691 SgPoint p;
00692 if (isCaptured != (firstPlayer == bd.GetStone(prey)))
00693 p = sequence.IsEmpty() ? SG_PASS : sequence.Front();
00694 else
00695 p = SG_NULLMOVE;
00696 return p;
00697 }
00698
00699
00700