00001 //---------------------------------------------------------------------------- 00002 /** @file GoBoardUtil.cpp 00003 See GoBoardUtil.h 00004 */ 00005 //---------------------------------------------------------------------------- 00006 00007 #include "SgSystem.h" 00008 #include "GoBoardUtil.h" 00009 00010 #include <iomanip> 00011 #include <sstream> 00012 #include <string> 00013 #include "GoBoard.h" 00014 #include "GoModBoard.h" 00015 #include "GoMoveExecutor.h" 00016 #include "GoSafetySolver.h" 00017 #include "SgDebug.h" 00018 #include "SgException.h" 00019 #include "SgNbIterator.h" 00020 #include "SgProp.h" 00021 00022 using namespace std; 00023 using SgPropUtil::PointToSgfString; 00024 00025 //---------------------------------------------------------------------------- 00026 00027 namespace { 00028 00029 /** Function used in GoBoardUtil::CfgDistance() */ 00030 void CfgDistanceCheck(const GoBoard& bd, SgPointArray<int>& array, 00031 GoPointList& pointList, int d, SgPoint p) 00032 { 00033 if (! bd.IsBorder(p)) 00034 { 00035 if (bd.Occupied(p)) 00036 p = bd.Anchor(p); 00037 if (array[p] == numeric_limits<int>::max()) 00038 { 00039 array[p] = d; 00040 pointList.PushBack(p); 00041 } 00042 } 00043 } 00044 00045 /** Function used in GoBoardUtil::ScorePosition() */ 00046 void ScorePositionRecurse(const GoBoard& bd, SgPoint p, 00047 const SgPointSet& deadStones, SgMarker& marker, 00048 bool& isBlackAdjacent, bool& isWhiteAdjacent, 00049 int& nuPoints, int& nuDeadWhite, int& nuDeadBlack) 00050 { 00051 if (bd.IsBorder(p)) 00052 return; 00053 SgEmptyBlackWhite c = bd.GetColor(p); 00054 if (c != SG_EMPTY && ! deadStones.Contains(p)) 00055 { 00056 if (c == SG_BLACK) 00057 ++isBlackAdjacent; 00058 else 00059 ++isWhiteAdjacent; 00060 return; 00061 } 00062 if (! marker.NewMark(p)) 00063 return; 00064 ++nuPoints; 00065 if (c == SG_BLACK) 00066 ++nuDeadBlack; 00067 if (c == SG_WHITE) 00068 ++nuDeadWhite; 00069 ScorePositionRecurse(bd, p + SG_NS, deadStones, marker, isBlackAdjacent, 00070 isWhiteAdjacent, nuPoints, nuDeadWhite, nuDeadBlack); 00071 ScorePositionRecurse(bd, p - SG_NS, deadStones, marker, isBlackAdjacent, 00072 isWhiteAdjacent, nuPoints, nuDeadWhite, nuDeadBlack); 00073 ScorePositionRecurse(bd, p + SG_WE, deadStones, marker, isBlackAdjacent, 00074 isWhiteAdjacent, nuPoints, nuDeadWhite, nuDeadBlack); 00075 ScorePositionRecurse(bd, p - SG_WE, deadStones, marker, isBlackAdjacent, 00076 isWhiteAdjacent, nuPoints, nuDeadWhite, nuDeadBlack); 00077 } 00078 00079 } // namespace 00080 00081 //---------------------------------------------------------------------------- 00082 00083 void GoBoardUtil::AddNeighborBlocksOfColor(const GoBoard& bd, SgPoint p, 00084 SgBlackWhite c, 00085 SgVector<SgPoint>& neighbors) 00086 { 00087 if (bd.IsColor(p - SG_NS, c)) 00088 neighbors.Include(bd.Anchor(p - SG_NS)); 00089 if (bd.IsColor(p - SG_WE, c)) 00090 neighbors.Include(bd.Anchor(p - SG_WE)); 00091 if (bd.IsColor(p + SG_WE, c)) 00092 neighbors.Include(bd.Anchor(p + SG_WE)); 00093 if (bd.IsColor(p + SG_NS, c)) 00094 neighbors.Include(bd.Anchor(p + SG_NS)); 00095 } 00096 00097 void GoBoardUtil::AddWall(GoBoard& bd, 00098 SgBlackWhite color, 00099 SgPoint start, 00100 int length, 00101 int direction) 00102 { 00103 for (SgPoint p = start; length > 0; --length) 00104 { 00105 bd.Play(p, color); 00106 p += direction; 00107 } 00108 } 00109 00110 GoPointList GoBoardUtil::AdjacentStones(const GoBoard& bd, SgPoint point) 00111 { 00112 SG_ASSERT(bd.IsValidPoint(point)); 00113 SG_ASSERT(bd.Occupied(point)); 00114 const SgBlackWhite other = SgOppBW(bd.GetStone(point)); 00115 GoPointList result; 00116 SgMarker& mark = bd.m_userMarker; 00117 SgReserveMarker reserve(mark); 00118 SG_UNUSED(reserve); 00119 mark.Clear(); 00120 for (GoBoard::StoneIterator it(bd, point); it; ++it) 00121 { 00122 if (bd.NumNeighbors(*it, other) > 0) 00123 { 00124 SgPoint p = *it; 00125 if (bd.IsColor(p - SG_NS, other) && mark.NewMark(p - SG_NS)) 00126 result.PushBack(p - SG_NS); 00127 if (bd.IsColor(p - SG_WE, other) && mark.NewMark(p - SG_WE)) 00128 result.PushBack(p - SG_WE); 00129 if (bd.IsColor(p + SG_WE, other) && mark.NewMark(p + SG_WE)) 00130 result.PushBack(p + SG_WE); 00131 if (bd.IsColor(p + SG_NS, other) && mark.NewMark(p + SG_NS)) 00132 result.PushBack(p + SG_NS); 00133 } 00134 }; 00135 return result; 00136 } 00137 00138 void GoBoardUtil::AdjacentBlocks(const GoBoard& bd, SgPoint p, int maxLib, 00139 SgVector<SgPoint>* blocks) 00140 { 00141 SG_ASSERT(blocks); 00142 SgPoint a[SG_MAXPOINT]; 00143 int n = bd.AdjacentBlocks(p, maxLib, a, SG_MAXPOINT); 00144 blocks->SetTo(a, n); 00145 } 00146 00147 void GoBoardUtil::BlocksAdjacentToPoints(const GoBoard& bd, 00148 const SgVector<SgPoint>& points, 00149 SgBlackWhite c, 00150 SgVector<SgPoint>* blocks) 00151 { 00152 // Mark all points to avoid n^2 algorithm. 00153 SgMarker& mark = bd.m_userMarker; 00154 SgReserveMarker reserve(mark); 00155 SG_UNUSED(reserve); 00156 mark.Clear(); 00157 for (SgVectorIterator<SgPoint> it1(points); it1; ++it1) 00158 mark.Include(*it1); 00159 // Add the anchor of each adjacent block to the list of blocks. 00160 SgPoint a[SG_MAXPOINT]; 00161 int n = 0; 00162 for (SgVectorIterator<SgPoint> it2(points); it2; ++it2) 00163 { 00164 SgPoint p = *it2; 00165 if (bd.NumNeighbors(p, c) > 0) 00166 { 00167 if (bd.IsColor(p - SG_NS, c) 00168 && mark.NewMark(bd.Anchor(p - SG_NS))) 00169 a[n++] = bd.Anchor(p - SG_NS); 00170 if (bd.IsColor(p - SG_WE, c) 00171 && mark.NewMark(bd.Anchor(p - SG_WE))) 00172 a[n++] = bd.Anchor(p - SG_WE); 00173 if (bd.IsColor(p + SG_WE, c) 00174 && mark.NewMark(bd.Anchor(p + SG_WE))) 00175 a[n++] = bd.Anchor(p + SG_WE); 00176 if (bd.IsColor(p + SG_NS, c) 00177 && mark.NewMark(bd.Anchor(p + SG_NS))) 00178 a[n++] = bd.Anchor(p + SG_NS); 00179 } 00180 } 00181 blocks->SetTo(a, n); 00182 } 00183 00184 void GoBoardUtil::BlocksAdjacentToPoints(const GoBoard& bd, 00185 const SgPointSet& points, 00186 SgBlackWhite c, 00187 SgVector<SgPoint>* blocks) 00188 { 00189 // exact copy from list version above 00190 SG_ASSERT(blocks); 00191 // Mark all points to avoid n^2 algorithm. 00192 SgMarker& mark = bd.m_userMarker; 00193 SgReserveMarker reserve(mark); 00194 SG_UNUSED(reserve); 00195 mark.Clear(); 00196 for (SgSetIterator it1(points); it1; ++it1) 00197 mark.Include(*it1); 00198 // Add the anchor of each adjacent block to the list of blocks. 00199 SgPoint a[SG_MAXPOINT]; 00200 int n = 0; 00201 for (SgSetIterator it2(points); it2; ++it2) 00202 { 00203 SgPoint p = *it2; 00204 if (bd.NumNeighbors(p, c) > 0) 00205 { 00206 if (bd.IsColor(p - SG_NS, c) 00207 && mark.NewMark(bd.Anchor(p - SG_NS))) 00208 a[n++] = bd.Anchor(p - SG_NS); 00209 if (bd.IsColor(p - SG_WE, c) 00210 && mark.NewMark(bd.Anchor(p - SG_WE))) 00211 a[n++] = bd.Anchor(p - SG_WE); 00212 if (bd.IsColor(p + SG_WE, c) 00213 && mark.NewMark(bd.Anchor(p + SG_WE))) 00214 a[n++] = bd.Anchor(p + SG_WE); 00215 if (bd.IsColor(p + SG_NS, c) 00216 && mark.NewMark(bd.Anchor(p + SG_NS))) 00217 a[n++] = bd.Anchor(p + SG_NS); 00218 } 00219 } 00220 blocks->SetTo(a, n); 00221 } 00222 00223 bool GoBoardUtil::BlockIsAdjacentTo(const GoBoard& bd, SgPoint block, 00224 const SgPointSet& walls) 00225 { 00226 for (GoBoard::StoneIterator it(bd, block); it; ++it) 00227 { 00228 if ( walls.Contains((*it) + SG_NS) 00229 || walls.Contains((*it) - SG_NS) 00230 || walls.Contains((*it) + SG_WE) 00231 || walls.Contains((*it) - SG_WE) 00232 ) 00233 return true; 00234 } 00235 return false; 00236 } 00237 00238 SgPointArray<int> GoBoardUtil::CfgDistance(const GoBoard& bd, SgPoint p, 00239 int maxDist) 00240 { 00241 SgPointArray<int> array(numeric_limits<int>::max()); 00242 GoPointList pointList; 00243 if (bd.Occupied(p)) 00244 p = bd.Anchor(p); 00245 pointList.PushBack(p); 00246 int begin = 0; 00247 int end = 1; 00248 int d = 0; 00249 array[p] = d; 00250 while (begin != end && d < maxDist) 00251 { 00252 ++d; 00253 for (int i = begin; i != end; ++i) 00254 { 00255 p = pointList[i]; 00256 if (bd.Occupied(p)) 00257 { 00258 for (GoBoard::StoneIterator it(bd, p); it; ++it) 00259 { 00260 CfgDistanceCheck(bd, array, pointList, d, *it + SG_NS); 00261 CfgDistanceCheck(bd, array, pointList, d, *it - SG_NS); 00262 CfgDistanceCheck(bd, array, pointList, d, *it + SG_WE); 00263 CfgDistanceCheck(bd, array, pointList, d, *it - SG_WE); 00264 } 00265 } 00266 else 00267 { 00268 CfgDistanceCheck(bd, array, pointList, d, p + SG_NS); 00269 CfgDistanceCheck(bd, array, pointList, d, p - SG_NS); 00270 CfgDistanceCheck(bd, array, pointList, d, p + SG_WE); 00271 CfgDistanceCheck(bd, array, pointList, d, p - SG_WE); 00272 } 00273 } 00274 begin = end; 00275 end = pointList.Length(); 00276 } 00277 return array; 00278 } 00279 00280 void GoBoardUtil::DumpBoard(const GoBoard& bd, std::ostream& out) 00281 { 00282 const int size = bd.Size(); 00283 out << bd; 00284 if (bd.MoveNumber() == 0) 00285 return; 00286 out << "(;SZ[" << size << "]\n"; 00287 const GoSetup& setup = bd.Setup(); 00288 if (! setup.IsEmpty()) 00289 { 00290 for (SgBWIterator it; it; ++it) 00291 { 00292 SgBlackWhite c = *it; 00293 int stoneNumber = 0; 00294 out << (c == SG_BLACK ? "AB" : "AW"); 00295 for (SgSetIterator it2(setup.m_stones[c]); it2; ++it2) 00296 { 00297 SgPoint p = *it2; 00298 ++stoneNumber; 00299 out << '[' << PointToSgfString(p, size, SG_PROPPOINTFMT_GO) 00300 << ']'; 00301 if (stoneNumber % 10 == 0) 00302 out << '\n'; 00303 } 00304 out << '\n'; 00305 } 00306 out << "PL[" << (setup.m_player == SG_BLACK ? 'B' : 'W') << "]\n"; 00307 } 00308 int moveNumber = 0; 00309 for (int i = 0; i < bd.MoveNumber(); ++i) 00310 { 00311 GoPlayerMove move = bd.Move(i); 00312 out << ';'; 00313 out << (move.Color() == SG_BLACK ? "B" : "W"); 00314 ++moveNumber; 00315 out << '[' << PointToSgfString(move.Point(), size, SG_PROPPOINTFMT_GO) 00316 << ']'; 00317 if (moveNumber % 10 == 0) 00318 out << '\n'; 00319 } 00320 out << ")\n"; 00321 } 00322 00323 void GoBoardUtil::ExpandToBlocks(const GoBoard& board, SgPointSet& pointSet) 00324 { 00325 // @todo faster to use GoBoard::StoneIterator in GoBoard? 00326 SG_ASSERT(pointSet.SubsetOf(board.Occupied())); 00327 int size = board.Size(); 00328 for (SgBlackWhite color = SG_BLACK; color <= SG_WHITE; ++color) 00329 { 00330 SgPointSet set = pointSet & board.All(color); 00331 bool change(true); 00332 while (change) 00333 { 00334 change = false; 00335 SgPointSet next = set | (set.Border(size) & board.All(color)); 00336 if (next != set) 00337 { 00338 change = true; 00339 set = next; 00340 } 00341 } 00342 pointSet |= set; 00343 } 00344 } 00345 00346 void GoBoardUtil::DiagonalsOfColor(const GoBoard& bd, SgPoint p, int c, 00347 SgVector<SgPoint>* diagonals) 00348 { 00349 diagonals->Clear(); 00350 if (bd.IsColor(p - SG_NS - SG_WE, c)) 00351 diagonals->PushBack(p - SG_NS - SG_WE); 00352 if (bd.IsColor(p - SG_NS + SG_WE, c)) 00353 diagonals->PushBack(p - SG_NS + SG_WE); 00354 if (bd.IsColor(p + SG_NS - SG_WE, c)) 00355 diagonals->PushBack(p + SG_NS - SG_WE); 00356 if (bd.IsColor(p + SG_NS + SG_WE, c)) 00357 diagonals->PushBack(p + SG_NS + SG_WE); 00358 } 00359 00360 bool GoBoardUtil::EndOfGame(const GoBoard& bd) 00361 { 00362 SgBlackWhite toPlay = bd.ToPlay(); 00363 GoPlayerMove passToPlay(toPlay, SG_PASS); 00364 GoPlayerMove passOpp(SgOppBW(toPlay), SG_PASS); 00365 int moveNumber = bd.MoveNumber(); 00366 if (bd.Rules().TwoPassesEndGame()) 00367 { 00368 return moveNumber >= 2 00369 && bd.Move(moveNumber - 1) == passOpp 00370 && bd.Move(moveNumber - 2) == passToPlay; 00371 } 00372 else // Three passes in a row end the game. 00373 { 00374 return moveNumber >= 3 00375 && bd.Move(moveNumber - 1) == passOpp 00376 && bd.Move(moveNumber - 2) == passToPlay 00377 && bd.Move(moveNumber - 3) == passOpp; 00378 } 00379 } 00380 00381 00382 bool GoBoardUtil::GenerateIfLegal(const GoBoard& bd, SgPoint move, 00383 SgVector<SgPoint>* moves) 00384 { 00385 if (bd.IsLegal(move)) 00386 { 00387 if (moves) 00388 moves->Include(move); 00389 /* */ return true; /* */ 00390 } 00391 return false; 00392 } 00393 00394 void GoBoardUtil::GetCoordString(SgMove p, std::string* s, int boardSize) 00395 { 00396 SG_ASSERT(s); 00397 SG_ASSERT(p != SG_NULLMOVE); 00398 if (p == SG_PASS) 00399 *s = "Pass"; 00400 else if (p == SG_COUPONMOVE) 00401 *s = "Coupon"; 00402 else 00403 { 00404 int col = SgPointUtil::Col(p); 00405 int row = SgPointUtil::Row(p); 00406 if (9 <= col) 00407 ++col; // skip 'I' 00408 ostringstream o; 00409 o << static_cast<char>('A' + col - 1) << (boardSize + 1 - row); 00410 *s = o.str(); 00411 } 00412 } 00413 00414 bool GoBoardUtil::HasAdjacentBlocks(const GoBoard& bd, SgPoint p, 00415 int maxLib) 00416 { 00417 SG_ASSERT(bd.Occupied(p)); 00418 const SgBlackWhite other = SgOppBW(bd.GetStone(p)); 00419 for (GoBoard::StoneIterator stone(bd, p); stone; ++stone) 00420 for (SgNb4Iterator nb(*stone); nb; ++nb) 00421 if (bd.IsColor(*nb, other) && bd.AtMostNumLibs(*nb, maxLib)) 00422 return true; 00423 return false; 00424 } 00425 00426 bool GoBoardUtil::IsHandicapPoint(SgGrid size, SgGrid col, SgGrid row) 00427 { 00428 SgGrid line1; 00429 SgGrid line3; 00430 if (size < 9) 00431 return false; 00432 if (size <= 11) 00433 { 00434 line1 = 3; 00435 line3 = size - 2; 00436 } 00437 else 00438 { 00439 line1 = 4; 00440 line3 = size - 3; 00441 } 00442 if (size > 11 && size % 2 != 0) // mark mid points 00443 { 00444 SgGrid line2 = size / 2 + 1; 00445 return (row == line1 || row == line2 || row == line3) 00446 && (col == line1 || col == line2 || col == line3); 00447 } 00448 else 00449 return (row == line1 || row == line3) 00450 && (col == line1 || col == line3); 00451 } 00452 00453 bool GoBoardUtil::IsSimpleEyeOfBlock(const GoBoard& bd, SgPoint lib, 00454 SgPoint blockAnchor, 00455 const SgVector<SgPoint>& eyes) 00456 { 00457 SgBlackWhite color = bd.GetStone(blockAnchor); 00458 // need IsColor test for nbs because might be off board. 00459 if (bd.IsColor(lib - SG_NS, color) 00460 && bd.Anchor(lib - SG_NS) != blockAnchor) 00461 return false; 00462 if (bd.IsColor(lib + SG_NS, color) 00463 && bd.Anchor(lib + SG_NS) != blockAnchor) 00464 return false; 00465 if (bd.IsColor(lib - SG_WE, color) 00466 && bd.Anchor(lib - SG_WE) != blockAnchor) 00467 return false; 00468 if (bd.IsColor(lib + SG_WE, color) 00469 && bd.Anchor(lib + SG_WE) != blockAnchor) 00470 return false; 00471 int nuForFalse = (bd.Line(lib) == 1) ? 1 : 2; 00472 // no need to check diagonals for same block since direct neighbors are. 00473 for (SgNb4DiagIterator it(lib); it; ++it) 00474 { 00475 SgPoint nb(*it); 00476 if (! bd.IsBorder(nb) && ! bd.IsColor(nb, color) 00477 && ! eyes.Contains(nb)) 00478 if (--nuForFalse <= 0) 00479 return false; 00480 } 00481 return true; 00482 } 00483 00484 bool GoBoardUtil::IsSnapback(const GoBoard& constBd, SgPoint p) 00485 { 00486 SG_ASSERT(constBd.IsValidPoint(p)); 00487 SG_ASSERT(constBd.Occupied(p)); 00488 00489 bool snapback = false; 00490 if (constBd.IsSingleStone(p) && constBd.InAtari(p)) 00491 { 00492 const SgPoint lib = constBd.TheLiberty(p); 00493 GoModBoard mbd(constBd); 00494 GoBoard& bd = mbd.Board(); 00495 const bool isLegal = 00496 GoBoardUtil::PlayIfLegal(bd, lib, SgOppBW(bd.GetStone(p))); 00497 if ( isLegal 00498 && bd.InAtari(lib) 00499 && ! bd.IsSingleStone(lib) 00500 ) 00501 snapback = true; 00502 if (isLegal) 00503 bd.Undo(); 00504 } 00505 return snapback; 00506 } 00507 00508 bool GoBoardUtil::ManySecondaryLibs(const GoBoard& bd, SgPoint block) 00509 { 00510 // was always 8, not enough for loose ladder in CAPTURES.SGB, problem 2 00511 // one liberty can have 3 new secondary, total of 4 which are taken by 00512 // opp. move. 00513 // current value is just a guess, experiment. 00514 const int LIBERTY_LIMIT = 9; 00515 static SgMarker m; 00516 m.Clear(); 00517 int nu = 0; 00518 for (GoBoard::LibertyIterator it(bd, block); it; ++it) 00519 { 00520 SgPoint p(*it); 00521 if (m.NewMark(p)) 00522 if (++nu >= LIBERTY_LIMIT) 00523 return true; 00524 for (SgNb4Iterator itn(p); itn; ++itn) 00525 { 00526 if (bd.IsEmpty(*itn) && m.NewMark(*itn)) 00527 if (++nu >= LIBERTY_LIMIT) 00528 return true; 00529 } 00530 } 00531 return (nu >= LIBERTY_LIMIT); 00532 } 00533 00534 SgSList<SgPoint,4> GoBoardUtil::NeighborsOfColor(const GoBoard& bd, SgPoint p, 00535 int c) 00536 { 00537 SgSList<SgPoint,4> result; 00538 if (bd.IsColor(p - SG_NS, c)) 00539 result.PushBack(p - SG_NS); 00540 if (bd.IsColor(p - SG_WE, c)) 00541 result.PushBack(p - SG_WE); 00542 if (bd.IsColor(p + SG_WE, c)) 00543 result.PushBack(p + SG_WE); 00544 if (bd.IsColor(p + SG_NS, c)) 00545 result.PushBack(p + SG_NS); 00546 return result; 00547 } 00548 00549 void GoBoardUtil::NeighborsOfColor(const GoBoard& bd, SgPoint p, int c, 00550 SgVector<SgPoint>* neighbors) 00551 { 00552 neighbors->Clear(); 00553 if (bd.IsColor(p - SG_NS, c)) 00554 neighbors->PushBack(p - SG_NS); 00555 if (bd.IsColor(p - SG_WE, c)) 00556 neighbors->PushBack(p - SG_WE); 00557 if (bd.IsColor(p + SG_WE, c)) 00558 neighbors->PushBack(p + SG_WE); 00559 if (bd.IsColor(p + SG_NS, c)) 00560 neighbors->PushBack(p + SG_NS); 00561 } 00562 00563 bool GoBoardUtil::PassWins(const GoBoard& bd, SgBlackWhite toPlay) 00564 { 00565 if (toPlay != bd.ToPlay()) 00566 // Not defined if non-alternating moves 00567 return false; 00568 if (! bd.Rules().CaptureDead() || bd.Rules().JapaneseScoring()) 00569 return false; 00570 if (bd.GetLastMove() != SG_PASS) 00571 return false; 00572 float komi = bd.Rules().Komi().ToFloat(); 00573 float score = GoBoardUtil::TrompTaylorScore(bd, komi); 00574 if ((score > 0 && toPlay == SG_BLACK) 00575 || (score < 0 && toPlay == SG_WHITE)) 00576 return true; 00577 return false; 00578 } 00579 00580 bool GoBoardUtil::PlayIfLegal(GoBoard& bd, SgPoint p, SgBlackWhite player) 00581 { 00582 if (p != SG_PASS && p != SG_COUPONMOVE) 00583 { 00584 if (! bd.IsEmpty(p)) 00585 return false; 00586 if (! bd.Rules().AllowSuicide() && bd.IsSuicide(p, player)) 00587 return false; 00588 } 00589 bd.Play(p, player); 00590 if (bd.LastMoveInfo(GO_MOVEFLAG_ILLEGAL)) 00591 { 00592 bd.Undo(); 00593 return false; 00594 } 00595 return true; 00596 } 00597 00598 void GoBoardUtil::ReduceToAnchors(const GoBoard& bd, 00599 SgVector<SgPoint>* stones) 00600 { 00601 SG_ASSERT(stones); 00602 SgVector<SgPoint> result; 00603 for (SgVectorIterator<SgPoint> stone(*stones); stone; ++stone) 00604 if (bd.Occupied(*stone)) 00605 result.Insert(bd.Anchor(*stone)); 00606 stones->SwapWith(&result); 00607 } 00608 00609 void GoBoardUtil::ReduceToAnchors(const GoBoard& bd, 00610 const SgVector<SgPoint>& stones, 00611 SgSList<SgPoint,SG_MAXPOINT>& anchors) 00612 { 00613 anchors.Clear(); 00614 for (SgVectorIterator<SgPoint> it(stones); it; ++it) 00615 if (bd.Occupied(*it)) 00616 anchors.Include(bd.Anchor(*it)); 00617 } 00618 00619 void GoBoardUtil::RegionCode(const GoBoard& bd, 00620 const SgVector<SgPoint>& region, 00621 SgHashCode* c) 00622 { 00623 BOOST_STATIC_ASSERT(SG_BLACK < 2); 00624 BOOST_STATIC_ASSERT(SG_WHITE < 2); 00625 00626 c->Clear(); 00627 for (SgVectorIterator<SgPoint> it(region); it; ++it) 00628 { 00629 SgPoint p = *it; 00630 if (bd.Occupied(p)) 00631 SgHashUtil::XorZobrist(*c, p + bd.GetStone(p) * SG_MAXPOINT); 00632 } 00633 } 00634 00635 bool GoBoardUtil::RemainingChineseHandicap(const GoBoard& bd) 00636 { 00637 const GoRules& rules = bd.Rules(); 00638 return (! rules.JapaneseHandicap() 00639 && rules.Handicap() > bd.TotalNumStones(SG_BLACK)); 00640 } 00641 00642 float GoBoardUtil::ScoreSimpleEndPosition(const GoBoard& bd, float komi, 00643 bool noCheck) 00644 { 00645 int score = 0; 00646 for (GoBoard::Iterator it(bd); it; ++it) 00647 score += ScorePoint(bd, *it, noCheck); 00648 return (score - komi); 00649 } 00650 00651 void GoBoardUtil::SharedLiberties(const GoBoard& bd, 00652 SgPoint block1, 00653 SgPoint block2, 00654 SgVector<SgPoint>* sharedLibs) 00655 { 00656 SG_ASSERT(sharedLibs); 00657 SG_ASSERT(bd.Occupied(block1)); 00658 SG_ASSERT(bd.Occupied(block2)); 00659 block1 = bd.Anchor(block1); 00660 block2 = bd.Anchor(block2); 00661 sharedLibs->Clear(); 00662 for (GoBoard::LibertyIterator libIter(bd, block1); libIter; ++libIter) 00663 { 00664 SgPoint lib = *libIter; 00665 if (bd.IsLibertyOfBlock(lib, block2)) 00666 sharedLibs->PushBack(lib); 00667 } 00668 } 00669 00670 void GoBoardUtil::SharedLibertyBlocks(const GoBoard& bd, SgPoint anchor, 00671 int maxLib, SgVector<SgPoint>* blocks) 00672 { 00673 SG_ASSERT(blocks); 00674 // Mark all points and previous blocks. 00675 SgMarker& mark = bd.m_userMarker; 00676 SgReserveMarker reserve(mark); 00677 SG_UNUSED(reserve); 00678 mark.Clear(); 00679 for (GoBoard::StoneIterator it1(bd, anchor); it1; ++it1) 00680 mark.Include(*it1); 00681 for (SgVectorIterator<SgPoint> it(*blocks); it; ++it) 00682 { 00683 SgPoint a = *it; 00684 for (GoBoard::StoneIterator it(bd, a); it; ++it) 00685 mark.Include(*it); 00686 } 00687 SgBlackWhite c = bd.GetStone(anchor); 00688 // Add the anchor of each adjacent block to the list of blocks. 00689 for (GoBoard::LibertyIterator it2(bd, anchor); it2; ++it2) 00690 { 00691 SgPoint p = *it2; 00692 if (bd.NumNeighbors(p, c) > 0) 00693 { 00694 if (bd.IsColor(p - SG_NS, c) && mark.NewMark(bd.Anchor(p - SG_NS)) 00695 && bd.AtMostNumLibs(p - SG_NS, maxLib)) 00696 blocks->PushBack(bd.Anchor(p - SG_NS)); 00697 if (bd.IsColor(p - SG_WE, c) && mark.NewMark(bd.Anchor(p - SG_WE)) 00698 && bd.AtMostNumLibs(p - SG_WE, maxLib)) 00699 blocks->PushBack(bd.Anchor(p - SG_WE)); 00700 if (bd.IsColor(p + SG_WE, c) && mark.NewMark(bd.Anchor(p + SG_WE)) 00701 && bd.AtMostNumLibs(p + SG_WE, maxLib)) 00702 blocks->PushBack(bd.Anchor(p + SG_WE)); 00703 if (bd.IsColor(p + SG_NS, c) && mark.NewMark(bd.Anchor(p + SG_NS)) 00704 && bd.AtMostNumLibs(p + SG_NS, maxLib)) 00705 blocks->PushBack(bd.Anchor(p + SG_NS)); 00706 } 00707 } 00708 } 00709 00710 void GoBoardUtil::UndoAll(GoBoard& bd) 00711 { 00712 while (bd.CanUndo()) 00713 bd.Undo(); 00714 } 00715 00716 bool GoBoardUtil::AtLeastTwoSharedLibs(const GoBoard& bd, SgPoint block1, 00717 SgPoint block2) 00718 { 00719 SG_ASSERT(bd.Occupied(block1)); 00720 SG_ASSERT(bd.Occupied(block2)); 00721 //block1 = bd.Anchor(block1); 00722 block2 = bd.Anchor(block2); 00723 bool fHasOneShared = false; 00724 for (GoBoard::LibertyIterator libIter(bd, block1); libIter; ++libIter) 00725 { 00726 if (bd.IsLibertyOfBlock(*libIter, block2)) 00727 { 00728 if (fHasOneShared) 00729 return true; 00730 fHasOneShared = true; 00731 } 00732 } 00733 return false; 00734 } 00735 00736 void GoBoardUtil::TestForChain(GoBoard& bd, SgPoint block, SgPoint block2, 00737 SgPoint lib, SgVector<SgPoint>* extended) 00738 { 00739 if (AtLeastTwoSharedLibs(bd, block, block2)) 00740 extended->PushBack(block); 00741 else // protected lib. 00742 { 00743 GoRestoreToPlay r(bd); 00744 bd.SetToPlay(SgOppBW(bd.GetStone(block))); 00745 if (MoveNotLegalOrAtari(bd, lib)) 00746 extended->PushBack(block); 00747 } 00748 } 00749 00750 bool GoBoardUtil::HasStonesOfBothColors(const GoBoard& bd, 00751 const SgVector<SgPoint>& stones) 00752 { 00753 SgBWArray<bool> has(false); 00754 for (SgVectorIterator<SgPoint> it(stones); it; ++it) 00755 { 00756 if (bd.Occupied(*it)) 00757 { 00758 SgBlackWhite color(bd.GetStone(*it)); 00759 has[color] = true; 00760 if (has[SgOppBW(color)]) 00761 return true; 00762 } 00763 } 00764 return false; 00765 } 00766 00767 bool GoBoardUtil::MoveNotLegalOrAtari(GoBoard& bd, SgPoint move) 00768 { 00769 GoMoveExecutor execute(bd, move); 00770 return (! execute.IsLegal() || bd.InAtari(move)); 00771 } 00772 00773 bool GoBoardUtil::MoveLegalAndNotAtari(GoBoard& bd, SgPoint move) 00774 { 00775 GoMoveExecutor execute(bd, move); 00776 return (execute.IsLegal() && ! bd.InAtari(move)); 00777 } 00778 00779 bool GoBoardUtil::ScorePosition(const GoBoard& bd, 00780 const SgPointSet& deadStones, float& score) 00781 { 00782 SgMarker marker; 00783 int stones = 0; 00784 int territory = 0; 00785 int dead = 0; 00786 for (GoBoard::Iterator it(bd); it; ++it) 00787 { 00788 SgPoint p = *it; 00789 if (bd.Occupied(p) && ! deadStones.Contains(p)) 00790 { 00791 if (bd.GetColor(p) == SG_BLACK) 00792 ++stones; 00793 else 00794 --stones; 00795 continue; 00796 } 00797 if (marker.Contains(p)) 00798 continue; 00799 int nuPoints = 0; 00800 int nuDeadWhite = 0; 00801 int nuDeadBlack = 0; 00802 bool isBlackAdjacent = false; 00803 bool isWhiteAdjacent = false; 00804 ScorePositionRecurse(bd, p, deadStones, marker, isBlackAdjacent, 00805 isWhiteAdjacent, nuPoints, nuDeadWhite, 00806 nuDeadBlack); 00807 if ((nuDeadWhite > 0 && nuDeadBlack > 0) 00808 || (isBlackAdjacent && nuDeadBlack > 0) 00809 || (isWhiteAdjacent && nuDeadWhite > 0)) 00810 return false; 00811 dead += nuDeadBlack; 00812 dead -= nuDeadWhite; 00813 if (isBlackAdjacent && ! isWhiteAdjacent) 00814 territory += nuPoints; 00815 else if (isWhiteAdjacent && ! isBlackAdjacent) 00816 territory -= nuPoints; 00817 } 00818 int prisoners = bd.NumPrisoners(SG_BLACK) - bd.NumPrisoners(SG_WHITE); 00819 if (bd.Rules().JapaneseScoring()) 00820 score = territory - dead - prisoners - bd.Rules().Komi().ToFloat(); 00821 else 00822 score = territory + stones - bd.Rules().Komi().ToFloat(); 00823 return true; 00824 } 00825 00826 int GoBoardUtil::Stones(const GoBoard& bd, SgPoint p, SgPoint stones[]) 00827 { 00828 SG_ASSERT(bd.IsValidPoint(p)); 00829 SG_ASSERT(bd.Occupied(p)); 00830 if (bd.IsSingleStone(p)) 00831 { 00832 stones[0] = p; 00833 return 1; 00834 } 00835 else 00836 { 00837 int nm = 0; 00838 for (GoBoard::StoneIterator it(bd, bd.Anchor(p)); it; ++it) 00839 stones[nm++] = p; 00840 return nm; 00841 } 00842 } 00843 00844 bool GoBoardUtil::TwoPasses(const GoBoard& bd) 00845 { 00846 SgBlackWhite toPlay = bd.ToPlay(); 00847 GoPlayerMove passToPlay(toPlay, SG_PASS); 00848 GoPlayerMove passOpp(SgOppBW(toPlay), SG_PASS); 00849 int moveNumber = bd.MoveNumber(); 00850 return ( moveNumber >= 2 00851 && bd.Move(moveNumber - 1) == passOpp 00852 && bd.Move(moveNumber - 2) == passToPlay 00853 ); 00854 } 00855 00856 SgPointSet GoBoardUtil::Lines(const GoBoard& bd, SgGrid from, SgGrid to) 00857 { 00858 SG_ASSERT(from >= 1); 00859 SG_ASSERT(from <= to); 00860 SG_ASSERT(to <= (bd.Size() + 1) / 2); 00861 SgPointSet lines; 00862 for (SgGrid i = from; i <= to; ++i) 00863 lines |= bd.LineSet(i); 00864 return lines; 00865 } 00866 00867 SgRect GoBoardUtil::GetDirtyRegion(const GoBoard& bd, SgMove move, 00868 SgBlackWhite colour, bool checklibs, 00869 bool premove) 00870 { 00871 SgRect dirty; 00872 if (move == SG_PASS) 00873 return dirty; 00874 00875 SgBlackWhite opp = SgOppBW(colour); 00876 00877 // Point played has changed 00878 dirty.Include(move); 00879 00880 SgPointSet blocks; 00881 00882 // This move adjusts libs for all adjacent blocks 00883 if (checklibs) 00884 { 00885 for (SgNb4Iterator inb(move); inb; ++inb) 00886 if (bd.Occupied(*inb)) 00887 for (GoBoard::StoneIterator istone(bd, *inb); istone; 00888 ++istone) 00889 dirty.Include(*istone); 00890 } 00891 00892 // Check if this move will make a capture 00893 if (premove) 00894 { 00895 for (SgNb4Iterator inb(move); inb; ++inb) 00896 { 00897 if (bd.IsColor(*inb, opp) && bd.NumLiberties(*inb) == 1) 00898 { 00899 for (GoBoard::StoneIterator icap(bd, *inb); icap; ++icap) 00900 { 00901 dirty.Include(*icap); 00902 00903 // Track blocks who gain libs as a result of capture 00904 if (checklibs) 00905 { 00906 for (SgNb4Iterator inb2(*icap); inb2; ++inb2) 00907 if (bd.IsColor(*inb2, colour)) 00908 blocks.Include(bd.Anchor(*inb2)); 00909 } 00910 } 00911 } 00912 } 00913 } 00914 00915 // Check if this move did make a capture 00916 if (! premove && bd.CapturingMove()) 00917 { 00918 for (GoPointList::Iterator icaptures(bd.CapturedStones()); icaptures; 00919 ++icaptures) 00920 { 00921 dirty.Include(*icaptures); 00922 00923 // Track blocks who gained liberties as a result of a capture 00924 if (checklibs) 00925 { 00926 for (SgNb4Iterator inb(*icaptures); inb; ++inb) 00927 if (bd.IsColor(*inb, colour)) 00928 blocks.Include(bd.Anchor(*inb)); 00929 } 00930 } 00931 } 00932 00933 // Now mark all stones of blocks that gained liberties 00934 if (checklibs) 00935 { 00936 for (SgSetIterator iblocks(blocks); iblocks; ++iblocks) 00937 for (GoBoard::StoneIterator istone(bd, *iblocks); istone; 00938 ++istone) 00939 dirty.Include(*istone); 00940 } 00941 00942 return dirty; 00943 } 00944 00945 int GoBoardUtil::Approx2Libs(const GoBoard& board, SgPoint block, 00946 SgPoint p, SgBlackWhite color) 00947 { 00948 int libs2 = 0; 00949 for (SgNb4Iterator inb(p); inb; ++inb) 00950 { 00951 SgPoint nb = *inb; 00952 if (board.IsEmpty(nb)) 00953 libs2++; 00954 else if (board.IsColor(nb, color) 00955 && board.Anchor(nb) != board.Anchor(block)) 00956 libs2 += board.NumLiberties(nb); // May double count libs 00957 } 00958 00959 return libs2; 00960 } 00961 00962 //---------------------------------------------------------------------------- 00963 00964 std::ostream& operator<<(std::ostream& out, const GoBoardWrite::WriteMap& w) 00965 { 00966 w.Points().Write(out, w.Board().Size()); 00967 return out; 00968 } 00969 00970 //----------------------------------------------------------------------------