00001 //---------------------------------------------------------------------------- 00002 /** @file GoLadder.cpp */ 00003 //---------------------------------------------------------------------------- 00004 00005 #include "SgSystem.h" 00006 #include "GoLadder.h" 00007 00008 #include <algorithm> 00009 #include <memory> 00010 #include "GoBoard.h" 00011 #include "GoBoardUtil.h" 00012 #include "GoModBoard.h" 00013 #include "SgVector.h" 00014 #include "SgStack.h" 00015 00016 using namespace std; 00017 using GoBoardUtil::NeighborsOfColor; 00018 using GoBoardUtil::PlayIfLegal; 00019 00020 //---------------------------------------------------------------------------- 00021 00022 namespace { 00023 00024 const int GOOD_FOR_PREY = 1000; 00025 00026 const int GOOD_FOR_HUNTER = -1000; 00027 00028 } // namespace 00029 00030 //---------------------------------------------------------------------------- 00031 00032 GoLadder::GoLadder() 00033 { 00034 } 00035 00036 inline bool GoLadder::CheckMoveOverflow() const 00037 { 00038 return (m_bd->MoveNumber() >= m_maxMoveNumber); 00039 } 00040 00041 void GoLadder::InitMaxMoveNumber() 00042 { 00043 // reserve is the maximum number of moves played before recursion 00044 // 5 is an optimistic bound 00045 const int RESERVE = 5; 00046 m_maxMoveNumber = min(m_bd->MoveNumber() + MAX_LADDER_MOVES, 00047 GO_MAX_NUM_MOVES - RESERVE); 00048 } 00049 00050 /** Marks all stones in the block p as part of the prey. 00051 If 'stones' is not 0, then append the stones to the existing list. */ 00052 void GoLadder::MarkStonesAsPrey(SgPoint p, SgVector<SgPoint>* stones) 00053 { 00054 SG_ASSERT(m_bd->IsValidPoint(p)); 00055 if (m_bd->Occupied(p)) 00056 { 00057 for (GoBoard::StoneIterator it(*m_bd, p); it; ++it) 00058 { 00059 SgPoint p = *it; 00060 m_partOfPrey.Include(p); 00061 if (stones) 00062 stones->PushBack(p); 00063 } 00064 } 00065 } 00066 00067 /** Filter out captured blocks, blocks not in atari, and blocks not adjacent 00068 to the prey. 00069 The latter are found by checking whether blocks adjacent to the block in 00070 question are the prey or not. Does not return the correct blocks if the 00071 prey has more than three liberties, but in that case, the prey has 00072 escaped anyway. */ 00073 void GoLadder::FilterAdjacent(GoPointList& adjBlocks) 00074 { 00075 GoPointList temp; 00076 for (GoPointList::Iterator it(adjBlocks); it; ++it) 00077 { 00078 SgPoint block = *it; 00079 if (m_bd->IsColor(block, m_hunterColor) 00080 && m_bd->InAtari(block) 00081 && BlockIsAdjToPrey(block, 1)) 00082 temp.PushBack(block); 00083 } 00084 ReduceToBlocks(temp); 00085 adjBlocks = temp; 00086 } 00087 00088 bool GoLadder::PointIsAdjToPrey(SgPoint p) 00089 { 00090 return m_partOfPrey[p - SG_NS] 00091 || m_partOfPrey[p - SG_WE] 00092 || m_partOfPrey[p + SG_WE] 00093 || m_partOfPrey[p + SG_NS]; 00094 } 00095 00096 bool GoLadder::BlockIsAdjToPrey(SgPoint p, int numAdj) 00097 { 00098 SG_ASSERT(m_bd->IsColor(p, m_hunterColor)); 00099 for (GoBoard::StoneIterator it(*m_bd, p); it; ++it) 00100 if (PointIsAdjToPrey(*it) && --numAdj == 0) 00101 return true; 00102 return false; 00103 } 00104 00105 /** Play hunter move and update all the relevant information. 00106 Play at one of the two liberties of the prey. */ 00107 int GoLadder::PlayHunterMove(int depth, SgPoint move, SgPoint lib1, 00108 SgPoint lib2, const GoPointList& adjBlk, 00109 SgVector<SgPoint>* sequence) 00110 { 00111 SG_ASSERT(move == lib1 || move == lib2); 00112 // TODO: only pass move and otherLib 00113 int result = 0; 00114 if (PlayIfLegal(*m_bd, move, m_hunterColor)) 00115 { 00116 // Find new adjacent blocks: only block just played can be new 00117 // in atari. 00118 // But other blocks previously in atari may have gained new liberties 00119 // because the move captured a stone, or the move may have extended a 00120 // block previously in atari. 00121 // If it was in atari before, and the move doesn't capture 00122 // anything, then the block will still be in atari afterwards - no 00123 // need to check again. 00124 GoPointList newAdj; 00125 if (m_bd->InAtari(move)) 00126 newAdj.PushBack(move); 00127 for (GoPointList::Iterator it(adjBlk); it; ++it) 00128 { 00129 SgPoint block = *it; 00130 if (! m_bd->AreInSameBlock(block, move)) 00131 { 00132 if (! m_bd->CapturingMove() || m_bd->InAtari(block)) 00133 newAdj.PushBack(block); 00134 } 00135 } 00136 if (move == lib1) 00137 lib1 = lib2; 00138 result = PreyLadder(depth + 1, lib1, newAdj, sequence); 00139 if (sequence) 00140 sequence->PushBack(move); 00141 m_bd->Undo(); 00142 } 00143 else 00144 { 00145 if (sequence) 00146 sequence->Clear(); 00147 result = GOOD_FOR_PREY - depth; 00148 } 00149 return result; 00150 } 00151 00152 /** Play prey move and update all the relevant information. 00153 Extend the prey by playing at its only liberty, or capture a block 00154 adjacent to the prey. */ 00155 int GoLadder::PlayPreyMove(int depth, SgPoint move, SgPoint lib1, 00156 const GoPointList& adjBlk, 00157 SgVector<SgPoint>* sequence) 00158 { 00159 int result = 0; 00160 GoPointList newAdj(adjBlk); 00161 SgVector<SgPoint> newLib; 00162 SgVector<SgPoint> newStones; 00163 SgVector<SgPoint> neighbors; 00164 if (move == lib1) 00165 { 00166 NeighborsOfColor(*m_bd, move, m_preyColor, &neighbors); 00167 for (SgVectorIterator<SgPoint> iter(neighbors); iter; ++iter) 00168 { 00169 SgPoint block = *iter; 00170 if (! m_partOfPrey[block]) 00171 { 00172 MarkStonesAsPrey(block, &newStones); 00173 GoPointList temp = 00174 GoBoardUtil::AdjacentStones(*m_bd, block); 00175 newAdj.PushBackList(temp); 00176 for (GoBoard::LibertyIterator it(*m_bd, block); it; ++it) 00177 newLib.Include(*it); 00178 } 00179 } 00180 m_partOfPrey.Include(move); 00181 } 00182 if (PlayIfLegal(*m_bd, move, m_preyColor)) 00183 { 00184 if (move == lib1) 00185 { 00186 NeighborsOfColor(*m_bd, move, SG_EMPTY, &neighbors); 00187 for (SgVectorIterator<SgPoint> iter(newLib); iter; ++iter) 00188 { 00189 SgPoint point = *iter; 00190 // Test for Empty is necessary because newLib will include 00191 // the move just played. 00192 if (m_bd->IsEmpty(point)) 00193 neighbors.Include(point); 00194 } 00195 } 00196 else 00197 { 00198 neighbors.PushBack(lib1); 00199 } 00200 if (m_bd->CapturingMove()) 00201 { // Add the points at the captured stones that are adjacent to the 00202 // prey to the liberties, at least if exactly one stone captured. 00203 for (GoPointList::Iterator it(m_bd->CapturedStones()); it; 00204 ++it) 00205 { 00206 SgPoint stone = *it; 00207 if (PointIsAdjToPrey(stone)) 00208 neighbors.Include(stone); 00209 } 00210 } 00211 SG_ASSERT(! neighbors.IsEmpty()); 00212 lib1 = neighbors[0]; 00213 SG_ASSERT(m_bd->IsEmpty(lib1)); 00214 SgArrayList<SgPoint,4> temp = 00215 NeighborsOfColor(*m_bd, move, m_hunterColor); 00216 newAdj.PushBackList(temp); 00217 FilterAdjacent(newAdj); 00218 00219 if (neighbors.Length() == 1) 00220 result = HunterLadder(depth + 1, lib1, newAdj, sequence); 00221 else if (neighbors.Length() == 2) 00222 { 00223 SgPoint lib2 = neighbors[1]; 00224 SG_ASSERT(m_bd->IsEmpty(lib2)); 00225 result = HunterLadder(depth + 1, lib1, lib2, newAdj, sequence); 00226 } 00227 else // 3 <= numLib 00228 { 00229 if (sequence) 00230 sequence->Clear(); 00231 result = GOOD_FOR_PREY - (depth + 1); 00232 } 00233 if (sequence) 00234 sequence->PushBack(move); 00235 m_bd->Undo(); 00236 } 00237 else 00238 { 00239 if (sequence) 00240 sequence->Clear(); 00241 result = GOOD_FOR_HUNTER + depth; 00242 } 00243 m_partOfPrey.Exclude(move); 00244 m_partOfPrey.Exclude(newStones); 00245 00246 return result; 00247 } 00248 00249 int GoLadder::PreyLadder(int depth, SgPoint lib1, 00250 const GoPointList& adjBlk, 00251 SgVector<SgPoint>* sequence) 00252 { 00253 if (CheckMoveOverflow()) 00254 return GOOD_FOR_PREY; 00255 int result = 0; 00256 for (GoPointList::Iterator iter(adjBlk); iter; ++iter) 00257 { 00258 SgPoint block = *iter; 00259 SgPoint move = *GoBoard::LibertyIterator(*m_bd, block); 00260 if (BlockIsAdjToPrey(block, 2)) 00261 { 00262 if (sequence) 00263 sequence->SetTo(move); 00264 result = GOOD_FOR_PREY - depth; 00265 } 00266 else if (move != lib1) 00267 { 00268 result = PlayPreyMove(depth, move, lib1, adjBlk, sequence); 00269 } 00270 if (0 < result) 00271 break; 00272 } 00273 if (result <= 0) 00274 { 00275 if (sequence) 00276 { 00277 SgVector<SgPoint> seq2; 00278 int result2 = PlayPreyMove(depth, lib1, lib1, adjBlk, &seq2); 00279 if (result < result2 || result == 0) 00280 { 00281 result = result2; 00282 sequence->SwapWith(&seq2); 00283 } 00284 } 00285 else 00286 { 00287 int result2 = PlayPreyMove(depth, lib1, lib1, adjBlk, 0); 00288 if (result < result2 || result == 0) 00289 result = result2; 00290 } 00291 } 00292 return result; 00293 } 00294 00295 int GoLadder::HunterLadder(int depth, SgPoint lib1, const GoPointList& adjBlk, 00296 SgVector<SgPoint>* sequence) 00297 { 00298 SG_UNUSED(adjBlk); 00299 if (CheckMoveOverflow()) 00300 return GOOD_FOR_PREY; 00301 if (sequence) 00302 sequence->SetTo(lib1); 00303 // TODO: should probably test for IsSnapback here, but don't have 00304 // the right information available. 00305 return GOOD_FOR_HUNTER + depth; 00306 } 00307 00308 int GoLadder::HunterLadder(int depth, SgPoint lib1, SgPoint lib2, 00309 const GoPointList& adjBlk, 00310 SgVector<SgPoint>* sequence) 00311 { 00312 if (CheckMoveOverflow()) 00313 return GOOD_FOR_PREY; 00314 int result = 0; 00315 if (m_bd->NumEmptyNeighbors(lib1) < m_bd->NumEmptyNeighbors(lib2)) 00316 { 00317 swap(lib1, lib2); 00318 } 00319 if (m_bd->NumEmptyNeighbors(lib1) == 3 00320 && ! SgPointUtil::AreAdjacent(lib1, lib2)) 00321 { 00322 // If not playing at lib1, then prey will play at lib1 and 00323 // get three liberties; little to update in this case. 00324 m_bd->Play(lib1, m_hunterColor); 00325 result = PreyLadder(depth + 1, lib2, adjBlk, sequence); 00326 if (sequence) 00327 sequence->PushBack(lib1); 00328 m_bd->Undo(); 00329 } 00330 else 00331 { 00332 // Two liberties, hunter to play, but not standard case. 00333 if (! adjBlk.IsEmpty() 00334 && *GoBoard::LibertyIterator(*m_bd, adjBlk[0]) == lib2) 00335 { 00336 swap(lib1, lib2); // protect hunter blocks in atari 00337 } 00338 result = PlayHunterMove(depth, lib1, lib1, lib2, 00339 adjBlk, sequence); 00340 if (0 <= result) // escaped 00341 { 00342 if (sequence) 00343 { 00344 SgVector<SgPoint> seq2; 00345 int result2 = PlayHunterMove(depth, lib2, lib1, lib2, 00346 adjBlk, &seq2); 00347 if (result2 < result) 00348 { result = result2; 00349 sequence->SwapWith(&seq2); 00350 } 00351 } 00352 else 00353 { 00354 int result2 = PlayHunterMove(depth, lib2, lib1, lib2, 00355 adjBlk, 0); 00356 if (result2 < result) 00357 result = result2; 00358 } 00359 } 00360 } 00361 return result; 00362 } 00363 00364 void GoLadder::ReduceToBlocks(GoPointList& stones) 00365 { 00366 // Single block is frequent case, don't compute block. 00367 if (stones.IsEmpty()) 00368 ; // nothing to do 00369 else if (stones.Length() <= 1) 00370 { 00371 if (m_bd->IsEmpty(stones[0])) 00372 stones.Clear(); 00373 } 00374 else 00375 { 00376 GoPointList visited; 00377 // TODO: create SgMarker member in GoLadder and use it for visited 00378 // points 00379 GoPointList result; 00380 for (GoPointList::Iterator it(stones); it; ++it) 00381 { 00382 SgPoint stone = *it; 00383 if (m_bd->Occupied(stone) && ! visited.Contains(stone)) 00384 { 00385 result.PushBack(stone); 00386 for (GoBoard::StoneIterator it(*m_bd, stone); it; ++it) 00387 visited.PushBack(*it); 00388 } 00389 } 00390 stones = result; 00391 } 00392 } 00393 00394 /** Main ladder routine */ 00395 int GoLadder::Ladder(const GoBoard& bd, SgPoint prey, SgBlackWhite toPlay, 00396 SgVector<SgPoint>* sequence, bool twoLibIsEscape) 00397 { 00398 GoModBoard modBoard(bd); 00399 m_bd = &modBoard.Board(); 00400 InitMaxMoveNumber(); 00401 if (sequence) 00402 sequence->Clear(); 00403 if (! m_bd->Occupied(prey)) 00404 return 0; 00405 if (CheckMoveOverflow()) 00406 return GOOD_FOR_PREY; 00407 int result = 0; 00408 m_preyColor = m_bd->GetStone(prey); 00409 m_hunterColor = SgOppBW(m_preyColor); 00410 int numLib = m_bd->NumLiberties(prey); 00411 if (2 < numLib) 00412 result = GOOD_FOR_PREY; 00413 else 00414 { 00415 GoBoard::LibertyIterator libit(*m_bd, prey); 00416 SgPoint lib1 = *libit; 00417 m_partOfPrey.Clear(); 00418 MarkStonesAsPrey(prey); 00419 GoPointList adjBlk = GoBoardUtil::AdjacentStones(*m_bd, prey); 00420 FilterAdjacent(adjBlk); 00421 if (toPlay == m_preyColor) 00422 { 00423 if (numLib == 1) 00424 result = PreyLadder(0, lib1, adjBlk, sequence); 00425 else if (twoLibIsEscape) // prey to play, numLib >= 2 00426 // For example, Explorer cannot treat this case as a ladder, 00427 // it messes up the logic 00428 result = GOOD_FOR_PREY; 00429 else 00430 { 00431 // Prey to play, two liberties. This is usually good, but 00432 // need to prove that there is some move that really 00433 // escapes laddercapture. 00434 // Try liberties of adjacent blocks with at most two 00435 // liberties, try lib1 and lib2, and try moves one away 00436 // from the two liberties. 00437 // Good example with three blocks that test this case: 00438 // (;GM[1]SZ[19]FF[3] 00439 // AB[qa][pa][pb][pd][pc][qe][re][rd][rc][se] 00440 // AW[pe][pf][qf][qd][qc][rb][qb][sa][sc][rf][rg][sg]) 00441 SgVector<SgPoint> movesToTry; 00442 00443 // Liberties of adj. blocks with at most two liberties. 00444 adjBlk = GoBoardUtil::AdjacentStones(*m_bd, prey); 00445 ReduceToBlocks(adjBlk); 00446 for (GoPointList::Iterator iterAdj(adjBlk); iterAdj; 00447 ++iterAdj) 00448 { 00449 SgPoint block = *iterAdj; 00450 SG_ASSERT(m_bd->IsColor(block, m_hunterColor)); 00451 SG_ASSERT(BlockIsAdjToPrey(block, 1)); 00452 if (m_bd->NumLiberties(block) <= 2) 00453 for (GoBoard::LibertyIterator it(*m_bd, block); it; 00454 ++it) 00455 movesToTry.PushBack(*it); 00456 } 00457 00458 // Liberties of blocks. 00459 ++libit; 00460 SgPoint lib2 = *libit; 00461 movesToTry.PushBack(lib1); 00462 movesToTry.PushBack(lib2); 00463 00464 // Moves one away from liberties. 00465 SgVector<SgPoint> neighbors; 00466 NeighborsOfColor(*m_bd, lib1, SG_EMPTY, &neighbors); 00467 movesToTry.Concat(&neighbors); 00468 NeighborsOfColor(*m_bd, lib2, SG_EMPTY, &neighbors); 00469 movesToTry.Concat(&neighbors); 00470 00471 // Try whether any of these moves lead to escape. 00472 for (SgVectorIterator<SgPoint> it(movesToTry); it; ++it) 00473 { 00474 if (PlayIfLegal(*m_bd, *it, m_preyColor)) 00475 { 00476 if (Ladder(bd, prey, m_hunterColor, 0, twoLibIsEscape) 00477 > 0) 00478 { 00479 if (sequence) 00480 sequence->PushBack(*it); 00481 result = GOOD_FOR_PREY; 00482 } 00483 m_bd->Undo(); 00484 } 00485 if (result != 0) 00486 break; 00487 } 00488 00489 // If none of those moves worked, prey can't escape. 00490 // This is a bit pessimistic, there may be other moves 00491 // that do lead to escape (e.g. approach moves), but 00492 // ladder algorithm doesn't know about those. 00493 if (result == 0) 00494 result = GOOD_FOR_HUNTER; 00495 } 00496 } 00497 else 00498 { 00499 if (IsSnapback(prey)) 00500 result = GOOD_FOR_PREY; 00501 else 00502 { 00503 ++libit; 00504 if (libit) // two liberties 00505 result = HunterLadder(0, lib1, *libit, adjBlk, sequence); 00506 else // one liberty 00507 result = HunterLadder(0, lib1, adjBlk, sequence); 00508 } 00509 } 00510 } 00511 if (sequence) 00512 sequence->Reverse(); // built as a stack, with first move at end. 00513 return result; 00514 } 00515 00516 /** Check whether the block at 'prey' is caught in a snapback. 00517 Snapback means that it can be captured, but it's only a single stone, and 00518 the prey can capture right back. */ 00519 bool GoLadder::IsSnapback(SgPoint prey) 00520 { 00521 bool isSnapback = false; 00522 if (m_bd->IsSingleStone(prey) && m_bd->InAtari(prey)) 00523 { 00524 SgPoint liberty = *GoBoard::LibertyIterator(*m_bd, prey); 00525 if (PlayIfLegal(*m_bd, liberty, SgOppBW(m_bd->GetStone(prey)))) 00526 { 00527 isSnapback = (m_bd->InAtari(liberty) 00528 && ! m_bd->IsSingleStone(liberty)); 00529 m_bd->Undo(); 00530 } 00531 } 00532 return isSnapback; 00533 } 00534 00535 //---------------------------------------------------------------------------- 00536 00537 bool GoLadderUtil::Ladder(const GoBoard& bd, SgPoint prey, 00538 SgBlackWhite toPlay, bool twoLibIsEscape, 00539 SgVector<SgPoint>* sequence) 00540 { 00541 SG_ASSERT(bd.IsValidPoint(prey)); 00542 SG_ASSERT(bd.Occupied(prey)); 00543 // AR: from Martin: for an unsettled block with 2 liberties, it 00544 // immediately says it can escape, but does not return a move. 00545 // Sequence is empty. So I have to special case this and look for 00546 // moves that escape from ladder myself. 00547 // ---> need to tell Martin if I find this 00548 #ifndef NDEBUG 00549 SgHashCode oldHash = bd.GetHashCode(); 00550 #endif 00551 GoLadder ladder; 00552 int result = ladder.Ladder(bd, prey, toPlay, sequence, twoLibIsEscape); 00553 #ifndef NDEBUG 00554 // Make sure Ladder didn't change the board position. 00555 SG_ASSERT(oldHash == bd.GetHashCode()); 00556 #endif 00557 SG_ASSERT(result != 0); 00558 return (result < 0); 00559 } 00560 00561 GoLadderStatus GoLadderUtil::LadderStatus(const GoBoard& bd, SgPoint prey, 00562 bool twoLibIsEscape, 00563 SgPoint* toCapture, 00564 SgPoint* toEscape) 00565 { 00566 SG_ASSERT(bd.IsValidPoint(prey)); 00567 SG_ASSERT(bd.Occupied(prey)); 00568 #ifndef NDEBUG 00569 SgHashCode oldHash = bd.GetHashCode(); 00570 #endif 00571 // Unsettled only if can capture when hunter plays first, and can escape 00572 // if prey plays first. 00573 GoLadder ladder; 00574 SgBlackWhite preyColor = bd.GetStone(prey); 00575 SgVector<SgPoint> captureSequence; 00576 GoLadderStatus status = GO_LADDER_ESCAPED; 00577 if (ladder.Ladder(bd, prey, SgOppBW(preyColor), &captureSequence, 00578 twoLibIsEscape) < 0) 00579 { 00580 SgVector<SgPoint> escapeSequence; 00581 if (ladder.Ladder(bd, prey, preyColor, &escapeSequence, 00582 twoLibIsEscape) < 0) 00583 status = GO_LADDER_CAPTURED; 00584 else 00585 { 00586 status = GO_LADDER_UNSETTLED; 00587 // Unsettled = ladder depends on who plays first, so there must 00588 // be a move that can be played. 00589 SG_ASSERT(captureSequence.NonEmpty()); 00590 // escapeSequence can be empty in 2 libs, prey to play case 00591 SG_ASSERT(twoLibIsEscape || escapeSequence.NonEmpty()); 00592 if (toCapture) 00593 *toCapture = captureSequence.Front(); 00594 if (toEscape) 00595 *toEscape = escapeSequence.IsEmpty() ? SG_PASS : 00596 escapeSequence.Front(); 00597 } 00598 } 00599 #ifndef NDEBUG 00600 // Make sure Ladder didn't change the board position. 00601 SG_ASSERT(oldHash == bd.GetHashCode()); 00602 #endif 00603 return status; 00604 } 00605 00606 bool GoLadderUtil::IsProtectedLiberty(const GoBoard& bd, SgPoint liberty, 00607 SgBlackWhite color) 00608 { 00609 bool ignoreLadder; 00610 bool ignoreKo; 00611 return IsProtectedLiberty(bd, liberty, color, ignoreLadder, ignoreKo, 00612 true); 00613 } 00614 00615 bool GoLadderUtil::IsProtectedLiberty(const GoBoard& bd1, SgPoint liberty, 00616 SgBlackWhite col, bool& byLadder, 00617 bool& isKoCut, bool tryLadder) 00618 { 00619 byLadder = false; 00620 isKoCut = false; 00621 GoModBoard mbd(bd1); 00622 GoBoard& bd = mbd.Board(); 00623 00624 const SgBlackWhite toPlay = bd1.ToPlay(); 00625 bd.SetToPlay(SgOppBW(col)); 00626 bool isProtected; 00627 if (! PlayIfLegal(bd, liberty)) 00628 isProtected = bd.LastMoveInfo(GO_MOVEFLAG_SUICIDE); 00629 // opponent cannot play there 00630 else 00631 { 00632 if (bd.LastMoveInfo(GO_MOVEFLAG_SUICIDE)) 00633 isProtected = true; 00634 else 00635 { 00636 if (bd.InAtari(liberty)) 00637 { 00638 if (bd.NumStones(liberty) > 1) 00639 isProtected = true; 00640 else 00641 { 00642 SgPoint p = bd.TheLiberty(liberty); 00643 if (PlayIfLegal(bd, p)) 00644 { 00645 isProtected = (bd.NumStones(p) != 1) 00646 || (bd.NumLiberties(p) != 1); 00647 // yes, can re-capture there 00648 bd.Undo(); 00649 } 00650 else 00651 isProtected = false; 00652 00653 if (! isProtected) 00654 isKoCut = true; 00655 } 00656 } 00657 else if (tryLadder) 00658 { 00659 isProtected = Ladder(bd, liberty, bd.ToPlay(), true); 00660 if (isProtected) 00661 byLadder = true; 00662 } 00663 else // don't try ladder 00664 isProtected = false; 00665 } 00666 bd.Undo(); 00667 } 00668 bd.SetToPlay(toPlay); 00669 return isProtected; 00670 } 00671 00672 /** try to escape/capture prey block 00673 Possible return values: 00674 - SG_PASS if already escaped/captured 00675 - the point to play 00676 - SG_NULLMOVE in case of failure */ 00677 SgPoint GoLadderUtil::TryLadder(const GoBoard& bd, SgPoint prey, 00678 SgBlackWhite firstPlayer) 00679 { 00680 SgVector<SgPoint> sequence; 00681 bool isCaptured = Ladder(bd, prey, firstPlayer, true, &sequence); 00682 // if move is same color as prey, we want to escape 00683 // else we want to capture. 00684 SgPoint p; 00685 if (isCaptured != (firstPlayer == bd.GetStone(prey))) 00686 p = sequence.IsEmpty() ? SG_PASS : sequence.Front(); 00687 else 00688 p = SG_NULLMOVE; 00689 return p; 00690 } 00691 00692 //---------------------------------------------------------------------------- 00693