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


17 Jun 2010 Doxygen 1.4.7