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