00001
00002
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 }
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
00044
00045 const int RESERVE = 5;
00046 m_maxMoveNumber = min(m_bd->MoveNumber() + MAX_LADDER_MOVES,
00047 GO_MAX_NUM_MOVES - RESERVE);
00048 }
00049
00050
00051
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
00068
00069
00070
00071
00072
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
00106
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
00113 int result = 0;
00114 if (PlayIfLegal(*m_bd, move, m_hunterColor))
00115 {
00116
00117
00118
00119
00120
00121
00122
00123
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
00153
00154
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
00191
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 {
00202
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
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
00304
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
00323
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
00333 if (! adjBlk.IsEmpty()
00334 && *GoBoard::LibertyIterator(*m_bd, adjBlk[0]) == lib2)
00335 {
00336 swap(lib1, lib2);
00337 }
00338 result = PlayHunterMove(depth, lib1, lib1, lib2,
00339 adjBlk, sequence);
00340 if (0 <= result)
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
00367 if (stones.IsEmpty())
00368 ;
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
00378
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
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)
00426
00427
00428 result = GOOD_FOR_PREY;
00429 else
00430 {
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441 SgVector<SgPoint> movesToTry;
00442
00443
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
00459 ++libit;
00460 SgPoint lib2 = *libit;
00461 movesToTry.PushBack(lib1);
00462 movesToTry.PushBack(lib2);
00463
00464
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
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
00490
00491
00492
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)
00505 result = HunterLadder(0, lib1, *libit, adjBlk, sequence);
00506 else
00507 result = HunterLadder(0, lib1, adjBlk, sequence);
00508 }
00509 }
00510 }
00511 if (sequence)
00512 sequence->Reverse();
00513 return result;
00514 }
00515
00516
00517
00518
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
00544
00545
00546
00547
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
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
00572
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
00588
00589 SG_ASSERT(captureSequence.NonEmpty());
00590
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
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
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
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
00664 isProtected = false;
00665 }
00666 bd.Undo();
00667 }
00668 bd.SetToPlay(toPlay);
00669 return isProtected;
00670 }
00671
00672
00673
00674
00675
00676
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
00683
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