00001 //---------------------------------------------------------------------------- 00002 /** @file GoUctPatterns.h */ 00003 //---------------------------------------------------------------------------- 00004 00005 #ifndef GOUCT_PATTERNS_H 00006 #define GOUCT_PATTERNS_H 00007 00008 #include "GoBoard.h" 00009 #include "GoBoardUtil.h" 00010 #include "SgBoardColor.h" 00011 #include "SgBWArray.h" 00012 #include "SgPoint.h" 00013 00014 //---------------------------------------------------------------------------- 00015 00016 /** Some hard-coded pattern matching routines to match patterns used by MoGo. 00017 See <a href="http://hal.inria.fr/docs/00/11/72/66/PDF/MoGoReport.pdf"> 00018 Modification of UCT with Patterns in Monte-Carlo Go</a>. 00019 00020 The move is always in the center of the pattern or at the middle edge 00021 point (lower line) for edge patterns. The patterns are matched for both 00022 colors, unless specified otherwise. Notation: 00023 @verbatim 00024 O White x = Black or Empty 00025 X = Black o = White or Empty 00026 . = Empty B = Black to Play 00027 ? = Don't care W = White to Play 00028 @endverbatim 00029 00030 Patterns for Hane. <br> 00031 True is returned if any pattern is matched. 00032 @verbatim 00033 X O X X O . X O ? X O O 00034 . . . . . . X . . . . . 00035 ? ? ? ? . ? ? . ? ? . ? B 00036 @endverbatim 00037 00038 Patterns for Cut1. <br> 00039 True is returned if the first pattern is matched, but not the next two. 00040 @verbatim 00041 X O ? X O ? X O ? 00042 O . ? O . O O . . 00043 ? ? ? ? . ? ? O ? 00044 @endverbatim 00045 00046 Pattern for Cut2. 00047 @verbatim 00048 ? X ? 00049 O . O 00050 x x x 00051 @endverbatim 00052 00053 Pattern for Edge. <br> 00054 True is returned if any pattern is matched. 00055 @verbatim 00056 X . ? ? X ? ? X O ? X O ? X O 00057 O . ? o . O ? . ? B ? . o W O . X W 00058 @endverbatim */ 00059 template<class BOARD> 00060 class GoUctPatterns 00061 { 00062 public: 00063 GoUctPatterns(const BOARD& bd); 00064 00065 /** Match any of the patterns. */ 00066 bool MatchAny(SgPoint p) const; 00067 00068 private: 00069 /** 3^5 = size of edge pattern table */ 00070 static const int GOUCT_POWER3_5 = 3 * 3 * 3 * 3 * 3; 00071 00072 /** 3^8 = size of center pattern table. */ 00073 static const int GOUCT_POWER3_8 = 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3; 00074 00075 /** See m_edgeTable. */ 00076 typedef SgArray<bool, GOUCT_POWER3_5> GoUctEdgePatternTable; 00077 00078 /** See m_table. */ 00079 typedef SgArray<bool, GOUCT_POWER3_8> GoUctPatternTable; 00080 00081 const BOARD& m_bd; 00082 00083 /** lookup table for 8-neighborhood of a move candidate */ 00084 SgBWArray<GoUctPatternTable> m_table; 00085 00086 /** lookup table on the edge of board */ 00087 SgBWArray<GoUctEdgePatternTable> m_edgeTable; 00088 00089 static bool CheckCut1(const GoBoard& bd, SgPoint p, SgBlackWhite c, 00090 int cDir, int otherDir); 00091 00092 static bool CheckCut2(const GoBoard& bd, SgPoint p, const SgBlackWhite c, 00093 int cDir, int otherDir); 00094 00095 static bool CheckHane1(const GoBoard& bd, SgPoint p, SgBlackWhite c, 00096 SgBlackWhite opp, int cDir, int otherDir); 00097 00098 static int CodeOf8Neighbors(const BOARD& bd, SgPoint p); 00099 00100 static int CodeOfEdgeNeighbors(const BOARD& bd, SgPoint p); 00101 00102 static int EdgeDirection(GoBoard& bd, SgPoint p, int index); 00103 00104 static int EBWCodeOfPoint(const BOARD& bd, SgPoint p); 00105 00106 static int FindDir(const GoBoard& bd, SgPoint p, SgBlackWhite c); 00107 00108 static void InitCenterPatternTable(SgBWArray<GoUctPatternTable>& table); 00109 00110 static void InitEdgePatternTable(SgBWArray<GoUctEdgePatternTable>& 00111 edgeTable); 00112 00113 static bool MatchCut(const GoBoard& bd, SgPoint p); 00114 00115 static bool MatchEdge(const GoBoard& bd, SgPoint p, const int nuBlack, 00116 const int nuWhite); 00117 00118 static bool MatchHane(const GoBoard& bd, SgPoint p, const int nuBlack, 00119 const int nuWhite); 00120 00121 static bool MatchAnyPattern(const GoBoard& bd, SgPoint p); 00122 00123 static int OtherDir(int dir); 00124 00125 static int SetupCodedEdgePosition(GoBoard& bd, int code); 00126 00127 static int SetupCodedPosition(GoBoard& bd, int code); 00128 00129 /** Match any of the center patterns. */ 00130 bool MatchAnyCenter(SgPoint p) const; 00131 00132 /** Match any of the edge patterns. */ 00133 bool MatchAnyEdge(SgPoint p) const; 00134 }; 00135 00136 template<class BOARD> 00137 GoUctPatterns<BOARD>::GoUctPatterns(const BOARD& bd) 00138 : m_bd(bd) 00139 { 00140 InitCenterPatternTable(m_table); 00141 InitEdgePatternTable(m_edgeTable); 00142 } 00143 00144 template<class BOARD> 00145 bool GoUctPatterns<BOARD>::CheckHane1(const GoBoard& bd, SgPoint p, 00146 SgBlackWhite c, SgBlackWhite opp, 00147 int cDir, int otherDir) 00148 { 00149 return bd.IsColor(p + cDir, c) 00150 && bd.IsColor(p + cDir + otherDir, opp) 00151 && bd.IsColor(p + cDir - otherDir, opp) 00152 && bd.IsEmpty(p + otherDir) 00153 && bd.IsEmpty(p - otherDir) 00154 ; 00155 } 00156 00157 template<class BOARD> 00158 bool GoUctPatterns<BOARD>::CheckCut1(const GoBoard& bd, SgPoint p, 00159 SgBlackWhite c, int cDir, int otherDir) 00160 { 00161 SG_ASSERT_BW(c); 00162 return bd.IsColor(p + otherDir, c) 00163 && bd.IsColor(p + cDir + otherDir, SgOppBW(c)); 00164 } 00165 00166 template<class BOARD> 00167 bool GoUctPatterns<BOARD>::CheckCut2(const GoBoard& bd, SgPoint p, 00168 const SgBlackWhite c, int cDir, 00169 int otherDir) 00170 { 00171 SG_ASSERT_BW(c); 00172 SG_ASSERT(bd.IsColor(p + cDir, c)); 00173 const SgBlackWhite opp = SgOppBW(c); 00174 return bd.IsColor(p - cDir, c) 00175 && ( ( bd.IsColor(p + otherDir, opp) 00176 && ! bd.IsColor(p - otherDir + cDir, c) 00177 && ! bd.IsColor(p - otherDir - cDir, c) 00178 ) 00179 || 00180 ( bd.IsColor(p - otherDir, opp) 00181 && ! bd.IsColor(p + otherDir + cDir, c) 00182 && ! bd.IsColor(p + otherDir - cDir, c) 00183 ) 00184 ); 00185 } 00186 00187 template<class BOARD> 00188 inline int GoUctPatterns<BOARD>::CodeOf8Neighbors(const BOARD& bd, SgPoint p) 00189 { 00190 SG_ASSERT(bd.Line(p) > 1); 00191 int code = (((((( EBWCodeOfPoint(bd, p - SG_NS - SG_WE) * 3 00192 + EBWCodeOfPoint(bd, p - SG_NS)) * 3 00193 + EBWCodeOfPoint(bd, p - SG_NS + SG_WE)) * 3 00194 + EBWCodeOfPoint(bd, p - SG_WE)) * 3 00195 + EBWCodeOfPoint(bd, p + SG_WE)) * 3 00196 + EBWCodeOfPoint(bd, p + SG_NS - SG_WE)) * 3 00197 + EBWCodeOfPoint(bd, p + SG_NS)) * 3 00198 + EBWCodeOfPoint(bd, p + SG_NS + SG_WE); 00199 SG_ASSERT(code >= 0); 00200 SG_ASSERT(code < GOUCT_POWER3_8); 00201 return code; 00202 } 00203 00204 template<class BOARD> 00205 inline int GoUctPatterns<BOARD>::CodeOfEdgeNeighbors(const BOARD& bd, 00206 SgPoint p) 00207 { 00208 SG_ASSERT(bd.Line(p) == 1); 00209 SG_ASSERT(bd.Pos(p) > 1); 00210 const int up = bd.Up(p); 00211 const int other = OtherDir(up); 00212 int code = (((EBWCodeOfPoint(bd, p + other) * 3 00213 + EBWCodeOfPoint(bd, p + up + other)) * 3 00214 + EBWCodeOfPoint(bd, p + up)) * 3 00215 + EBWCodeOfPoint(bd, p + up - other)) * 3 00216 + EBWCodeOfPoint(bd, p - other); 00217 SG_ASSERT(code >= 0); 00218 SG_ASSERT(code < GOUCT_POWER3_5); 00219 return code; 00220 } 00221 00222 template<class BOARD> 00223 inline int GoUctPatterns<BOARD>::EBWCodeOfPoint(const BOARD& bd, SgPoint p) 00224 { 00225 SG_ASSERT(bd.IsValidPoint(p)); 00226 BOOST_STATIC_ASSERT(SG_BLACK == 0); 00227 BOOST_STATIC_ASSERT(SG_WHITE == 1); 00228 BOOST_STATIC_ASSERT(SG_EMPTY == 2); 00229 return bd.GetColor(p); 00230 } 00231 00232 template<class BOARD> 00233 int GoUctPatterns<BOARD>::EdgeDirection(GoBoard& bd, SgPoint p, int index) 00234 { 00235 const int up = bd.Up(p); 00236 switch (index) 00237 { 00238 case 0: 00239 return OtherDir(up); 00240 case 1: 00241 return up + OtherDir(up); 00242 case 2: 00243 return up; 00244 case 3: 00245 return up - OtherDir(up); 00246 default: 00247 SG_ASSERT(index == 4); 00248 return - OtherDir(up); 00249 } 00250 } 00251 00252 /** Find direction of a neighboring stone in color c */ 00253 template<class BOARD> 00254 int GoUctPatterns<BOARD>::FindDir(const GoBoard& bd, SgPoint p, 00255 SgBlackWhite c) 00256 { 00257 if (bd.IsColor(p + SG_NS, c)) 00258 return SG_NS; 00259 if (bd.IsColor(p - SG_NS, c)) 00260 return -SG_NS; 00261 if (bd.IsColor(p + SG_WE, c)) 00262 return SG_WE; 00263 SG_ASSERT(bd.IsColor(p - SG_WE, c)); 00264 return -SG_WE; 00265 } 00266 00267 template<class BOARD> 00268 void GoUctPatterns<BOARD>::InitEdgePatternTable( 00269 SgBWArray<GoUctEdgePatternTable>& edgeTable) 00270 { 00271 GoBoard bd(5); 00272 const SgPoint p = SgPointUtil::Pt(1, 3); 00273 for (int i = 0; i < GOUCT_POWER3_5; ++i) 00274 { 00275 int count = SetupCodedEdgePosition(bd, i); 00276 for(SgBWIterator it; it; ++it) 00277 { 00278 bd.SetToPlay(*it); 00279 edgeTable[*it][i] = MatchAnyPattern(bd, p) ? 1 : 0; 00280 } 00281 while (count-- > 0) 00282 bd.Undo(); 00283 } 00284 } 00285 00286 template<class BOARD> 00287 void GoUctPatterns<BOARD>::InitCenterPatternTable( 00288 SgBWArray<GoUctPatternTable>& table) 00289 { 00290 GoBoard bd(5); 00291 const SgPoint p = SgPointUtil::Pt(3, 3); 00292 for (int i = 0; i < GOUCT_POWER3_8; ++i) 00293 { 00294 int count = SetupCodedPosition(bd, i); 00295 for(SgBWIterator it; it; ++it) 00296 { 00297 bd.SetToPlay(*it); 00298 table[*it][i] = MatchAnyPattern(bd, p) ? 1 : 0; 00299 } 00300 while (count-- > 0) 00301 bd.Undo(); 00302 } 00303 } 00304 00305 template<class BOARD> 00306 bool GoUctPatterns<BOARD>::MatchCut(const GoBoard& bd, SgPoint p) 00307 { 00308 if (bd.Num8EmptyNeighbors(p) > 6) 00309 return false; 00310 00311 const int nuEmpty = bd.NumEmptyNeighbors(p); 00312 //cut1 00313 const SgEmptyBlackWhite c1 = bd.GetColor(p + SG_NS); 00314 if ( c1 != SG_EMPTY 00315 && bd.NumNeighbors(p, c1) >= 2 00316 && ! (bd.NumNeighbors(p, c1) == 3 && nuEmpty == 1) 00317 && ( CheckCut1(bd, p, c1, SG_NS, SG_WE) 00318 || CheckCut1(bd, p, c1, SG_NS, -SG_WE) 00319 ) 00320 ) 00321 return true; 00322 const SgEmptyBlackWhite c2 = bd.GetColor(p - SG_NS); 00323 if ( c2 != SG_EMPTY 00324 && bd.NumNeighbors(p, c2) >= 2 00325 && ! (bd.NumNeighbors(p, c2) == 3 && nuEmpty == 1) 00326 && ( CheckCut1(bd, p, c2, -SG_NS, SG_WE) 00327 || CheckCut1(bd, p, c2, -SG_NS, -SG_WE) 00328 ) 00329 ) 00330 return true; 00331 //cut2 00332 if ( c1 != SG_EMPTY 00333 && bd.NumNeighbors(p, c1) == 2 00334 && bd.NumNeighbors(p, SgOppBW(c1)) > 0 00335 && bd.NumDiagonals(p, c1) <= 2 00336 && CheckCut2(bd, p, c1, SG_NS, SG_WE) 00337 ) 00338 return true; 00339 const SgEmptyBlackWhite c3 = bd.GetColor(p + SG_WE); 00340 if ( c3 != SG_EMPTY 00341 && bd.NumNeighbors(p, c3) == 2 00342 && bd.NumNeighbors(p, SgOppBW(c3)) > 0 00343 && bd.NumDiagonals(p, c3) <= 2 00344 && CheckCut2(bd, p, c3, SG_WE, SG_NS) 00345 ) 00346 return true; 00347 return false; 00348 } 00349 00350 template<class BOARD> 00351 bool GoUctPatterns<BOARD>::MatchEdge(const GoBoard& bd, SgPoint p, 00352 const int nuBlack, const int nuWhite) 00353 { 00354 const int up = bd.Up(p); 00355 const int side = OtherDir(up); 00356 const int nuEmpty = bd.NumEmptyNeighbors(p); 00357 const SgEmptyBlackWhite upColor = bd.GetColor(p + up); 00358 // edge1 00359 if ( nuEmpty > 0 00360 && (nuBlack > 0 || nuWhite > 0) 00361 && upColor == SG_EMPTY 00362 ) 00363 { 00364 const SgEmptyBlackWhite c1 = bd.GetColor(p + side); 00365 if (c1 != SG_EMPTY && bd.GetColor(p + side + up) == SgOppBW(c1)) 00366 return true; 00367 const SgEmptyBlackWhite c2 = bd.GetColor(p - side); 00368 if (c2 != SG_EMPTY && bd.GetColor(p - side + up) == SgOppBW(c2)) 00369 return true; 00370 } 00371 00372 // edge2 00373 if ( upColor != SG_EMPTY 00374 && ( (upColor == SG_BLACK && nuBlack == 1 && nuWhite > 0) 00375 || (upColor == SG_WHITE && nuWhite == 1 && nuBlack > 0) 00376 ) 00377 ) 00378 return true; 00379 00380 const SgBlackWhite toPlay = bd.ToPlay(); 00381 // edge3 00382 if ( upColor == toPlay 00383 && bd.NumDiagonals(p, SgOppBW(upColor)) > 0 00384 ) 00385 return true; 00386 00387 // edge4 00388 if ( upColor == SgOppBW(toPlay) 00389 && bd.NumNeighbors(p, upColor) <= 2 00390 && bd.NumDiagonals(p, toPlay) > 0 00391 ) 00392 { 00393 if ( bd.GetColor(p + side + up) == toPlay 00394 && bd.GetColor(p + side) != upColor 00395 ) 00396 return true; 00397 if ( bd.GetColor(p - side + up) == toPlay 00398 && bd.GetColor(p - side) != upColor 00399 ) 00400 return true; 00401 } 00402 // edge5 00403 if ( upColor == SgOppBW(toPlay) 00404 && bd.NumNeighbors(p, upColor) == 2 00405 && bd.NumNeighbors(p, toPlay) == 1 00406 ) 00407 { 00408 if ( bd.GetColor(p + side + up) == toPlay 00409 && bd.GetColor(p + side) == upColor 00410 ) 00411 return true; 00412 if ( bd.GetColor(p - side + up) == toPlay 00413 && bd.GetColor(p - side) == upColor 00414 ) 00415 return true; 00416 } 00417 return false; 00418 } 00419 00420 template<class BOARD> 00421 bool GoUctPatterns<BOARD>::MatchHane(const GoBoard& bd, SgPoint p, 00422 const int nuBlack, const int nuWhite) 00423 { 00424 const int nuEmpty = bd.NumEmptyNeighbors(p); 00425 if (nuEmpty < 2 || nuEmpty > 3) 00426 return false; 00427 if ( (nuBlack < 1 || nuBlack > 2) 00428 && (nuWhite < 1 || nuWhite > 2) 00429 ) 00430 return false; 00431 if (nuEmpty == 2) // hane3 pattern 00432 { 00433 if (nuBlack == 1 && nuWhite == 1) 00434 { 00435 const int dirB = FindDir(bd, p, SG_BLACK); 00436 const int dirW = FindDir(bd, p, SG_WHITE); 00437 if (! bd.IsEmpty(p + dirB + dirW)) 00438 return true; 00439 } 00440 } 00441 else if (nuEmpty == 3) // hane2 or hane4 00442 { 00443 SG_ASSERT(nuBlack + nuWhite == 1); 00444 const SgBlackWhite col = (nuBlack == 1) ? SG_BLACK : SG_WHITE; 00445 const SgBlackWhite opp = SgOppBW(col); 00446 const int dir = FindDir(bd, p, col); 00447 const int otherDir = OtherDir(dir); 00448 if ( bd.IsEmpty(p + dir + otherDir) 00449 && bd.IsColor(p + dir - otherDir, opp) 00450 ) 00451 return true; // hane2 00452 if ( bd.IsEmpty(p + dir - otherDir) 00453 && bd.IsColor(p + dir + otherDir, opp) 00454 ) 00455 return true; // hane2 00456 if (bd.ToPlay() == opp) 00457 { 00458 const SgEmptyBlackWhite c1 = bd.GetColor(p + dir + otherDir); 00459 if (c1 != SG_EMPTY) 00460 { 00461 const SgEmptyBlackWhite c2 = 00462 bd.GetColor(p + dir - otherDir); 00463 if (SgOppBW(c1) == c2) 00464 return true; // hane4 00465 } 00466 } 00467 } 00468 00469 // hane1 pattern 00470 const int nuBlackDiag = bd.NumDiagonals(p, SG_BLACK); 00471 if ( nuBlackDiag >= 2 00472 && nuWhite > 0 00473 && ( CheckHane1(bd, p, SG_WHITE, SG_BLACK, SG_NS, SG_WE) 00474 || CheckHane1(bd, p, SG_WHITE, SG_BLACK, -SG_NS, SG_WE) 00475 || CheckHane1(bd, p, SG_WHITE, SG_BLACK, SG_WE, SG_NS) 00476 || CheckHane1(bd, p, SG_WHITE, SG_BLACK, -SG_WE, SG_NS) 00477 ) 00478 ) 00479 return true; 00480 const int nuWhiteDiag = bd.NumDiagonals(p, SG_WHITE); 00481 if ( nuWhiteDiag >= 2 00482 && nuBlack > 0 00483 && ( CheckHane1(bd, p, SG_BLACK, SG_WHITE, SG_NS, SG_WE) 00484 || CheckHane1(bd, p, SG_BLACK, SG_WHITE, -SG_NS, SG_WE) 00485 || CheckHane1(bd, p, SG_BLACK, SG_WHITE, SG_WE, SG_NS) 00486 || CheckHane1(bd, p, SG_BLACK, SG_WHITE, -SG_WE, SG_NS) 00487 ) 00488 ) 00489 return true; 00490 return false; 00491 } 00492 00493 template<class BOARD> 00494 inline bool GoUctPatterns<BOARD>::MatchAnyCenter(SgPoint p) const 00495 { 00496 return m_table[m_bd.ToPlay()][CodeOf8Neighbors(m_bd, p)]; 00497 } 00498 00499 template<class BOARD> 00500 inline bool GoUctPatterns<BOARD>::MatchAnyEdge(SgPoint p) const 00501 { 00502 return m_edgeTable[m_bd.ToPlay()][CodeOfEdgeNeighbors(m_bd, p)]; 00503 } 00504 00505 template<class BOARD> 00506 inline bool GoUctPatterns<BOARD>::MatchAny(SgPoint p) const 00507 { 00508 // Quick refutation using the incremental neighbor counts of the board: 00509 // all patterns have at least one adjacent stone 00510 if ( m_bd.NumNeighbors(p, SG_BLACK) == 0 00511 && m_bd.NumNeighbors(p, SG_WHITE) == 0 00512 ) 00513 return false; 00514 if (m_bd.Line(p) > 1) 00515 return MatchAnyCenter(p); 00516 else if (m_bd.Pos(p) > 1) 00517 return MatchAnyEdge(p); 00518 else 00519 return false; 00520 } 00521 00522 /** Procedural matching function - used to initialize the table. */ 00523 template<class BOARD> 00524 bool GoUctPatterns<BOARD>::MatchAnyPattern(const GoBoard& bd, SgPoint p) 00525 { 00526 SG_ASSERT(bd.IsEmpty(p)); 00527 const int nuBlack = bd.NumNeighbors(p, SG_BLACK); 00528 const int nuWhite = bd.NumNeighbors(p, SG_WHITE); 00529 00530 // Quick refutation using the incremental neighbor counts of the board 00531 // All patterns have at least one adjacent stone 00532 if (nuBlack == 0 && nuWhite == 0) 00533 return false; 00534 00535 // Filter edge moves on (1,1) points in corners 00536 if (bd.Pos(p) == 1) 00537 return false; 00538 if (bd.Line(p) == 1) 00539 return MatchEdge(bd, p, nuBlack, nuWhite); 00540 else // Center 00541 return MatchHane(bd, p, nuBlack, nuWhite) 00542 || MatchCut(bd, p); 00543 } 00544 00545 template<class BOARD> 00546 inline int GoUctPatterns<BOARD>::OtherDir(int dir) 00547 { 00548 if (dir == SG_NS || dir == -SG_NS) 00549 return SG_WE; 00550 return SG_NS; 00551 } 00552 00553 template<class BOARD> 00554 int GoUctPatterns<BOARD>::SetupCodedEdgePosition(GoBoard& bd, int code) 00555 { 00556 const SgPoint p = SgPointUtil::Pt(1, 3); 00557 int count = 0; 00558 for (int i = 4; i >= 0; --i) // decoding gives points in reverse order 00559 { 00560 const SgPoint nb = p + EdgeDirection(bd, p, i); 00561 int c = code % 3; 00562 code /= 3; 00563 if (c != SG_EMPTY) 00564 { 00565 ++count; 00566 bd.Play(nb, c); 00567 } 00568 } 00569 return count; 00570 } 00571 00572 template<class BOARD> 00573 int GoUctPatterns<BOARD>::SetupCodedPosition(GoBoard& bd, int code) 00574 { 00575 const SgPoint p = SgPointUtil::Pt(3, 3); 00576 int count = 0; 00577 for (int i = 7; i >= 0; --i) // decoding gives points in reverse order 00578 { 00579 const SgPoint nb = p + SgNb8Iterator::Direction(i); 00580 int c = code % 3; 00581 code /= 3; 00582 if (c != SG_EMPTY) 00583 { 00584 ++count; 00585 bd.Play(nb, c); 00586 } 00587 } 00588 return count; 00589 } 00590 00591 //---------------------------------------------------------------------------- 00592 00593 #endif // GOUCT_PATTERNS_H