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