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 > 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