Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoBoardUtil.h

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoBoardUtil.h
00003     GoBoard-related utility classes. */
00004 //----------------------------------------------------------------------------
00005 
00006 #ifndef GO_BOARDUTIL_H
00007 #define GO_BOARDUTIL_H
00008 
00009 #include "GoBoard.h"
00010 #include "SgBoardColor.h"
00011 #include "SgDebug.h"
00012 #include "SgPoint.h"
00013 #include "SgPointArray.h"
00014 #include "SgStack.h"
00015 #include "SgVector.h"
00016 
00017 //----------------------------------------------------------------------------
00018 
00019 /** Utility functions for users of class GoBoard.
00020     Some of the functions that the board class as a template argument,
00021     such that they can be used with specialized variants of GoBoard that
00022     share only a sub-functionality. */
00023 namespace GoBoardUtil
00024 {
00025     /** Add anchors of neighbor blocks to list. */
00026     void AddNeighborBlocksOfColor(const GoBoard& bd,
00027                                   SgPoint p,
00028                                   SgBlackWhite color,
00029                                   SgVector<SgPoint>& neighbors);
00030 
00031     /** Add wall of stones in color to the board.
00032         @param bd
00033         @param color color of the wall.
00034         @param start Starting point for the wall.
00035         @param length number of stones in wall
00036         @param direction offset from one stone to next.
00037             e.g. direction = NS builds a North-South wall.
00038             can also build diagonal walls,
00039             e.g. by using direction = NS + WE,
00040             or even jumps.
00041         Precondition: all these squares must be empty,
00042                       and playing on them must be legal. */
00043     void AddWall(GoBoard& bd,
00044                  SgBlackWhite color,
00045                  SgPoint start,
00046                  int length,
00047                  int direction);
00048 
00049     /** Get list of stones adjacent to a block. */
00050     GoPointList AdjacentStones(const GoBoard& bd, SgPoint point);
00051 
00052     /** SgVector version of GoBoard::AdjacentBlocks */
00053     void AdjacentBlocks(const GoBoard& bd, SgPoint p, int maxLib,
00054                         SgVector<SgPoint>* blocks);
00055 
00056     /** Estimate second order liberties of point p for given block
00057         This is fast and approximate, may double count libs */
00058     int Approx2Libs(const GoBoard& board, SgPoint block, SgPoint p,
00059                     SgBlackWhite color);
00060 
00061     /** Return whether 'block1' and 'block2' have at least two shared
00062         liberties.
00063         Not defined for empty or border points. */
00064     bool AtLeastTwoSharedLibs(const GoBoard& bd, SgPoint block1,
00065                               SgPoint block2);
00066 
00067     bool BlockIsAdjacentTo(const GoBoard& bd, SgPoint block,
00068                            const SgPointSet& walls);
00069 
00070     void BlocksAdjacentToPoints(const GoBoard& bd,
00071                                 const SgVector<SgPoint>& points,
00072                                 SgBlackWhite c,
00073                                 SgVector<SgPoint>* anchors);
00074 
00075     /** List the anchors of all blocks of color 'c' adjacent to the region
00076         consisting of 'points'. */
00077     void BlocksAdjacentToPoints(const GoBoard& bd, const SgPointSet& points,
00078                                 SgBlackWhite c, SgVector<SgPoint>* anchors);
00079 
00080     /** Compute the common fate graph distance from all points to a given
00081         point.
00082         The common fate graph distance ist the shortest path between points
00083         with an edge cost of 0 for edges between stones of the same block,
00084         and an edge cost of 1 otherwise.
00085         @param bd
00086         @param p
00087         @param maxDist The maximum distance to search (points with a
00088         distance &gt; maxDist will get the value numeric_limits<int>::max())
00089         @return The array containing the distances; for blocks only the
00090         element at the block anchor is defined. */
00091     SgPointArray<int> CfgDistance(const GoBoard& bd, SgPoint p,
00092                                int maxDist = std::numeric_limits<int>::max());
00093 
00094     /** Is p contained in anchor[] ?
00095         anchor[] must be terminated by END_POINT. */
00096     bool ContainsAnchor(const SgPoint anchor[], const SgPoint p);
00097 
00098    /** Get diagonal points with a color.
00099        @param bd The board.
00100        @param p The point.
00101        @param c The color.
00102        @param diagonals Resulting point list. Will be cleared before
00103        adding the points. */
00104     void DiagonalsOfColor(const GoBoard& bd, SgPoint p, int c,
00105                           SgVector<SgPoint>* diagonals);
00106 
00107     /** Write board including move history to stream.
00108         This function is intended for printing the current board state
00109         for debugging or after a crash. The move history is written in SGF
00110         format. */
00111     void DumpBoard(const GoBoard& bd, std::ostream& out = SgDebug());
00112 
00113     /** Return whether the game is finished.
00114         (two or three consecutive pass moves).
00115         For the choice of two or three: @see GoRules constructor. */
00116     bool EndOfGame(const GoBoard& bd);
00117 
00118     /** Add other stones of blocks to SgPointSet if one is in set */
00119     void ExpandToBlocks(const GoBoard& board, SgPointSet& pointSet);
00120 
00121     /** Find a neighboring point in color c.
00122         Precondition: Call only if such a point exists. */
00123     template<class BOARD>
00124     SgPoint FindNeighbor(const BOARD& bd, SgPoint p, SgEmptyBlackWhite c);
00125 
00126     /** Include move in list if it is legal */
00127     bool GenerateIfLegal(const GoBoard& bd,
00128                          SgPoint move,
00129                          SgVector<SgPoint>* moves);
00130 
00131     /** Convert the given move to human-readable coordinates.
00132         (lower left A1 to upper right T19, leaving out column I). */
00133     void GetCoordString(SgMove move, std::string* s, int boardSize);
00134 
00135     /** Convert the given move to human-readable coordinates.
00136         (lower left A1 to upper right T19, leaving out column I). */
00137     void GetCoordString(const GoBoard& board, SgMove move, std::string* s);
00138 
00139 
00140     /** Which intersections were modified with the last move.
00141         Can check either before or after move is played (set premove) */
00142     SgRect GetDirtyRegion(const GoBoard& bd, SgMove move, SgBlackWhite color,
00143                           bool checklibs = false, bool premove = false);
00144 
00145     /** Return whether block has at least one adjacent opponent
00146         block with at most maxLib liberties. */
00147     bool HasAdjacentBlocks(const GoBoard& bd, SgPoint p, int maxLib);
00148 
00149     bool HasStonesOfBothColors(const GoBoard& bd,
00150                                const SgVector<SgPoint>& stones);
00151 
00152     template<class BOARD>
00153     bool IsBoardEmpty(const BOARD& bd);
00154 
00155     /** Return if point is surrounded by one color and no adjacent block is
00156         in atari.
00157         Good criterion for move generation in Monte-Carlo. See:
00158         Remi Coulom: Efficient selectivity and backup operators in
00159         Monte-Carlo tree search, CG2006, Appendix A.1,
00160         http://remi.coulom.free.fr/CG2006/ */
00161     template<class BOARD>
00162     bool IsCompletelySurrounded(const BOARD& bd, SgPoint p);
00163 
00164     bool IsHandicapPoint(SgGrid size, SgGrid col, SgGrid row);
00165 
00166     template<class BOARD>
00167     bool IsNeighborOfSome(const BOARD& bd, SgPoint p, SgPoint anchors[],
00168                           SgBlackWhite toPlay);
00169 
00170     /** Does block have two shared liberties with some other block? 
00171         WARNING: for efficiency this checks only the first two liberties
00172         of the block. So it is accurate for two-liberty blocks,
00173         and a heuristic for blocks with more liberties. */
00174     template<class BOARD>
00175     bool IsSimpleChain(const BOARD& bd, SgPoint block, SgPoint& other);
00176     
00177     /** Is lib a simple eye of block?
00178         Eyes is a list of other eye points, that do not need to be
00179         occupied for lib to be an eye.
00180         Precondition (not tested): lib is surrounded by stones of color. */
00181     bool IsSimpleEyeOfBlock(const GoBoard& bd, SgPoint lib,
00182                             SgPoint blockAnchor,
00183                             const SgVector<SgPoint>& eyes);
00184 
00185     /** Check if the move just played on p was a snapback.
00186         A snapback is a single stone in atari which can be captured by a
00187         legal move, if the move creates a block with more than one stone
00188         in atari. */
00189     bool IsSnapback(const GoBoard& bd, SgPoint p);
00190 
00191     /** all points on lines [from..to] */
00192     SgPointSet Lines(const GoBoard& bd, SgGrid from, SgGrid to);
00193 
00194     bool ManySecondaryLibs(const GoBoard& bd, SgPoint block);
00195 
00196     /** Either move is not legal, or the block at move is in atari
00197         after the move. */
00198     bool MoveNotLegalOrAtari(const GoBoard& bd, SgPoint move);
00199 
00200     /** Move is legal and the block at move is not in atari
00201         after the move. */
00202     bool MoveLegalAndNotAtari(const GoBoard& bd, SgPoint move);
00203 
00204     /** Get adjacent points with a color.
00205         @param bd The board.
00206         @param p The point.
00207         @param c The color.
00208         @return Resulting point list. */
00209     SgArrayList<SgPoint,4> NeighborsOfColor(const GoBoard& bd, SgPoint p,
00210                                             int c);
00211 
00212     /** Get adjacent points with a color (SgVector version).
00213         @param bd The board.
00214         @param p The point.
00215         @param c The color.
00216         @param neighbors Resulting point list. Will be cleared before
00217         adding the points. */
00218     void NeighborsOfColor(const GoBoard& bd, SgPoint p, int c,
00219                           SgVector<SgPoint>* neighbors);
00220 
00221     /** Check if Tromp-Taylor rules and pass wins. */
00222     bool TrompTaylorPassWins(const GoBoard& bd, SgBlackWhite toPlay);
00223 
00224     /** Play a move if legal
00225         @param bd The board.
00226         @param p Move to play; SG_PASS or on-board point.
00227         @param player Color to play.
00228         @return true if the move was executed. */
00229     bool PlayIfLegal(GoBoard& bd, SgPoint p, SgBlackWhite player);
00230 
00231     /** Play a move for the current player if legal.
00232         @param bd The board.
00233         @param p Move to play; SG_PASS or on-board point.
00234         @return true if the move was executed. */
00235     bool PlayIfLegal(GoBoard& bd, SgPoint p);
00236 
00237     /** Keep only the anchor of each block in the list.
00238         Points not occupied are removed from the list. The initial list may
00239         contain duplicate stones; these will be thrown out. The returned list
00240         will be sorted by anchors. */
00241     void ReduceToAnchors(const GoBoard& bd, SgVector<SgPoint>* stones);
00242 
00243     /** Keep only the anchor of each block in the list.
00244         Points not occupied are removed from the list. The initial list may
00245         contain duplicate stones; these will be thrown out. The returned list
00246         will not be sorted by anchors. */
00247     void ReduceToAnchors(const GoBoard& bd, const SgVector<SgPoint>& stones,
00248                          SgArrayList<SgPoint,SG_MAXPOINT> &anchors);
00249 
00250     /** Compute the hash code for region of this board position. */
00251     void RegionCode(const GoBoard& bd, const SgVector<SgPoint>& region,
00252                     SgHashCode* c);
00253 
00254     /** Returns true iff during the first N moves of a Chinese handicap
00255         game. */
00256     bool RemainingChineseHandicap(const GoBoard& bd);
00257 
00258     /** Check if move would be self-atari.
00259         Faster than Executing the move, then calling InAtari(). */
00260     template<class BOARD>
00261     bool SelfAtari(const BOARD& bd, SgPoint p);
00262 
00263     /** Same as above, but also compute number of stones put into selfatari.
00264          numStones is set only if the return value is 'true'. */
00265     template<class BOARD>
00266     bool SelfAtari(const BOARD& bd, SgPoint p, int& numStones);
00267 
00268     /** Check if move would be self-atari for given color.
00269         That color may be different from bd.ToPlay(). */
00270     template<class BOARD>
00271     bool SelfAtariForColor(const BOARD& bd, SgPoint p,
00272                            SgBlackWhite toPlay);
00273 
00274     /** Return all points that are liberties of both 'block1' and 'block2'.
00275         Not defined for empty or border points. */
00276     void SharedLiberties(const GoBoard& bd, SgPoint block1, SgPoint block2,
00277                          SgVector<SgPoint>* sharedLibs);
00278 
00279     void SharedLibertyBlocks(const GoBoard& bd, SgPoint anchor, int maxLib,
00280                              SgVector<SgPoint>* blocks);
00281 
00282     /** Count score given the set of dead stones.
00283         Checks all regions that are surrounded by stones that are not dead,
00284         and counts the score according to the board rules
00285         (Chinese/Japanese) and komi. Black points are counted positive.
00286         Cannot handle neutral eye points that can occur in seki with Japanese
00287         rules.
00288         @param bd
00289         @param deadStones
00290         @param[out] score
00291         @return @c false if position cannot be scored, because the dead
00292         stones information is not consistent (a region with dead stones of
00293         both colors exists or dead stones of a color in a region of that
00294         color). */
00295     bool ScorePosition(const GoBoard& bd, const SgPointSet& deadStones,
00296                        float& score);
00297 
00298     /** Helper function used in ScoreSimpleEndPosition */
00299     template<class BOARD>
00300     SgEmptyBlackWhite ScorePoint(const BOARD& bd, SgPoint p, bool noCheck);
00301 
00302     /** Score position with given safe stones and only simple eyes.
00303         This is a fast scoring function (e.g. suitable for Monte-Carlo),
00304         that can be used if playing continues as long as there are legal moves
00305         which do not fill the player's single point eyes.
00306         Precomputed safety status of points is used, all other empty points
00307         must be single empty points surrounded by one color.
00308         The score is counted using 1 point for all black stones or empty
00309         points with only black stones adjacent, and -1 point for white
00310         stones or empty points with only white stones adjacent.
00311         Komi of board is taken into account.
00312         @param bd
00313         @param komi Komi (bd.Rules().Komi() is not used to avoid multiple
00314         conversions of komi to float)
00315         @param safe
00316         @param noCheck
00317         @param scoreBoard Optional board to fill in the status of each
00318         point (SG_EMPTY means dame); null if not needed */
00319     template<class BOARD>
00320     float ScoreSimpleEndPosition(const BOARD& bd, float komi,
00321                                  const SgBWSet& safe, bool noCheck,
00322                                  SgPointArray<SgEmptyBlackWhite>* scoreBoard);
00323 
00324     /** Score position with all stones safe and only simple eyes.
00325         This is a fast scoring function (e.g. suitable for Monte-Carlo),
00326         that can be used if playing continues as long as there are legal moves
00327         which do not fill the player's single point eyes.
00328         All stones are considered safe, all empty points must be single
00329         empty points surrounded by one color.
00330         The score is counted using 1 point for all black stones or empty
00331         points with only black stones adjacent, and -1 point for white
00332         stones or empty points with only white stones adjacent.
00333         Komi of board is taken into account.
00334         @param bd The board with the position
00335         @param komi Komi (bd.Rules().Komi() is not used to avoid multiple
00336         conversions of komi to float)
00337         @param noCheck Don't throw an exception if not all empty points are
00338         single empty points (there are cases, where this score function is
00339         useful even if it is sometimes wrong)
00340         @throws SgException If there are empty points, which are not single
00341         empty points or with stones of both colors adjacent.
00342         @return Score including komi, positive for black. */
00343     float ScoreSimpleEndPosition(const GoBoard& bd, float komi,
00344                                  bool noCheck = false);
00345 
00346     /** Fill stones in an array.
00347         Kishi: I added this code to store stones to an array,
00348         because the list version first copies stones to an array,
00349         then copies an array to a list. For me, it's not necessary because
00350         I use arrays.
00351         @note Consider using GoBoard::StoneIterator instead, if you don't need
00352         to keep the array */
00353     int Stones(const GoBoard& bd, SgPoint p, SgPoint stones[]);
00354 
00355     void TestForChain(GoBoard& bd, SgPoint block, SgPoint block2, SgPoint lib,
00356                       SgVector<SgPoint>* extended);
00357 
00358     /** Compute the Tromp-Taylor-score for the current positions.
00359         The Tromp-Taylor score is a chinese scoring method that assumes that
00360         all stones on the board are alive.
00361         @param bd The board with the position to score.
00362         @param komi The komi
00363         @param scoreBoard Optional board to fill in the status of each
00364         point (SG_EMPTY means dame)
00365         @return The score, black counting positive, komi included. */
00366     template<class BOARD>
00367     float TrompTaylorScore(const BOARD& bd, float komi,
00368                            SgPointArray<SgEmptyBlackWhite>* scoreBoard = 0);
00369 
00370     /** Check if the last two moves were two passes in a row, the first pass
00371         by the current color to play, the second by the opponent. */
00372     bool TwoPasses(const GoBoard& bd);
00373 
00374     /** Undo all moves or setup stones. */
00375     void UndoAll(GoBoard& bd);
00376 
00377 } // namespace GoBoardUtil
00378 
00379 inline bool GoBoardUtil::ContainsAnchor(const SgPoint anchor[],
00380                                         const SgPoint p)
00381 {
00382     for (int i=0; anchor[i] != SG_ENDPOINT; ++i)
00383         if (p == anchor[i])
00384             return true;
00385     return false;
00386 }
00387 
00388 template<class BOARD>
00389 inline SgPoint GoBoardUtil::FindNeighbor(const BOARD& bd, SgPoint p,
00390                                          SgEmptyBlackWhite c)
00391 {
00392     if (bd.IsColor(p + SG_NS, c))
00393         return p + SG_NS;
00394     if (bd.IsColor(p - SG_NS, c))
00395         return p - SG_NS;
00396     if (bd.IsColor(p + SG_WE, c))
00397         return p + SG_WE;
00398     SG_ASSERT(bd.IsColor(p - SG_WE, c));
00399     return p - SG_WE;
00400 }
00401 
00402 inline void GoBoardUtil::GetCoordString(const GoBoard& board, SgMove move,
00403                                         std::string* s)
00404 {
00405     GetCoordString(move, s, board.Size());
00406 }
00407 
00408 template<class BOARD>
00409 bool GoBoardUtil::IsBoardEmpty(const BOARD& bd)
00410 {
00411     return bd.TotalNumStones(SG_BLACK) + bd.TotalNumStones(SG_WHITE) == 0;
00412 }
00413 
00414 template<class BOARD>
00415 inline bool GoBoardUtil::IsCompletelySurrounded(const BOARD& bd, SgPoint p)
00416 {
00417     SG_ASSERT(bd.IsEmpty(p));
00418     if (bd.HasEmptyNeighbors(p))
00419         return false;
00420     if (bd.HasNeighbors(p, SG_BLACK) && bd.HasNeighbors(p, SG_WHITE))
00421         return false;
00422     if (! bd.IsBorder(p - SG_NS) && bd.NumLiberties(p - SG_NS) == 1)
00423         return false;
00424     if (! bd.IsBorder(p - SG_WE) && bd.NumLiberties(p - SG_WE) == 1)
00425         return false;
00426     if (! bd.IsBorder(p + SG_WE) && bd.NumLiberties(p + SG_WE) == 1)
00427         return false;
00428     if (! bd.IsBorder(p + SG_NS) && bd.NumLiberties(p + SG_NS) == 1)
00429         return false;
00430     return true;
00431 }
00432 
00433 template<class BOARD>
00434 inline bool GoBoardUtil::IsNeighborOfSome(const BOARD& bd, SgPoint p,
00435                                           SgPoint anchors[],
00436                                           SgBlackWhite toPlay)
00437 {
00438     for (SgNb4Iterator it(p); it; ++it)
00439     {
00440         const SgPoint nb = *it;
00441         if (bd.IsColor(nb, toPlay))
00442         {
00443             SgPoint anchor = bd.Anchor(nb);
00444             for (int i = 0; anchors[i] != SG_ENDPOINT; ++i)
00445                 if (anchor == anchors[i])
00446                     return true;
00447         }
00448     }
00449     return false;
00450 }
00451 
00452 template<class BOARD>
00453 bool GoBoardUtil::IsSimpleChain(const BOARD& bd,
00454                                 SgPoint block,
00455                                 SgPoint& other)
00456 {
00457     if (bd.NumLiberties(block) < 2)
00458         return false;
00459     block = bd.Anchor(block);
00460     const SgBlackWhite color = bd.GetStone(block);
00461     typename BOARD::LibertyIterator it(bd, block);
00462     const SgPoint lib1 = *it; 
00463     ++it;
00464     const SgPoint lib2 = *it; 
00465     SgPoint anchors1[4 + 1];
00466     SgPoint anchors2[4 + 1];
00467     bd.NeighborBlocks(lib1, color, anchors1);
00468     bd.NeighborBlocks(lib2, color, anchors2);
00469     for (int i=0; anchors1[i] != SG_ENDPOINT; ++i)
00470     {
00471         const SgPoint anchor = anchors1[i];
00472         if (  anchor != block
00473            && GoBoardUtil::ContainsAnchor(anchors2, anchor)
00474            )
00475         {
00476             other = anchor;
00477             return true;
00478         }
00479     }
00480     return false;
00481 }
00482 
00483 inline bool GoBoardUtil::PlayIfLegal(GoBoard& bd, SgPoint p)
00484 {
00485     return PlayIfLegal(bd, p, bd.ToPlay());
00486 }
00487 
00488 template<class BOARD>
00489 SgEmptyBlackWhite GoBoardUtil::ScorePoint(const BOARD& bd, SgPoint p,
00490                                           bool noCheck)
00491 {
00492     SG_DEBUG_ONLY(noCheck);
00493     SgEmptyBlackWhite c = bd.GetColor(p);
00494     if (c != SG_EMPTY)
00495         return c;
00496     // Position must have only completely surrounded empty points
00497     SG_ASSERT(noCheck || bd.NumEmptyNeighbors(p) == 0
00498               || GoBoardUtil::SelfAtari(bd, p));
00499     if (bd.NumNeighbors(p, SG_BLACK) > 0
00500         && bd.NumNeighbors(p, SG_WHITE) == 0)
00501         return SG_BLACK;
00502     else if (bd.NumNeighbors(p, SG_WHITE) > 0
00503              && bd.NumNeighbors(p, SG_BLACK) == 0)
00504     {
00505         SG_ASSERT(bd.NumNeighbors(p, SG_WHITE) > 0);
00506         return SG_WHITE;
00507     }
00508     else
00509     {
00510         // Position must have no dame points
00511         SG_ASSERT(noCheck || GoBoardUtil::SelfAtari(bd, p));
00512         return SG_EMPTY;
00513     }
00514 }
00515 
00516 template<class BOARD>
00517 float GoBoardUtil::ScoreSimpleEndPosition(const BOARD& bd, float komi,
00518                                   const SgBWSet& safe, bool noCheck,
00519                                   SgPointArray<SgEmptyBlackWhite>* scoreBoard)
00520 {
00521     float score = -komi;
00522     for (typename BOARD::Iterator it(bd); it; ++it)
00523     {
00524         SgPoint p = *it;
00525         SgEmptyBlackWhite c;
00526         if (safe[SG_BLACK].Contains(p))
00527             c = SG_BLACK;
00528         else if (safe[SG_WHITE].Contains(p))
00529             c = SG_WHITE;
00530         else
00531             c = ScorePoint(bd, p, noCheck);
00532         switch (c)
00533         {
00534         case SG_BLACK:
00535             ++score;
00536             break;
00537         case SG_WHITE:
00538             --score;
00539             break;
00540         default:
00541             break;
00542         }
00543         if (scoreBoard != 0)
00544             (*scoreBoard)[p] = c;
00545     }
00546     return score;
00547 }
00548 
00549 template<class BOARD>
00550 inline bool GoBoardUtil::SelfAtari(const BOARD& bd, SgPoint p)
00551 {
00552     return SelfAtariForColor(bd, p, bd.ToPlay());
00553 }
00554 
00555 template<class BOARD>
00556 inline bool GoBoardUtil::SelfAtariForColor(const BOARD& bd, SgPoint p,
00557                                           SgBlackWhite toPlay)
00558 {
00559     // This function is inline even if it is long, because it returns early
00560     // in many cases, which makes the function call an overhead.
00561 
00562     // This function has a lot of redundacy with
00563     // SelfAtari(const GoBoard&,SgPoint,int&). The two versions exist
00564     // for efficiency (this function is called very often in UCT simulations)
00565 
00566     SG_ASSERT(bd.IsEmpty(p));
00567     // No self-atari, enough liberties
00568     if (bd.NumEmptyNeighbors(p) >= 2)
00569         return false;
00570     const SgBlackWhite opp = SgOppBW(toPlay);
00571     SgPoint lib = SG_NULLPOINT;
00572     bool hasOwnNb = false;
00573     bool hasCapture = false;
00574     for (SgNb4Iterator it(p); it; ++it)
00575     {
00576         const SgPoint nb = *it;
00577         const SgBlackWhite nbColor = bd.GetColor(nb);
00578         if (nbColor == SG_EMPTY)
00579         {
00580             if (lib == SG_NULLPOINT)
00581                 lib = nb;
00582             else if (lib != nb)
00583                 return false;
00584         }
00585         else if (nbColor == toPlay) // own stones
00586         {
00587             if (bd.NumLiberties(nb) > 2)
00588                 return false;
00589             else // check block's liberties other than p
00590                 for (typename BOARD::LibertyIterator it(bd, nb); it; ++it)
00591                 {
00592                     if (*it != p)
00593                     {
00594                         if (lib == SG_NULLPOINT)
00595                             lib = *it;
00596                         else if (lib != *it)
00597                             return false;
00598                     }
00599                 }
00600             hasOwnNb = true;
00601         }
00602         else if (nbColor == opp) // opponent stones - count as lib if in atari
00603         {
00604             if (bd.InAtari(nb))
00605             {
00606                 if (lib == SG_NULLPOINT)
00607                 {
00608                     lib = *it;
00609                     hasCapture = true;
00610                 }
00611                 else if (lib != *it)
00612                     return false;
00613             }
00614         }
00615     }
00616 
00617     if (lib == SG_NULLPOINT) // suicide
00618         return false;
00619     if (! hasOwnNb && hasCapture) // ko-type capture, OK
00620          return false;
00621     if (hasOwnNb && hasCapture) // check if we gained other liberties
00622     {
00623         // lib == one of the captured stones.
00624        SgPoint anchors[4 + 1];
00625        bd.NeighborBlocks(p, toPlay, 1, anchors);
00626        SG_ASSERT(bd.IsColor(lib, opp));
00627        for (typename BOARD::StoneIterator it(bd, lib); it; ++it)
00628        {
00629            if (*it != lib && IsNeighborOfSome(bd, *it, anchors, toPlay))
00630                return false;
00631        }
00632     }
00633     return true;
00634 }
00635 
00636 template<class BOARD>
00637 bool GoBoardUtil::SelfAtari(const BOARD& bd, SgPoint p, int& numStones)
00638 {
00639     SG_ASSERT(bd.IsEmpty(p));
00640     // No self-atari, enough liberties
00641     if (bd.NumEmptyNeighbors(p) >= 2)
00642         return false;
00643     const SgBlackWhite toPlay = bd.ToPlay();
00644     const SgBlackWhite opp = SgOppBW(toPlay);
00645     SgPoint lib = SG_NULLPOINT;
00646     bool hasOwnNb = false;
00647     bool hasCapture = false;
00648     for (SgNb4Iterator it(p); it; ++it)
00649     {
00650         const SgPoint nb = *it;
00651         const SgBlackWhite nbColor = bd.GetColor(nb);
00652         if (nbColor == SG_EMPTY)
00653         {
00654             if (lib == SG_NULLPOINT)
00655                 lib = nb;
00656             else if (lib != nb)
00657                 return false;
00658         }
00659         else if (nbColor == toPlay) // own stones
00660         {
00661             if (bd.NumLiberties(nb) > 2)
00662                 return false;
00663             else // check block's liberties other than p
00664                 for (typename BOARD::LibertyIterator it(bd, nb); it; ++it)
00665                 {
00666                     if (*it != p)
00667                     {
00668                         if (lib == SG_NULLPOINT)
00669                             lib = *it;
00670                         else if (lib != *it)
00671                             return false;
00672                     }
00673                 }
00674             hasOwnNb = true;
00675         }
00676         else if (nbColor == opp) // opponent stones - count as lib if in atari
00677         {
00678             if (bd.InAtari(nb))
00679             {
00680                 if (lib == SG_NULLPOINT)
00681                 {
00682                     lib = *it;
00683                     hasCapture = true;
00684                 }
00685                 else if (lib != *it)
00686                     return false;
00687             }
00688         }
00689     }
00690 
00691     if (lib == SG_NULLPOINT) // suicide
00692         return false;
00693     if (! hasOwnNb && hasCapture) // ko-type capture, OK
00694          return false;
00695     if (hasOwnNb && hasCapture) // check if we gained other liberties
00696     {
00697         // lib == one of the captured stones.
00698        SgPoint anchors[4 + 1];
00699        bd.NeighborBlocks(p, toPlay, 1, anchors);
00700        SG_ASSERT(bd.IsColor(lib, opp));
00701        for (typename BOARD::StoneIterator it(bd, lib); it; ++it)
00702        {
00703            if (*it != lib && IsNeighborOfSome(bd, *it, anchors, toPlay))
00704                return false;
00705        }
00706     }
00707     numStones = 1;
00708     if (hasOwnNb)
00709     {
00710         SgPoint anchors[4 + 1];
00711         bd.NeighborBlocks(p, toPlay, 2, anchors);
00712         for (int i = 0; anchors[i] != SG_ENDPOINT; ++i)
00713             numStones += bd.NumStones(anchors[i]);
00714     }
00715     return true;
00716 }
00717 
00718 template<class BOARD>
00719 float GoBoardUtil::TrompTaylorScore(const BOARD& bd, float komi,
00720                                   SgPointArray<SgEmptyBlackWhite>* scoreBoard)
00721 {
00722     float score = -komi;
00723     // Mark empty points visited in one of the (non-overlapping) flood-fills
00724     SgMarker mark;
00725     for (typename BOARD::Iterator it(bd); it; ++it)
00726     {
00727         if (mark.Contains(*it))
00728             continue;
00729         SgEmptyBlackWhite c = bd.GetColor(*it);
00730         if (c == SG_BLACK)
00731         {
00732             ++score;
00733             if (scoreBoard != 0)
00734                 (*scoreBoard)[*it] = SG_BLACK;
00735             continue;
00736         }
00737         if (c == SG_WHITE)
00738         {
00739             --score;
00740             if (scoreBoard != 0)
00741                 (*scoreBoard)[*it] = SG_WHITE;
00742             continue;
00743         }
00744         SgStack<SgPoint,SG_MAXPOINT> stack;
00745         GoPointList list;
00746         SG_ASSERT(c == SG_EMPTY);
00747         stack.Push(*it);
00748         mark.Include(*it);
00749         list.PushBack(*it);
00750         SgBWArray<bool> adjacent(false);
00751         int size = 0;
00752         while (! stack.IsEmpty())
00753         {
00754             SgPoint p = stack.Pop();
00755             SG_ASSERT(bd.GetColor(p) == SG_EMPTY);
00756             ++size;
00757             if (bd.HasNeighbors(p, SG_BLACK))
00758                 adjacent[SG_BLACK] = true;
00759             if (bd.HasNeighbors(p, SG_WHITE))
00760                 adjacent[SG_WHITE] = true;
00761             for (SgNb4Iterator it2(p); it2; ++it2)
00762                 if (! bd.IsBorder(*it2) && bd.GetColor(*it2) == SG_EMPTY
00763                     && ! mark.Contains(*it2))
00764                 {
00765                     stack.Push(*it2);
00766                     mark.Include(*it2);
00767                     list.PushBack(*it2);
00768                 }
00769         }
00770         if (adjacent[SG_BLACK] && ! adjacent[SG_WHITE])
00771         {
00772             score += float(size);
00773             c = SG_BLACK;
00774         }
00775         else if (! adjacent[SG_BLACK] && adjacent[SG_WHITE])
00776         {
00777             score -= float(size);
00778             c = SG_WHITE;
00779         }
00780         else
00781             c = SG_EMPTY;
00782         if (scoreBoard != 0)
00783             for (GoPointList::Iterator it2(list); it2; ++it2)
00784                 (*scoreBoard)[*it2] = c;
00785     }
00786     return score;
00787 }
00788 
00789 //----------------------------------------------------------------------------
00790 
00791 template<class BOARD>
00792 std::ostream& GoWriteBoard(std::ostream& out, const BOARD& bd)
00793 {
00794     // Write board to a buffer first to avoid intermingling if boards are
00795     // dumped from different threads at the same time (e.g. debugging
00796     // output after an assertion)
00797     std::ostringstream buffer;
00798     SgGrid size = bd.Size();
00799     if (size > 9)
00800         buffer << "   ";
00801     else
00802         buffer << "  ";
00803     SgGrid col;
00804     char c;
00805     for (col = 1, c = 'A'; col <= size; ++col, ++c)
00806     {
00807         if (c == 'I')
00808             ++c;
00809         buffer << c << ' ';
00810     }
00811     buffer << '\n';
00812     for (SgGrid row = size; row >= 1; --row)
00813     {
00814         if (size > 9 && row < 10)
00815             buffer << ' ';
00816         buffer << row << ' ';
00817         for (SgGrid col = 1; col <= size; ++col)
00818         {
00819             SgPoint p = SgPointUtil::Pt(col, row);
00820             switch (bd.GetColor(p))
00821             {
00822             case SG_BLACK:
00823                 buffer << 'X';
00824                 break;
00825             case SG_WHITE:
00826                 buffer << 'O';
00827                 break;
00828             case SG_EMPTY:
00829                 if (GoBoardUtil::IsHandicapPoint(size, col, row))
00830                     buffer << '+';
00831                 else
00832                     buffer << '.';
00833                 break;
00834             default:
00835                 SG_ASSERT(false);
00836             }
00837             buffer << ' ';
00838         }
00839         buffer << row;
00840         if (row <= 2)
00841         {
00842             if (size < 10)
00843                 buffer << "  ";
00844             else
00845                 buffer << "   ";
00846             // More important info first, because the number of infos shown
00847             // depends on the board size
00848             if (row == 1)
00849                 buffer << SgBW(bd.ToPlay()) << " to play";
00850         }
00851         buffer << '\n';
00852     }
00853     if (size > 9)
00854         buffer << "   ";
00855     else
00856         buffer << "  ";
00857     for (col = 1, c = 'A'; col <= size; ++col, ++c)
00858     {
00859         if (c == 'I')
00860             ++c;
00861         buffer << c << ' ';
00862     }
00863     buffer << '\n';
00864     out << buffer.str();
00865     return out;
00866 }
00867 
00868 inline std::ostream& operator<<(std::ostream& out, const GoBoard& bd)
00869 {
00870     return GoWriteBoard(out, bd);
00871 }
00872 
00873 //----------------------------------------------------------------------------
00874 
00875 /** Used to restore the ko rule to its current value in an exception-safe way.
00876     To use it, just declare a variable of this type on the stack for the
00877     desired scope. */
00878 class GoRestoreKoRule
00879 {
00880 public:
00881     GoRestoreKoRule(GoBoard& board);
00882 
00883     ~GoRestoreKoRule();
00884 
00885 private:
00886     GoBoard& m_board;
00887 
00888     GoRules::KoRule m_koRule;
00889 
00890     /** Not implemented. */
00891     GoRestoreKoRule(const GoRestoreKoRule&);
00892 
00893     /** Not implemented. */
00894     GoRestoreKoRule& operator=(const GoRestoreKoRule&);
00895 };
00896 
00897 inline GoRestoreKoRule::GoRestoreKoRule(GoBoard& board)
00898     : m_board(board),
00899       m_koRule(board.Rules().GetKoRule())
00900 {
00901 }
00902 
00903 inline GoRestoreKoRule::~GoRestoreKoRule()
00904 {
00905     m_board.Rules().SetKoRule(m_koRule);
00906 }
00907 
00908 //----------------------------------------------------------------------------
00909 
00910 /** Used to restore ToPlay to its current value in an exception-safe way.
00911     To use it, just declare a RestoreToPlay variable on the stack for the
00912     desired scope. */
00913 class GoRestoreToPlay
00914 {
00915 public:
00916     GoRestoreToPlay(GoBoard& board)
00917         : m_board(board),
00918           m_oldToPlay(board.ToPlay())
00919     { }
00920 
00921     ~GoRestoreToPlay()
00922     {
00923         m_board.SetToPlay(m_oldToPlay);
00924     }
00925 
00926 private:
00927     GoBoard& m_board;
00928 
00929     SgBlackWhite m_oldToPlay;
00930 
00931     /** Not implemented. */
00932     GoRestoreToPlay(const GoRestoreToPlay&);
00933 
00934     /** Not implemented. */
00935     GoRestoreToPlay& operator=(const GoRestoreToPlay&);
00936 };
00937 
00938 //----------------------------------------------------------------------------
00939 
00940 /** Iterate over all blocks' anchors on the board. */
00941 class GoBlockIterator
00942 {
00943 public:
00944     GoBlockIterator(const GoBoard& board)
00945         : m_board(board),
00946           m_p(board)
00947     {
00948         if (! *this)
00949             ++(*this);
00950     }
00951 
00952     void operator++()
00953     {
00954         do
00955         {
00956             ++m_p;
00957         }
00958         while (m_p && ! *this);
00959     }
00960 
00961     SgPoint operator*() const
00962     {
00963         SG_ASSERT(*this);
00964         return *m_p;
00965     }
00966 
00967     operator bool() const
00968     {
00969         SgPoint p = *m_p;
00970         return (m_board.Occupied(p) && p == m_board.Anchor(p));
00971     }
00972 
00973 private:
00974     const GoBoard& m_board;
00975 
00976     GoBoard::Iterator m_p;
00977 
00978     /** Not implemented. */
00979     GoBlockIterator(const GoBlockIterator&);
00980 
00981     /** Not implemented. */
00982     GoBlockIterator& operator=(const GoBlockIterator&);
00983 };
00984 
00985 //----------------------------------------------------------------------------
00986 
00987 /** Used to permit/forbid self-removal for certain periods of play.
00988     Restores the setting to the previous value in an exception-safe way.
00989     To use it, just declare a SelfRemoval variable on the stack for the
00990     desired scope. */
00991 class GoRestoreSuicide
00992 {
00993 public:
00994     GoRestoreSuicide(GoBoard& board, bool allow)
00995         : m_board(board),
00996           m_oldState(board.Rules().AllowSuicide())
00997     {
00998         m_board.Rules().SetAllowSuicide(allow);
00999     }
01000 
01001     ~GoRestoreSuicide()
01002     {
01003         m_board.Rules().SetAllowSuicide(m_oldState);
01004     }
01005 
01006 private:
01007     GoBoard& m_board;
01008 
01009     bool m_oldState;
01010 
01011     /** Not implemented. */
01012     GoRestoreSuicide(const GoRestoreSuicide&);
01013 
01014     /** Not implemented. */
01015     GoRestoreSuicide& operator=(const GoRestoreSuicide&);
01016 };
01017 
01018 //----------------------------------------------------------------------------
01019 
01020 /** Used to alter state of repetition and self-removal for certain periods of
01021     play.
01022     Restores the settings to the previous values in an exception-safe way.
01023     To use it, just declare a RestoreRepetitionAndRemoval variable on the
01024     stack for the desired scope. */
01025 class GoRestoreRepetitionAndSuicide
01026 {
01027 public:
01028     GoRestoreRepetitionAndSuicide(GoBoard& board, bool allowAnyRepetition,
01029                                   bool allowKoRepetition, bool allowSuicide)
01030         :  m_board(board),
01031            m_oldAnyRepetition(board.AnyRepetitionAllowed()),
01032            m_oldKoRepetition(board.KoRepetitionAllowed()),
01033            m_oldSuicide(board.Rules().AllowSuicide())
01034     {
01035         m_board.AllowAnyRepetition(allowAnyRepetition);
01036         m_board.AllowKoRepetition(allowKoRepetition);
01037         m_board.Rules().SetAllowSuicide(allowSuicide);
01038     }
01039 
01040     ~GoRestoreRepetitionAndSuicide()
01041     {
01042         m_board.AllowAnyRepetition(m_oldAnyRepetition);
01043         m_board.AllowKoRepetition(m_oldKoRepetition);
01044         m_board.Rules().SetAllowSuicide(m_oldSuicide);
01045     }
01046 
01047 private:
01048     GoBoard& m_board;
01049 
01050     /** arbitrary repetition for both players */
01051     bool m_oldAnyRepetition;
01052 
01053     bool m_oldKoRepetition;
01054 
01055     /** whether self-removal is allowed */
01056     bool m_oldSuicide;
01057 
01058     /** Not implemented. */
01059     GoRestoreRepetitionAndSuicide(const GoRestoreRepetitionAndSuicide&);
01060 
01061     /** Not implemented. */
01062     GoRestoreRepetitionAndSuicide&
01063     operator=(const GoRestoreRepetitionAndSuicide&);
01064 };
01065 
01066 //----------------------------------------------------------------------------
01067 
01068 /** Iterate through the anchors of all the blocks adjacent to the given
01069     point. */
01070 class GoNeighborBlockIterator
01071     : public SgPointIterator
01072 {
01073 public:
01074     GoNeighborBlockIterator(const GoBoard& board, SgPoint p, SgBlackWhite c)
01075         : SgPointIterator(m_points)
01076     {
01077         board.NeighborBlocks(p, c, m_points);
01078     }
01079 
01080     GoNeighborBlockIterator(const GoBoard& board, SgPoint p, SgBlackWhite c,
01081                             int maxLib)
01082         : SgPointIterator(m_points)
01083     {
01084         board.NeighborBlocks(p, c, maxLib, m_points);
01085     }
01086 
01087 
01088 private:
01089     /** At most 4 neighbor points, plus terminator. */
01090     SgPoint m_points[5];
01091 };
01092 
01093 //----------------------------------------------------------------------------
01094 
01095 static const int MAX_ADJACENT = (SG_MAX_SIZE + 1) * (SG_MAX_SIZE + 1) / 4;
01096 
01097 /** Iterate through the anchors of all the blocks adjacent to the given
01098     block. */
01099 template<class BOARD>
01100 class GoAdjBlockIterator
01101     : public SgPointIterator
01102 {
01103 public:
01104     GoAdjBlockIterator(const BOARD& board, SgPoint p, int maxLib);
01105 
01106 private:
01107     /** Maximum number of adjacent blocks.
01108         Not quite sure this is an upper limit, but couldn't find an
01109         example that had more adjacent stones than a spiral block with
01110         adjacent single stones spaced one apart. */
01111 
01112     SgPoint m_points[MAX_ADJACENT];
01113 };
01114 
01115 template<class BOARD>
01116 GoAdjBlockIterator<BOARD>::GoAdjBlockIterator(const BOARD& board,
01117                                               SgPoint p, int maxLib)
01118     : SgPointIterator(m_points)
01119 {
01120     board.AdjacentBlocks(p, maxLib, m_points, MAX_ADJACENT);
01121 }
01122 
01123 //----------------------------------------------------------------------------
01124 
01125 class GoNbIterator
01126     : public SgNbIterator
01127 {
01128 public:
01129     GoNbIterator(const GoBoard& bd, SgPoint p);
01130 };
01131 
01132 inline GoNbIterator::GoNbIterator(const GoBoard& bd, SgPoint p)
01133     : SgNbIterator(bd.BoardConst(), p)
01134 {
01135 }
01136 
01137 //----------------------------------------------------------------------------
01138 
01139 /** @todo move into its own file */
01140 namespace GoBoardWrite
01141 {
01142 
01143 /** Write a map of the board, showing marks for SgPointSet */
01144 class WriteMap
01145 {
01146 public:
01147     WriteMap(const GoBoard& bd, const SgPointSet& points)
01148     : m_bd(bd),
01149       m_points(points)
01150     {
01151     }
01152 
01153     const GoBoard& Board() const {return m_bd;}
01154 
01155     const SgPointSet& Points() const { return m_points; }
01156 
01157 private:
01158     const GoBoard& m_bd;
01159 
01160     const SgPointSet& m_points;
01161 };
01162 
01163 } // namespace GoBoardWrite
01164 
01165 std::ostream& operator<<(std::ostream& out, const GoBoardWrite::WriteMap& w);
01166 
01167 //----------------------------------------------------------------------------
01168 
01169 #endif // GO_BOARDUTIL_H


Sun Mar 13 2011 Doxygen 1.7.1