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