00001 //---------------------------------------------------------------------------- 00002 /** @file GoRegion.h 00003 A GoRegion contained in a @see GoRegionBoard. 00004 00005 A GoRegion is a maximal connected set of points surrounded by stones 00006 of the same color. 00007 00008 Regions compute properties such as 1-vitality, 00009 2-vitality [Mueller 95, p. 62-63],[Mueller 97b]. 00010 They are used for static and search-based safety solvers. 00011 They are also used for better move generation, e.g. in semeai. 00012 */ 00013 //---------------------------------------------------------------------------- 00014 00015 #ifndef GO_REGION_H 00016 #define GO_REGION_H 00017 00018 #include <bitset> 00019 #include <iosfwd> 00020 #include "GoBoard.h" 00021 #include "GoBoardUtil.h" 00022 #include "GoEyeCount.h" 00023 #include "SgVector.h" 00024 #include "SgIncrementalStack.h" 00025 #include "SgMiaiStrategy.h" 00026 #include "SgPointArray.h" 00027 00028 class GoBlock; 00029 class GoChain; 00030 class GoRegionBoard; 00031 00032 //---------------------------------------------------------------------------- 00033 00034 /** Information computed about a GoRegion. 00035 @todo Does not follow naming convention for constants in global 00036 namespace; rename to all-uppercase and use prefix GO_ 00037 */ 00038 enum GoRegionFlag 00039 { 00040 GO_REGION_SMALL, 00041 GO_REGION_CORRIDOR, 00042 GO_REGION_STATIC_1VC, 00043 GO_REGION_1VC, 00044 GO_REGION_STATIC_2V, 00045 GO_REGION_2V, 00046 GO_REGION_SINGLE_BLOCK_BOUNDARY, 00047 GO_REGION_OPP_CAN_LIVE_INSIDE, 00048 GO_REGION_AT_LEAST_SEKI, 00049 GO_REGION_SAFE, 00050 GO_REGION_PROTECTED_CUTS, 00051 GO_REGION_STATIC_1VITAL, 00052 GO_REGION_1VITAL, 00053 GO_REGION_USED_FOR_MERGE, 00054 GO_REGION_VALID, 00055 GO_REGION_COMPUTED_BLOCKS, 00056 GO_REGION_COMPUTED_CHAINS, 00057 GO_REGION_COMPUTED_NAKADE, 00058 _GO_REGION_FLAG_COUNT 00059 }; 00060 00061 /** Set of GoRegionFlag */ 00062 typedef std::bitset<_GO_REGION_FLAG_COUNT> GoRegionFlags; 00063 00064 //---------------------------------------------------------------------------- 00065 00066 /** GoRegion represents a region surrounded by blocks of one color. 00067 00068 Each empty point, and each point occupied by white, 00069 is contained in exactly one black region. 00070 Similarly each empty point, and each point occupied by black, 00071 is contained in exactly one white region. 00072 00073 A region keeps data such as its color, the blocks and chains of its color, 00074 the eye status, and a strategy to follow to keep the region safe. 00075 00076 Regions can provide liberties for its boundary blocks. 00077 They can be "1-vital" or "2-vital". This is used by safety solvers. 00078 For details see [Mueller 1997]: 00079 Playing it safe: Recognizing secure territories in computer Go by using 00080 static rules and search. In H. Matsubara, editor, Game Programming 00081 Workshop in Japan '97, pages 80-86, Computer Shogi Association, Tokyo, 00082 Japan, 1997. 00083 00084 @todo Avoid cyclic dependency with GoBlock 00085 */ 00086 class GoRegion 00087 { 00088 public: 00089 /** Construct region on points in color */ 00090 GoRegion(const GoBoard& board, const SgPointSet& points, 00091 SgBlackWhite color); 00092 00093 /** Destructor */ 00094 ~GoRegion() 00095 { 00096 #ifndef NDEBUG 00097 ++s_free; 00098 #endif 00099 } 00100 00101 /** Clear all flags etc. to recompute region */ 00102 void ReInitialize(); 00103 00104 /** For debugging */ 00105 void CheckConsistency() const; 00106 00107 // Accessors 00108 /** The points of the region */ 00109 const SgPointSet& Points() const 00110 { 00111 return m_points; 00112 } 00113 00114 /** The dependency set, outside neighbors of m_points */ 00115 SgPointSet Dep() const 00116 { 00117 return m_points.Border(m_bd.Size()); 00118 } 00119 00120 /** liberties not adjacent to boundary stones */ 00121 SgPointSet AllInsideLibs() const; 00122 00123 /** Chains of region */ 00124 const SgVectorOf<GoChain>& Chains() const 00125 { 00126 return m_chains; 00127 } 00128 00129 /** Blocks of region */ 00130 const SgVectorOf<GoBlock>& Blocks() const 00131 { 00132 return m_blocks; 00133 } 00134 00135 /** Blocks of region. 00136 Non const version used by GoRegionBoard. 00137 */ 00138 SgVectorOf<GoBlock>& BlocksNonConst() 00139 { 00140 return m_blocks; 00141 } 00142 00143 /** Interior blocks of region: all liberties are in m_points */ 00144 SgVectorOf<GoBlock> InteriorBlocks() const; 00145 00146 /** Is block an interior block of this region? */ 00147 bool IsInteriorBlock(const GoBlock* block) const; 00148 00149 /** Is block a boundary block of this region? */ 00150 bool IsBoundaryBlock(const GoBlock* block) const 00151 { 00152 return ! IsInteriorBlock(block); 00153 } 00154 00155 /** all points of region's blocks - even those not contained in Dep() */ 00156 SgPointSet BlocksPoints() const; 00157 00158 /** area of points plus interior blocks */ 00159 SgPointSet PointsPlusInteriorBlocks() const; 00160 00161 /** color of region */ 00162 SgBlackWhite Color() const 00163 { 00164 return m_color; 00165 } 00166 00167 /** minimum number of eyes in region */ 00168 int MinEyes() const {return m_eyes.MinEyes();} 00169 00170 /** maximum number of eyes in region */ 00171 int MaxEyes() const {return m_eyes.MaxEyes();} 00172 00173 /** minimum number of potential eyes in region */ 00174 int MinPotEyes() const {return m_eyes.MinPotEyes();} 00175 00176 /** maximum number of potential eyes in region */ 00177 int MaxPotEyes() const {return m_eyes.MaxPotEyes();} 00178 00179 /** Write all information */ 00180 void Write(std::ostream& out) const; 00181 00182 /** Write short identifier */ 00183 void WriteID(std::ostream& out) const; 00184 00185 // Flags 00186 /** get value of precomputed flag */ 00187 bool GetFlag(GoRegionFlag flag) const; 00188 00189 /** compute, then get value of precomputed flag */ 00190 bool ComputeAndGetFlag(GoRegionFlag flag); 00191 00192 /** test if flag has been computed */ 00193 bool ComputedFlag(GoRegionFlag flag) const; 00194 00195 /** compute the flag, set it to true or false */ 00196 void ComputeFlag(GoRegionFlag flag); 00197 00198 /** Only blocks are incrementally updated, others must be reset after 00199 move 00200 */ 00201 void ResetNonBlockFlags() 00202 { m_computedFlags.reset(); // clear all other flags. 00203 m_computedFlags.set(GO_REGION_COMPUTED_BLOCKS); 00204 m_eyes.Clear(); 00205 Invalidate(); 00206 } 00207 00208 /** is region data valid? */ 00209 bool IsValid() const {return m_flags.test(GO_REGION_VALID);} 00210 00211 /** reset valid flag, region data not current anymore */ 00212 void Invalidate() {m_flags.reset(GO_REGION_VALID);} 00213 00214 /** Computes GO_REGION_SMALL, GO_REGION_CORRIDOR, 00215 GO_REGION_SINGLE_BLOCK_BOUNDARY, 00216 GO_REGION_STATIC_1VC, GO_REGION_STATIC_2V 00217 */ 00218 void ComputeBasicFlags(); 00219 00220 /** compute flag */ 00221 void DoComputeFlag(GoRegionFlag flag); 00222 00223 /** mark that flag is computed now */ 00224 void SetComputedFlag(GoRegionFlag flag) {m_computedFlags.set(flag);} 00225 00226 /** set flag to value */ 00227 void SetFlag(GoRegionFlag flag, bool value) 00228 { m_computedFlags.set(flag); 00229 m_flags.set(flag, value); 00230 } 00231 00232 /** store search depth used in ExVital1Task for 1vc search */ 00233 void Set1VCDepth(int depth) {m_1vcDepth = depth;} 00234 00235 // Queries 00236 /** does region have two free (unused) liberties to connect chains? */ 00237 bool Has2ConnForChains(const GoChain* c1, const GoChain* c2) const; 00238 00239 /** call Has2ConnForBlocks for region with exactly two chains*/ 00240 bool Has2Conn() const; 00241 00242 /** is this healthy for a block in list? */ 00243 bool HealthyForSomeBlock(const SgVectorOf<GoBlock>& blocks) const; 00244 00245 /** Is region completely surrounded by blocks? */ 00246 bool IsSurrounded(const SgVectorOf<GoBlock>& blocks) const; 00247 00248 /** did ExVital1Task have at least this search depth? */ 00249 bool ComputedVitalForDepth(int depth) const; 00250 00251 /** is any adjacent block safe? */ 00252 bool SomeBlockIsSafe() const; 00253 00254 /** are all adjacent block safe? */ 00255 bool AllBlockIsSafe() const; 00256 00257 /** Is block of anchor adjacent to region? */ 00258 bool AdjacentToBlock(SgPoint anchor) const; 00259 00260 /** Is one of blocks given by anchors adjacent to region? */ 00261 bool AdjacentToSomeBlock(const SgVector<SgPoint>& anchors) const; 00262 00263 /** For flat region: cuts not occupied is enough to make region 1-vital */ 00264 bool Safe2Cuts(const GoBoard& board) const; 00265 00266 /** Test if all empty points are liberties of blocks 00267 @todo does not test only for boundary block libs, but all libs. 00268 */ 00269 bool AllEmptyAreLibs() const; 00270 00271 /** Test if points of region are 2-vital for color. 00272 Condition 1: all empty points are in liberties of some boundary block 00273 Condition 2: two intersection points (@see Has2IPs) 00274 refined version: points need not be liberty, if next to both IP. 00275 Example: bent-5 in the corner, as in Berlekamp/Wolfe C.11 top left 00276 corner 00277 */ 00278 bool Has2SureLibs(SgMiaiStrategy* miaiStrategy) const; 00279 00280 /** block liberties in this region */ 00281 void InsideLibs(const GoBlock* b, SgVector<SgPoint>* libs) const; 00282 00283 /** Is a liberty of b in region? */ 00284 bool HasBlockLibs(const GoBlock* b) const; 00285 00286 /** Can a block get n libs inside region? */ 00287 bool HasLibsForBlock(const GoBlock* b, int n) const; 00288 00289 /** Does each adjacent block have a liberty in region? */ 00290 bool HasLibForAllBlocks() const; 00291 00292 /** Does each block have n liberties in region? */ 00293 bool HasLibsForAllBlocks(int n) const; 00294 00295 /** Can we find two connection paths to each interior empty point? 00296 This implements only the simple, non-recursive case. 00297 See Find2ConnForAllInterior() for a more general solution. 00298 */ 00299 bool Find2ConnForAll() const; 00300 00301 /** find 2-connection paths for all interior empty points, using recursive 00302 extension. Erik v.d.Werf's recursive extension to Find2ConnForAll. 00303 */ 00304 bool Find2ConnForAllInterior(SgMiaiStrategy* miaiStrategy, 00305 SgVector<SgPoint>& usedLibs) const; 00306 00307 /** An IP divides a region into two eyes AND connects all surrounding 00308 blocks, this one defines that ip has to be adjancent to all interior 00309 points. 00310 */ 00311 bool Has2IPs(const SgVector<SgPoint>& interiorEmpty, SgMiaiPair* ips) 00312 const; 00313 00314 /** whether there are 2 intersection points, doesn't have to be adjacent 00315 to all interior points. 00316 */ 00317 bool Has2IntersectionPoints(const SgVector<SgPoint>& usedLibs) const; 00318 00319 /** Get all intersections points inside region */ 00320 void GetIPs(SgVector<SgPoint>* ips) const; 00321 00322 /** Get all SgMiaiPairs that can divide the region. A dividing 00323 SgMiaiPair has two adjacent points that are libs from the 00324 same boundary block, and they are split points for region. 00325 This is a loose condition for SgMiaiPairs. 00326 */ 00327 void GetDivideMiaiPairs(SgVector<SgMiaiPair>& pairs) const; 00328 00329 /** Compute joint liberties of all m_blocks. Since we need at least 2 00330 joint libs, we stop computing if we find that this is impossible. 00331 */ 00332 void JointLibs(SgVector<SgPoint>* libs) const; 00333 00334 /** See ExEye::IsCorridor() */ 00335 bool IsCorridor() const; 00336 00337 /** update region if affected: old was merged into newChain */ 00338 bool ReplaceChain(const GoChain* old, const GoChain* newChain); 00339 00340 /** chains are mergable if two connections are found */ 00341 bool Find2Mergable(GoChain** c1, GoChain** c2) const; 00342 00343 /** Find two so far unused liberties for connecting c1 and c2 00344 @todo In future, must respect all other chain conditions 00345 active in this region. 00346 */ 00347 void Find2FreeLibs(const GoChain* c1, const GoChain* c2, 00348 SgPoint* lib1, SgPoint* lib2) const; 00349 00350 /** Get blocks of color in area */ 00351 void FindBlocks(const GoRegionBoard& ra); 00352 00353 /** Set blocks for this region from pre-found blocks list */ 00354 void SetBlocks(const SgVectorOf<GoBlock>& blocks); 00355 00356 /** Get chain of color in area. 00357 @todo There must be faster ways to do this. 00358 */ 00359 void FindChains(const GoRegionBoard& ra); 00360 00361 /** Set safe flag for region */ 00362 void SetToSafe() {SetFlag(GO_REGION_SAFE, true);} 00363 00364 // Execute move helpers 00365 /** For incremental update - block no longer adjacent */ 00366 void RemoveBlock(const GoBlock* b); 00367 00368 /** Stone was played: remove from m_points */ 00369 void OnAddStone(SgPoint p); 00370 00371 /** Stone removed: add to m_points */ 00372 void OnRemoveStone(SgPoint p); 00373 00374 /** class finalization */ 00375 static void Fini(); 00376 00377 private: 00378 00379 /** The board */ 00380 const GoBoard& m_bd; 00381 00382 /** Flags describing attributes of region */ 00383 GoRegionFlags m_flags; 00384 00385 /** Which flags have been computed? */ 00386 GoRegionFlags m_computedFlags; 00387 00388 /** Points of region */ 00389 SgPointSet m_points; 00390 00391 /** Color of region = color of surrounding stones */ 00392 SgBlackWhite m_color; 00393 00394 /** Blocks of m_color adjacent to region. Some may be on inside */ 00395 SgVectorOf<GoBlock> m_blocks; 00396 00397 /** Chains of region */ 00398 SgVectorOf<GoChain> m_chains; 00399 00400 /** Number of eyes in region */ 00401 GoEyeCount m_eyes; 00402 00403 /** Point to change eye status. Can be SG_NULLPOINT */ 00404 SgPoint m_vitalPoint; 00405 00406 /** search depth used in ExVital1Task for 1vc search */ 00407 int m_1vcDepth; 00408 00409 /** Miai strategy to keep region safe. */ 00410 SgMiaiStrategy m_miaiStrategy; 00411 00412 /** A simple test if all cuts between blocks are protected. 00413 Works only for corridors (@see IsCorridor): 00414 If corridor, then opp. can get at most 2 libs (Opp2L). 00415 If Opp2L && cut point empty then opp cut move is self atari, 00416 therefore cut is protected (and remains protected if opponent 00417 is captured and plays inside again) 00418 */ 00419 bool ProtectedCuts(const GoBoard& board) const; 00420 00421 /** Static test if region is 1-vital. 00422 @todo explain the algorithm. 00423 */ 00424 bool ComputeIs1Vital() const; 00425 00426 /** Static test if region is 1-vital and all blocks are connected. */ 00427 bool StaticIs1VitalAndConnected() const; 00428 00429 /** Compute eye status for some standard small regions */ 00430 void ComputeNakade(); 00431 00432 /** Compute eye space for region having only 1 block */ 00433 void ComputeSingleBlockEyeSpace(); 00434 00435 /** Compute eye space for region having more than 1 block */ 00436 void ComputeMultipleBlockEyeSpace(); 00437 00438 /** Compute eye space for region */ 00439 void ComputeEyeSpace(); 00440 00441 /** compute at most maxNu empty points in the interior 00442 @todo does not test only for boundary block libs, but all libs. 00443 */ 00444 void InteriorEmpty(SgVector<SgPoint>* interiorEmpty, int maxNu) const; 00445 00446 #ifndef NDEBUG 00447 /** debugging bookkeeping. */ 00448 static int s_alloc, s_free; 00449 #endif 00450 }; 00451 00452 std::ostream& operator<<(std::ostream& stream, const GoRegion& r); 00453 00454 //---------------------------------------------------------------------------- 00455 00456 #endif // GO_REGION_H 00457