00001 //---------------------------------------------------------------------------- 00002 /** @file GoUctPlayoutPolicy.h 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #ifndef GOUCT_PLAYOUTPOLICY_H 00007 #define GOUCT_PLAYOUTPOLICY_H 00008 00009 #include <iostream> 00010 #include <boost/array.hpp> 00011 #include "GoBoardUtil.h" 00012 #include "GoEyeUtil.h" 00013 #include "GoUctPatterns.h" 00014 #include "GoUctPureRandomGenerator.h" 00015 00016 //---------------------------------------------------------------------------- 00017 00018 /** Parameters for GoUctPlayoutPolicy. */ 00019 class GoUctPlayoutPolicyParam 00020 { 00021 public: 00022 /** Enable collection of statistics. 00023 Has a negative impact on performance. Default is false. 00024 */ 00025 bool m_statisticsEnabled; 00026 00027 /** Use Nakade heuristic. 00028 See section 6.2 of: Chatriot, Gelly, Hoock, Perez, Rimmel, Teytaud: 00029 <a href="http://www.lri.fr/~teytaud/eg.pdf"> 00030 Combining expert, offline, transient and online learning in 00031 Monte-Carlo exploration</a> 00032 */ 00033 bool m_useNakadeHeuristic; 00034 00035 /** See GoUctPureRandomGenerator::GenerateFillboardMove. 00036 Default is 0 00037 */ 00038 int m_fillboardTries; 00039 00040 GoUctPlayoutPolicyParam(); 00041 }; 00042 00043 //---------------------------------------------------------------------------- 00044 00045 /** Move types used in GoUctPlayoutPolicy. */ 00046 enum GoUctPlayoutPolicyType 00047 { 00048 GOUCT_FILLBOARD, 00049 00050 GOUCT_NAKADE, 00051 00052 GOUCT_ATARI_CAPTURE, 00053 00054 GOUCT_ATARI_DEFEND, 00055 00056 GOUCT_LOWLIB, 00057 00058 GOUCT_PATTERN, 00059 00060 GOUCT_CAPTURE, 00061 00062 GOUCT_RANDOM, 00063 00064 GOUCT_SELFATARI_CORRECTION, 00065 00066 GOUCT_CLUMP_CORRECTION, 00067 00068 GOUCT_PASS, 00069 00070 _GOUCT_NU_DEFAULT_PLAYOUT_TYPE 00071 }; 00072 00073 const char* GoUctPlayoutPolicyTypeStr(GoUctPlayoutPolicyType type); 00074 00075 //---------------------------------------------------------------------------- 00076 00077 /** Statistics collected by GoUctPlayoutPolicy */ 00078 struct GoUctPlayoutPolicyStat 00079 { 00080 /** Number of moves generated. */ 00081 std::size_t m_nuMoves; 00082 00083 /** Length of sequences of consecutive non-pure-random moves. */ 00084 SgUctStatistics m_nonRandLen; 00085 00086 /** Length of list of equivalent best moves. 00087 Does not include the length of the move list for pure random moves. 00088 */ 00089 SgUctStatistics m_moveListLen; 00090 00091 /** Number of moves of a certain type. */ 00092 boost::array<std::size_t,_GOUCT_NU_DEFAULT_PLAYOUT_TYPE> m_nuMoveType; 00093 00094 void Clear(); 00095 00096 void Write(std::ostream& out) const; 00097 }; 00098 00099 //---------------------------------------------------------------------------- 00100 00101 /** Default playout policy for usage in GoUctGlobalSearch. 00102 Parametrized by the board class to make it usable with both GoBoard 00103 and GoUctBoard. 00104 If all heuristics are disabled, the policy plays purely random moves. 00105 The order and types of the heuristics are inspired by the first 00106 technical report about the MoGo program. 00107 Instances of this class must be thread-safe during a search. 00108 */ 00109 template<class BOARD> 00110 class GoUctPlayoutPolicy 00111 { 00112 public: 00113 /** Constructor. 00114 @param bd 00115 @param param The parameters. The policy stores a reference to @c param 00116 to allow changing the parameters of a group of playout policies later. 00117 Therefore the lifetime of @c param must exceed the lifetime of the 00118 policy. 00119 */ 00120 GoUctPlayoutPolicy(const BOARD& bd, const GoUctPlayoutPolicyParam& param); 00121 00122 00123 /** @name Functions needed by all playout policies. */ 00124 // @{ 00125 00126 /** Generate a move 00127 Generates a random move in the following order: 00128 -# Atari heuristic (if enabled) 00129 -# Proximity heuristic (if enabled) (using patterns if enabled) 00130 -# Capture heuristic (if enabled) 00131 -# Purely random 00132 */ 00133 SgPoint GenerateMove(); 00134 00135 void EndPlayout(); 00136 00137 void StartPlayout(); 00138 00139 void OnPlay(); 00140 00141 /** Return the type of the last move generated. */ 00142 GoUctPlayoutPolicyType MoveType() const; 00143 00144 // @} // @name 00145 00146 00147 /** @name Statistics */ 00148 // @{ 00149 00150 /** Return current statistics. 00151 The statistics are only collected, if enabled with 00152 EnableStatistics(). 00153 */ 00154 const GoUctPlayoutPolicyStat& Statistics() const; 00155 00156 void ClearStatistics(); 00157 00158 // @} // @name 00159 00160 00161 /** Return the list of equivalent best moves from last move generation. 00162 The played move was randomly selected from this list. 00163 */ 00164 GoPointList GetEquivalentBestMoves() const; 00165 00166 /** Make pattern matcher available for other uses. 00167 Avoids that a user of the playout policy who also wants to use the 00168 pattern matcher for other purposes needs to allocate a second 00169 matcher (Use case: prior knowledge) 00170 */ 00171 const GoUctPatterns<BOARD>& Patterns() const; 00172 00173 private: 00174 /** A function that possibly corrects a given point */ 00175 typedef bool Corrector(const BOARD&, SgPoint&); 00176 00177 /** Incrementally keeps track of blocks in atari. */ 00178 class CaptureGenerator 00179 { 00180 public: 00181 CaptureGenerator(const BOARD& bd); 00182 00183 void StartPlayout(); 00184 00185 void OnPlay(); 00186 00187 /** Generate capture moves. 00188 @param[out] moves The resulting list of capture moves. The passed 00189 in list is expected to be empty. 00190 */ 00191 void Generate(GoPointList& moves); 00192 00193 private: 00194 const BOARD& m_bd; 00195 00196 /** Anchor stones of blocks that need to be checked for atari. */ 00197 std::vector<SgPoint> m_candidates; 00198 }; 00199 00200 /** Use patterns around last own move, too */ 00201 static const bool SECOND_LAST_MOVE_PATTERNS = true; 00202 00203 /** Shift move to neighbor if it would make an ugly clump. 00204 See GoUctUtil::DoClumpCorrection 00205 */ 00206 static const bool USE_CLUMP_CORRECTION = false; 00207 00208 static const bool DEBUG_CORRECT_MOVE = false; 00209 00210 const BOARD& m_bd; 00211 00212 const GoUctPlayoutPolicyParam& m_param; 00213 00214 GoUctPatterns<BOARD> m_patterns; 00215 00216 /** m_moves have already been checked, skip GeneratePoint test. */ 00217 bool m_checked; 00218 00219 /** Type of the last generated move. */ 00220 GoUctPlayoutPolicyType m_moveType; 00221 00222 /** See GoUctPlayoutPolicyStat::m_nonRandLen. */ 00223 std::size_t m_nonRandLen; 00224 00225 /** Last move. 00226 Stored in member variable to avoid multiple calls to 00227 GoBoard::GetLastMove during GenerateMove. 00228 */ 00229 SgPoint m_lastMove; 00230 00231 /** List of equivalent best moves generated by the policy. 00232 The highest priority heuristic will generate all moves in this list. 00233 Moves in this list are not yet checked, if they are legal. 00234 This list is not used in GenerateMove(), if a pure random move 00235 is generated. 00236 */ 00237 GoPointList m_moves; 00238 00239 SgRandom m_random; 00240 00241 /** Balancer for GoUctUtil::IsMutualAtari(). */ 00242 mutable SgBalancer m_balancer; 00243 00244 CaptureGenerator m_captureGenerator; 00245 00246 GoUctPureRandomGenerator<BOARD> m_pureRandomGenerator; 00247 00248 GoUctPlayoutPolicyStat m_statistics; 00249 00250 /** Try to correct the proposed move, typically by moving it to a 00251 'better' point such as other liberty or neighbor. 00252 Examples implemented: self-ataries, clumps. 00253 */ 00254 bool CorrectMove(GoUctPlayoutPolicy<BOARD>::Corrector& corrFunction, 00255 SgPoint& mv, GoUctPlayoutPolicyType moveType); 00256 00257 /** Captures if last move was self-atari */ 00258 bool GenerateAtariCaptureMove(); 00259 00260 /** Generate escapes if last move was atari. */ 00261 bool GenerateAtariDefenseMove(); 00262 00263 /** Generate low lib moves around lastMove */ 00264 bool GenerateLowLibMove(SgPoint lastMove); 00265 00266 bool GenerateNakadeMove(); 00267 00268 void GenerateNakadeMove(SgPoint p); 00269 00270 /** Generate pattern move around last two moves */ 00271 bool GeneratePatternMove(); 00272 00273 void GeneratePatternMove(SgPoint p); 00274 00275 void GeneratePatternMove2(SgPoint p, SgPoint lastMove); 00276 00277 void GeneratePureRandom(); 00278 00279 bool GeneratePoint(SgPoint p) const; 00280 00281 /** Does playing on a liberty increase number of liberties for block? 00282 If yes, add to m_moves. 00283 Disabled if both liberties are simple chain libs, e.g. bamboo. 00284 */ 00285 void PlayGoodLiberties(SgPoint block); 00286 00287 /** see GoUctUtil::SelectRandom */ 00288 SgPoint SelectRandom(); 00289 00290 /** Add statistics for most recently played move. */ 00291 void UpdateStatistics(); 00292 }; 00293 00294 template<class BOARD> 00295 GoUctPlayoutPolicy<BOARD>::CaptureGenerator::CaptureGenerator(const BOARD& bd) 00296 : m_bd(bd) 00297 { 00298 m_candidates.reserve(GO_MAX_NUM_MOVES); 00299 } 00300 00301 template<class BOARD> 00302 void GoUctPlayoutPolicy<BOARD>::CaptureGenerator::StartPlayout() 00303 { 00304 m_candidates.clear(); 00305 for (typename BOARD::Iterator it(m_bd); it; ++it) 00306 { 00307 const SgPoint p = *it; 00308 if (m_bd.Occupied(p) && m_bd.Anchor(p) == p && m_bd.InAtari(p)) 00309 m_candidates.push_back(p); 00310 } 00311 } 00312 00313 template<class BOARD> 00314 void GoUctPlayoutPolicy<BOARD>::CaptureGenerator::OnPlay() 00315 { 00316 SgPoint lastMove = m_bd.GetLastMove(); 00317 if (lastMove == SG_NULLMOVE || lastMove == SG_PASS) 00318 return; 00319 if (m_bd.OccupiedInAtari(lastMove)) 00320 m_candidates.push_back(m_bd.Anchor(lastMove)); 00321 if (m_bd.NumNeighbors(lastMove, m_bd.ToPlay()) == 0) 00322 return; 00323 if (m_bd.OccupiedInAtari(lastMove + SG_NS)) 00324 m_candidates.push_back(m_bd.Anchor(lastMove + SG_NS)); 00325 if (m_bd.OccupiedInAtari(lastMove - SG_NS)) 00326 m_candidates.push_back(m_bd.Anchor(lastMove - SG_NS)); 00327 if (m_bd.OccupiedInAtari(lastMove + SG_WE)) 00328 m_candidates.push_back(m_bd.Anchor(lastMove + SG_WE)); 00329 if (m_bd.OccupiedInAtari(lastMove - SG_WE)) 00330 m_candidates.push_back(m_bd.Anchor(lastMove - SG_WE)); 00331 } 00332 00333 template<class BOARD> 00334 void GoUctPlayoutPolicy<BOARD>::CaptureGenerator::Generate(GoPointList& moves) 00335 { 00336 SG_ASSERT(moves.IsEmpty()); 00337 const SgBlackWhite opp = m_bd.Opponent(); 00338 // For efficiency reasons, this function does not check, if the same 00339 // move is generated multiple times (and will therefore played with 00340 // higher probabilty, if there are also other capture moves), because in 00341 // nearly all cases, there is zero or one global capture move on the 00342 // board. Most captures are done immediately by the atari heuristic 00343 for (size_t i = 0; i < m_candidates.size(); ++i) 00344 { 00345 const SgPoint p = m_candidates[i]; 00346 if (! m_bd.OccupiedInAtari(p)) 00347 { 00348 m_candidates[i] = m_candidates[m_candidates.size() - 1]; 00349 m_candidates.pop_back(); 00350 --i; 00351 continue; 00352 } 00353 if (m_bd.GetColor(p) == opp) 00354 moves.PushBack(m_bd.TheLiberty(p)); 00355 } 00356 } 00357 00358 template<class BOARD> 00359 GoUctPlayoutPolicy<BOARD>::GoUctPlayoutPolicy(const BOARD& bd, 00360 const GoUctPlayoutPolicyParam& param) 00361 : m_bd(bd), 00362 m_param(param), 00363 m_patterns(bd), 00364 m_checked(false), 00365 m_balancer(100), 00366 m_captureGenerator(bd), 00367 m_pureRandomGenerator(bd, m_random) 00368 { 00369 } 00370 00371 template<class BOARD> 00372 void GoUctPlayoutPolicy<BOARD>::ClearStatistics() 00373 { 00374 m_statistics.Clear(); 00375 } 00376 00377 template<class BOARD> 00378 bool GoUctPlayoutPolicy<BOARD>::CorrectMove( 00379 GoUctPlayoutPolicy<BOARD>::Corrector& corrFunction, 00380 SgPoint& mv, GoUctPlayoutPolicyType moveType) 00381 { 00382 #if DEBUG 00383 const SgPoint oldMv = mv; 00384 #endif 00385 if (! corrFunction(m_bd, mv)) 00386 return false; 00387 00388 m_moves.SetTo(mv); 00389 m_moveType = moveType; 00390 00391 #if DEBUG 00392 if (DEBUG_CORRECT_MOVE) 00393 SgDebug() << m_bd 00394 << "Replace " << SgWriteMove(oldMv, m_bd.ToPlay()) 00395 << " by " << SgWriteMove(mv, m_bd.ToPlay()) << '\n'; 00396 #endif 00397 return true; 00398 } 00399 00400 template<class BOARD> 00401 void GoUctPlayoutPolicy<BOARD>::EndPlayout() 00402 { 00403 } 00404 00405 template<class BOARD> 00406 bool GoUctPlayoutPolicy<BOARD>::GenerateAtariCaptureMove() 00407 { 00408 SG_ASSERT(! SgIsSpecialMove(m_lastMove)); 00409 if (m_bd.InAtari(m_lastMove)) 00410 { 00411 SgMove mv = m_bd.TheLiberty(m_lastMove); 00412 m_moves.PushBack(mv); 00413 return true; 00414 } 00415 return false; 00416 } 00417 00418 template<class BOARD> 00419 bool GoUctPlayoutPolicy<BOARD>::GenerateAtariDefenseMove() 00420 { 00421 SG_ASSERT(m_moves.IsEmpty()); 00422 SG_ASSERT(! SgIsSpecialMove(m_lastMove)); 00423 SgBlackWhite toPlay = m_bd.ToPlay(); 00424 if (m_bd.NumNeighbors(m_lastMove, toPlay) == 0) 00425 return false; 00426 SgSList<SgPoint,4> anchorList; 00427 for (SgNb4Iterator it(m_lastMove); it; ++it) 00428 { 00429 if (m_bd.GetColor(*it) != toPlay || ! m_bd.InAtari(*it)) 00430 continue; 00431 SgPoint anchor = m_bd.Anchor(*it); 00432 if (anchorList.Contains(anchor)) 00433 continue; 00434 anchorList.PushBack(anchor); 00435 00436 // Check if move on last liberty would escape the atari 00437 SgPoint theLiberty = m_bd.TheLiberty(anchor); 00438 if (! GoBoardUtil::SelfAtari(m_bd, theLiberty)) 00439 m_moves.PushBack(theLiberty); 00440 00441 // Capture adjacent blocks 00442 for (GoAdjBlockIterator<BOARD> it2(m_bd, anchor, 1); it2; ++it2) 00443 { 00444 SgPoint oppLiberty = m_bd.TheLiberty(*it2); 00445 // If opponent's last liberty is not my last liberty, we know 00446 // that we will have two liberties after capturing (my last 00447 // liberty + at least one stone captured). If both last liberties 00448 // are the same, we already checked above with 00449 // GoBoardUtil::SelfAtari(theLiberty), if the move escapes the 00450 // atari 00451 if (oppLiberty != theLiberty) 00452 m_moves.PushBack(oppLiberty); 00453 } 00454 } 00455 return ! m_moves.IsEmpty(); 00456 } 00457 00458 template<class BOARD> 00459 void GoUctPlayoutPolicy<BOARD>::PlayGoodLiberties(SgPoint block) 00460 { 00461 SgPoint ignoreOther; 00462 if (! GoBoardUtil::IsSimpleChain(m_bd, block, ignoreOther)) 00463 for (typename BOARD::LibertyIterator it(m_bd, block); it; ++it) 00464 if ( GoUctUtil::GainsLiberties(m_bd, block, *it) 00465 && ! GoBoardUtil::SelfAtari(m_bd, *it) 00466 ) 00467 m_moves.PushBack(*it); 00468 } 00469 00470 template<class BOARD> 00471 bool GoUctPlayoutPolicy<BOARD>::GenerateLowLibMove(SgPoint lastMove) 00472 { 00473 SG_ASSERT(! SgIsSpecialMove(lastMove)); 00474 SG_ASSERT(! m_bd.IsEmpty(lastMove)); 00475 const SgBlackWhite toPlay = m_bd.ToPlay(); 00476 00477 // take liberty of last move 00478 if (m_bd.NumLiberties(lastMove) == 2) 00479 { 00480 const SgPoint anchor = m_bd.Anchor(lastMove); 00481 PlayGoodLiberties(anchor); 00482 } 00483 00484 if (m_bd.NumNeighbors(lastMove, toPlay) != 0) 00485 { 00486 // play liberties of neighbor blocks 00487 SgSList<SgPoint,4> anchorList; 00488 for (SgNb4Iterator it(lastMove); it; ++it) 00489 { 00490 if (m_bd.GetColor(*it) == toPlay 00491 && m_bd.NumLiberties(*it) == 2) 00492 { 00493 const SgPoint anchor = m_bd.Anchor(*it); 00494 if (! anchorList.Contains(anchor)) 00495 { 00496 anchorList.PushBack(anchor); 00497 PlayGoodLiberties(anchor); 00498 } 00499 } 00500 } 00501 } 00502 00503 return ! m_moves.IsEmpty(); 00504 } 00505 00506 template<class BOARD> 00507 SG_ATTR_FLATTEN SgPoint GoUctPlayoutPolicy<BOARD>::GenerateMove() 00508 { 00509 m_moves.Clear(); 00510 m_checked = false; 00511 00512 SgPoint mv = SG_NULLMOVE; 00513 00514 if (m_param.m_fillboardTries > 0) 00515 { 00516 m_moveType = GOUCT_FILLBOARD; 00517 mv = m_pureRandomGenerator.GenerateFillboardMove( 00518 m_param.m_fillboardTries); 00519 } 00520 00521 m_lastMove = m_bd.GetLastMove(); 00522 if (mv == SG_NULLMOVE 00523 && ! SgIsSpecialMove(m_lastMove) // skip if Pass or Null 00524 && ! m_bd.IsEmpty(m_lastMove) // skip if move was suicide 00525 ) 00526 { 00527 if (m_param.m_useNakadeHeuristic && GenerateNakadeMove()) 00528 { 00529 m_moveType = GOUCT_NAKADE; 00530 mv = SelectRandom(); 00531 } 00532 if (mv == SG_NULLMOVE && GenerateAtariCaptureMove()) 00533 { 00534 m_moveType = GOUCT_ATARI_CAPTURE; 00535 mv = SelectRandom(); 00536 } 00537 if (mv == SG_NULLMOVE && GenerateAtariDefenseMove()) 00538 { 00539 m_moveType = GOUCT_ATARI_DEFEND; 00540 mv = SelectRandom(); 00541 } 00542 if (mv == SG_NULLMOVE && GenerateLowLibMove(m_lastMove)) 00543 { 00544 m_moveType = GOUCT_LOWLIB; 00545 mv = SelectRandom(); 00546 } 00547 if (mv == SG_NULLMOVE && GeneratePatternMove()) 00548 { 00549 m_moveType = GOUCT_PATTERN; 00550 mv = SelectRandom(); 00551 } 00552 } 00553 if (mv == SG_NULLMOVE) 00554 { 00555 m_moveType = GOUCT_CAPTURE; 00556 m_captureGenerator.Generate(m_moves); 00557 mv = SelectRandom(); 00558 } 00559 if (mv == SG_NULLMOVE) 00560 { 00561 m_moveType = GOUCT_RANDOM; 00562 mv = m_pureRandomGenerator.Generate(m_balancer); 00563 } 00564 00565 if (mv == SG_NULLMOVE) 00566 { 00567 m_moveType = GOUCT_PASS; 00568 mv = SG_PASS; 00569 } 00570 else 00571 { 00572 SG_ASSERT(m_bd.IsLegal(mv)); 00573 m_checked = CorrectMove(GoUctUtil::DoSelfAtariCorrection, mv, 00574 GOUCT_SELFATARI_CORRECTION); 00575 if (USE_CLUMP_CORRECTION && ! m_checked) 00576 CorrectMove(GoUctUtil::DoClumpCorrection, mv, 00577 GOUCT_CLUMP_CORRECTION); 00578 } 00579 SG_ASSERT(m_bd.IsLegal(mv)); 00580 SG_ASSERT(mv == SG_PASS || ! m_bd.IsSuicide(mv)); 00581 00582 if (m_param.m_statisticsEnabled) 00583 UpdateStatistics(); 00584 00585 return mv; 00586 } 00587 00588 /** Nakade heuristic. 00589 If there is a region of three empty points adjacent to last move, play in 00590 the center of the region. 00591 */ 00592 template<class BOARD> 00593 bool GoUctPlayoutPolicy<BOARD>::GenerateNakadeMove() 00594 { 00595 SG_ASSERT(m_moves.IsEmpty()); 00596 SG_ASSERT(! SgIsSpecialMove(m_lastMove)); 00597 GenerateNakadeMove(m_lastMove + SG_NS); 00598 GenerateNakadeMove(m_lastMove - SG_NS); 00599 GenerateNakadeMove(m_lastMove + SG_WE); 00600 GenerateNakadeMove(m_lastMove - SG_WE); 00601 // Ignore duplicates in move list, happens rarely 00602 return ! m_moves.IsEmpty(); 00603 } 00604 00605 template<class BOARD> 00606 void GoUctPlayoutPolicy<BOARD>::GenerateNakadeMove(SgPoint p) 00607 { 00608 SgBlackWhite toPlay = m_bd.ToPlay(); 00609 if (m_bd.IsEmpty(p) && m_bd.NumNeighbors(p, toPlay) == 0) 00610 { 00611 int numEmptyNeighbors = m_bd.NumEmptyNeighbors(p); 00612 if (numEmptyNeighbors == 2) 00613 { 00614 int n = 0; 00615 for (SgNb4Iterator it(p); it; ++it) 00616 if (m_bd.IsEmpty(*it)) 00617 { 00618 if (m_bd.NumEmptyNeighbors(*it) != 1 00619 || m_bd.NumNeighbors(*it, toPlay) > 0) 00620 return; 00621 if (++n > 2) 00622 break; 00623 } 00624 m_moves.PushBack(p); 00625 } 00626 else if (numEmptyNeighbors == 1) 00627 { 00628 for (SgNb4Iterator it(p); it; ++it) 00629 if (m_bd.IsEmpty(*it)) 00630 { 00631 if (m_bd.NumEmptyNeighbors(*it) != 2 00632 || m_bd.NumNeighbors(*it, toPlay) > 0) 00633 return; 00634 for (SgNb4Iterator it2(*it); it2; ++it2) 00635 if (m_bd.IsEmpty(*it2) && *it2 != p) 00636 { 00637 if (m_bd.NumEmptyNeighbors(*it2) == 1 00638 && m_bd.NumNeighbors(*it2, toPlay) == 0) 00639 m_moves.PushBack(*it); 00640 break; 00641 } 00642 break; 00643 } 00644 00645 } 00646 } 00647 } 00648 00649 /** Pattern heuristic. 00650 Use patterns (only in 3x3 neighborhood of last move) 00651 @see GoUctPatterns 00652 */ 00653 template<class BOARD> 00654 bool GoUctPlayoutPolicy<BOARD>::GeneratePatternMove() 00655 { 00656 SG_ASSERT(m_moves.IsEmpty()); 00657 SG_ASSERT(! SgIsSpecialMove(m_lastMove)); 00658 GeneratePatternMove(m_lastMove + SG_NS - SG_WE); 00659 GeneratePatternMove(m_lastMove + SG_NS); 00660 GeneratePatternMove(m_lastMove + SG_NS + SG_WE); 00661 GeneratePatternMove(m_lastMove - SG_WE); 00662 GeneratePatternMove(m_lastMove + SG_WE); 00663 GeneratePatternMove(m_lastMove - SG_NS - SG_WE); 00664 GeneratePatternMove(m_lastMove - SG_NS); 00665 GeneratePatternMove(m_lastMove - SG_NS + SG_WE); 00666 if (SECOND_LAST_MOVE_PATTERNS) 00667 { 00668 const SgPoint lastMove2 = m_bd.Get2ndLastMove(); 00669 if (! SgIsSpecialMove(lastMove2)) 00670 { 00671 GeneratePatternMove2(lastMove2 + SG_NS - SG_WE, m_lastMove); 00672 GeneratePatternMove2(lastMove2 + SG_NS, m_lastMove); 00673 GeneratePatternMove2(lastMove2 + SG_NS + SG_WE, m_lastMove); 00674 GeneratePatternMove2(lastMove2 - SG_WE, m_lastMove); 00675 GeneratePatternMove2(lastMove2 + SG_WE, m_lastMove); 00676 GeneratePatternMove2(lastMove2 - SG_NS - SG_WE, m_lastMove); 00677 GeneratePatternMove2(lastMove2 - SG_NS, m_lastMove); 00678 GeneratePatternMove2(lastMove2 - SG_NS + SG_WE, m_lastMove); 00679 } 00680 } 00681 return ! m_moves.IsEmpty(); 00682 } 00683 00684 template<class BOARD> 00685 inline void GoUctPlayoutPolicy<BOARD>::GeneratePatternMove(SgPoint p) 00686 { 00687 if (m_bd.IsEmpty(p) 00688 && m_patterns.MatchAny(p) 00689 && ! GoBoardUtil::SelfAtari(m_bd, p)) 00690 m_moves.PushBack(p); 00691 } 00692 00693 template<class BOARD> 00694 inline void GoUctPlayoutPolicy<BOARD>::GeneratePatternMove2(SgPoint p, 00695 SgPoint lastMove) 00696 { 00697 if (m_bd.IsEmpty(p) 00698 && ! SgPointUtil::In8Neighborhood(lastMove, p) 00699 && m_patterns.MatchAny(p) 00700 && ! GoBoardUtil::SelfAtari(m_bd, p)) 00701 m_moves.PushBack(p); 00702 } 00703 00704 template<class BOARD> 00705 inline bool GoUctPlayoutPolicy<BOARD>::GeneratePoint(SgPoint p) const 00706 { 00707 return GoUctUtil::GeneratePoint(m_bd, m_balancer, p, m_bd.ToPlay()); 00708 } 00709 00710 template<class BOARD> 00711 GoPointList GoUctPlayoutPolicy<BOARD>::GetEquivalentBestMoves() const 00712 { 00713 GoPointList result; 00714 if (m_moveType == GOUCT_RANDOM) 00715 { 00716 for (typename BOARD::Iterator it(m_bd); it; ++it) 00717 if (m_bd.IsEmpty(*it) && GeneratePoint(*it)) 00718 result.PushBack(*it); 00719 } 00720 // Move in m_moves are not checked yet, if legal etc. 00721 for (GoPointList::Iterator it(m_moves); it; ++it) 00722 if (m_checked || GeneratePoint(*it)) 00723 result.PushBack(*it); 00724 return result; 00725 } 00726 00727 template<class BOARD> 00728 GoUctPlayoutPolicyType GoUctPlayoutPolicy<BOARD>::MoveType() 00729 const 00730 { 00731 return m_moveType; 00732 } 00733 00734 template<class BOARD> 00735 void GoUctPlayoutPolicy<BOARD>::OnPlay() 00736 { 00737 m_captureGenerator.OnPlay(); 00738 m_pureRandomGenerator.OnPlay(); 00739 } 00740 00741 00742 template<class BOARD> 00743 const GoUctPatterns<BOARD>& GoUctPlayoutPolicy<BOARD>::Patterns() 00744 const 00745 { 00746 return m_patterns; 00747 } 00748 00749 template<class BOARD> 00750 inline SgPoint GoUctPlayoutPolicy<BOARD>::SelectRandom() 00751 { 00752 return GoUctUtil::SelectRandom(m_bd, m_bd.ToPlay(), m_moves, m_random, 00753 m_balancer); 00754 } 00755 00756 template<class BOARD> 00757 const GoUctPlayoutPolicyStat& 00758 GoUctPlayoutPolicy<BOARD>::Statistics() const 00759 { 00760 return m_statistics; 00761 } 00762 00763 template<class BOARD> 00764 void GoUctPlayoutPolicy<BOARD>::StartPlayout() 00765 { 00766 m_captureGenerator.StartPlayout(); 00767 m_pureRandomGenerator.Start(); 00768 m_nonRandLen = 0; 00769 } 00770 00771 template<class BOARD> 00772 void GoUctPlayoutPolicy<BOARD>::UpdateStatistics() 00773 { 00774 ++m_statistics.m_nuMoves; 00775 ++m_statistics.m_nuMoveType[m_moveType]; 00776 if (m_moveType == GOUCT_RANDOM) 00777 { 00778 if (m_nonRandLen > 0) 00779 { 00780 m_statistics.m_nonRandLen.Add(m_nonRandLen); 00781 m_nonRandLen = 0; 00782 } 00783 } 00784 else 00785 { 00786 ++m_nonRandLen; 00787 m_statistics.m_moveListLen.Add(GetEquivalentBestMoves().Length()); 00788 } 00789 } 00790 00791 //---------------------------------------------------------------------------- 00792 00793 template<class BOARD> 00794 class GoUctPlayoutPolicyFactory 00795 { 00796 public: 00797 /** Constructor. 00798 @param param Playout policy parameters. Stores a reference. Lifetime 00799 of the argument must exceed the lifetime of this factory and created 00800 objects. 00801 */ 00802 GoUctPlayoutPolicyFactory(const GoUctPlayoutPolicyParam& param); 00803 00804 GoUctPlayoutPolicy<BOARD>* Create(const BOARD& bd); 00805 00806 private: 00807 const GoUctPlayoutPolicyParam& m_param; 00808 }; 00809 00810 template<class BOARD> 00811 GoUctPlayoutPolicyFactory<BOARD> 00812 ::GoUctPlayoutPolicyFactory(const GoUctPlayoutPolicyParam& param) 00813 : m_param(param) 00814 { 00815 } 00816 00817 template<class BOARD> 00818 GoUctPlayoutPolicy<BOARD>* 00819 GoUctPlayoutPolicyFactory<BOARD>::Create(const BOARD& bd) 00820 { 00821 return new GoUctPlayoutPolicy<BOARD>(bd, m_param); 00822 } 00823 00824 //---------------------------------------------------------------------------- 00825 00826 #endif // GOUCT_PLAYOUTPOLICY_H