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