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