00001
00002
00003
00004
00005
00006
00007 #ifndef GOUCT_PLAYER_H
00008 #define GOUCT_PLAYER_H
00009
00010 #include <boost/scoped_ptr.hpp>
00011 #include <vector>
00012 #include "GoBoard.h"
00013 #include "GoBoardRestorer.h"
00014 #include "GoPlayer.h"
00015 #include "GoTimeControl.h"
00016 #include "GoUctDefaultRootFilter.h"
00017 #include "GoUctGlobalSearch.h"
00018 #include "GoUctObjectWithSearch.h"
00019 #include "GoUctPlayoutPolicy.h"
00020 #include "GoUctRootFilter.h"
00021 #include "SgDebug.h"
00022 #include "SgNbIterator.h"
00023 #include "SgNode.h"
00024 #include "SgPointArray.h"
00025 #include "SgRestorer.h"
00026 #include "SgSList.h"
00027 #include "SgMpiSynchronizer.h"
00028 #include "SgTime.h"
00029 #include "SgTimer.h"
00030 #include "SgUctTreeUtil.h"
00031 #include "SgWrite.h"
00032
00033 template<typename T,int SIZE> class SgSList;
00034
00035
00036
00037
00038 enum GoUctGlobalSearchMode
00039 {
00040
00041 GOUCT_SEARCHMODE_PLAYOUTPOLICY,
00042
00043
00044 GOUCT_SEARCHMODE_UCT,
00045
00046
00047 GOUCT_SEARCHMODE_ONEPLY
00048 };
00049
00050
00051
00052
00053 template <class SEARCH, class THREAD>
00054 class GoUctPlayer
00055 : public GoPlayer,
00056 public GoUctObjectWithSearch,
00057 public SgObjectWithDefaultTimeControl
00058 {
00059 public:
00060
00061 struct Statistics
00062 {
00063 std::size_t m_nuGenMove;
00064
00065 SgStatisticsExt<float,std::size_t> m_reuse;
00066
00067 SgStatisticsExt<float,std::size_t> m_gamesPerSecond;
00068
00069 Statistics();
00070
00071 void Clear();
00072
00073
00074 void Write(std::ostream& out) const;
00075 };
00076
00077 GoUctPlayoutPolicyParam m_playoutPolicyParam;
00078
00079
00080
00081
00082 GoUctPlayer(GoBoard& bd);
00083
00084 ~GoUctPlayer();
00085
00086
00087
00088
00089
00090 void OnBoardChange();
00091
00092
00093
00094
00095
00096
00097
00098 SgPoint GenMove(const SgTimeRecord& time, SgBlackWhite toPlay);
00099
00100 std::string Name() const;
00101
00102 void Ponder();
00103
00104
00105
00106
00107
00108
00109
00110 SgDefaultTimeControl& TimeControl();
00111
00112 const SgDefaultTimeControl& TimeControl() const;
00113
00114
00115
00116
00117
00118
00119
00120 GoUctSearch& Search();
00121
00122 const GoUctSearch& Search() const;
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135 bool AutoParam() const;
00136
00137
00138 void SetAutoParam(bool enable);
00139
00140
00141
00142
00143
00144
00145
00146 bool EarlyPass() const;
00147
00148
00149 void SetEarlyPass(bool enable);
00150
00151
00152
00153
00154 bool ForcedOpeningMoves() const;
00155
00156
00157 void SetForcedOpeningMoves(bool enable);
00158
00159
00160
00161
00162
00163 bool IgnoreClock() const;
00164
00165
00166 void SetIgnoreClock(bool enable);
00167
00168
00169 std::size_t MaxGames() const;
00170
00171
00172 void SetMaxGames(std::size_t maxGames);
00173
00174
00175
00176
00177
00178 bool EnablePonder() const;
00179
00180
00181 void SetEnablePonder(bool enable);
00182
00183
00184
00185
00186
00187
00188 std::size_t ResignMinGames() const;
00189
00190
00191 void SetResignMinGames(std::size_t n);
00192
00193
00194 bool UseRootFilter() const;
00195
00196
00197 void SetUseRootFilter(bool enable);
00198
00199
00200
00201
00202
00203
00204 bool ReuseSubtree() const;
00205
00206
00207 void SetReuseSubtree(bool enable);
00208
00209
00210
00211
00212 double ResignThreshold() const;
00213
00214
00215 void SetResignThreshold(double threshold);
00216
00217
00218 GoUctGlobalSearchMode SearchMode() const;
00219
00220
00221 void SetSearchMode(GoUctGlobalSearchMode mode);
00222
00223
00224 bool WriteDebugOutput() const;
00225
00226
00227 void SetWriteDebugOutput(bool flag);
00228
00229
00230
00231
00232
00233
00234
00235 const Statistics& GetStatistics() const;
00236
00237 void ClearStatistics();
00238
00239
00240
00241 SEARCH& GlobalSearch();
00242
00243 const SEARCH& GlobalSearch() const;
00244
00245
00246 GoUctRootFilter& RootFilter();
00247
00248
00249
00250
00251 void SetRootFilter(GoUctRootFilter* filter);
00252
00253 void SetMpiSynchronizer(const SgMpiSynchronizerHandle &synchronizerHandle);
00254
00255 SgMpiSynchronizerHandle GetMpiSynchronizer();
00256
00257 private:
00258
00259 GoUctGlobalSearchMode m_searchMode;
00260
00261
00262 bool m_autoParam;
00263
00264
00265 bool m_forcedOpeningMoves;
00266
00267
00268 bool m_ignoreClock;
00269
00270
00271 bool m_enablePonder;
00272
00273
00274 bool m_useRootFilter;
00275
00276
00277 bool m_reuseSubtree;
00278
00279
00280 bool m_earlyPass;
00281
00282
00283 double m_resignThreshold;
00284
00285
00286 int m_lastBoardSize;
00287
00288 std::size_t m_maxGames;
00289
00290 std::size_t m_resignMinGames;
00291
00292 SEARCH m_search;
00293
00294 GoTimeControl m_timeControl;
00295
00296 Statistics m_statistics;
00297
00298 boost::scoped_ptr<GoUctRootFilter> m_rootFilter;
00299
00300
00301
00302 boost::scoped_ptr<GoUctPlayoutPolicy<GoBoard> > m_playoutPolicy;
00303
00304 SgMpiSynchronizerHandle m_mpiSynchronizer;
00305
00306 bool m_writeDebugOutput;
00307
00308 SgMove GenMovePlayoutPolicy(SgBlackWhite toPlay);
00309
00310 bool DoEarlyPassSearch(size_t maxGames, double maxTime, SgPoint& move);
00311
00312 SgPoint DoSearch(SgBlackWhite toPlay, double maxTime,
00313 bool isDuringPondering);
00314
00315 void FindInitTree(SgUctTree& initTree, SgBlackWhite toPlay,
00316 double maxTime);
00317
00318 void SetDefaultParameters(int boardSize);
00319
00320 bool VerifyNeutralMove(size_t maxGames, double maxTime, SgPoint move);
00321 };
00322
00323 template <class SEARCH, class THREAD>
00324 inline bool GoUctPlayer<SEARCH, THREAD>::AutoParam() const
00325 {
00326 return m_autoParam;
00327 }
00328
00329 template <class SEARCH, class THREAD>
00330 inline SEARCH&
00331 GoUctPlayer<SEARCH, THREAD>::GlobalSearch()
00332 {
00333 return m_search;
00334 }
00335
00336 template <class SEARCH, class THREAD>
00337 inline const SEARCH& GoUctPlayer<SEARCH, THREAD>::GlobalSearch() const
00338 {
00339 return m_search;
00340 }
00341
00342 template <class SEARCH, class THREAD>
00343 inline bool GoUctPlayer<SEARCH, THREAD>::EarlyPass() const
00344 {
00345 return m_earlyPass;
00346 }
00347
00348 template <class SEARCH, class THREAD>
00349 inline bool GoUctPlayer<SEARCH, THREAD>::EnablePonder() const
00350 {
00351 return m_enablePonder;
00352 }
00353
00354 template <class SEARCH, class THREAD>
00355 inline bool GoUctPlayer<SEARCH, THREAD>::ForcedOpeningMoves() const
00356 {
00357 return m_forcedOpeningMoves;
00358 }
00359
00360 template <class SEARCH, class THREAD>
00361 inline bool GoUctPlayer<SEARCH, THREAD>::IgnoreClock() const
00362 {
00363 return m_ignoreClock;
00364 }
00365
00366 template <class SEARCH, class THREAD>
00367 inline std::size_t GoUctPlayer<SEARCH, THREAD>::MaxGames() const
00368 {
00369 return m_maxGames;
00370 }
00371
00372 template <class SEARCH, class THREAD>
00373 inline bool GoUctPlayer<SEARCH, THREAD>::UseRootFilter() const
00374 {
00375 return m_useRootFilter;
00376 }
00377
00378 template <class SEARCH, class THREAD>
00379 inline std::size_t GoUctPlayer<SEARCH, THREAD>::ResignMinGames() const
00380 {
00381 return m_resignMinGames;
00382 }
00383
00384 template <class SEARCH, class THREAD>
00385 inline double GoUctPlayer<SEARCH, THREAD>::ResignThreshold() const
00386 {
00387 return m_resignThreshold;
00388 }
00389
00390 template <class SEARCH, class THREAD>
00391 inline bool GoUctPlayer<SEARCH, THREAD>::ReuseSubtree() const
00392 {
00393 return m_reuseSubtree;
00394 }
00395
00396 template <class SEARCH, class THREAD>
00397 inline GoUctRootFilter& GoUctPlayer<SEARCH, THREAD>::RootFilter()
00398 {
00399 return *m_rootFilter;
00400 }
00401
00402 template <class SEARCH, class THREAD>
00403 inline GoUctGlobalSearchMode GoUctPlayer<SEARCH, THREAD>::SearchMode() const
00404 {
00405 return m_searchMode;
00406 }
00407
00408 template <class SEARCH, class THREAD>
00409 inline void GoUctPlayer<SEARCH, THREAD>::SetAutoParam(bool enable)
00410 {
00411 m_autoParam = enable;
00412 }
00413
00414 template <class SEARCH, class THREAD>
00415 inline void GoUctPlayer<SEARCH, THREAD>::SetEarlyPass(bool enable)
00416 {
00417 m_earlyPass = enable;
00418 }
00419
00420 template <class SEARCH, class THREAD>
00421 inline void GoUctPlayer<SEARCH, THREAD>::SetEnablePonder(bool enable)
00422 {
00423 m_enablePonder = enable;
00424 }
00425
00426 template <class SEARCH, class THREAD>
00427 inline void GoUctPlayer<SEARCH, THREAD>::SetForcedOpeningMoves(bool enable)
00428 {
00429 m_forcedOpeningMoves = enable;
00430 }
00431
00432 template <class SEARCH, class THREAD>
00433 inline void GoUctPlayer<SEARCH, THREAD>::SetIgnoreClock(bool enable)
00434 {
00435 m_ignoreClock = enable;
00436 }
00437
00438 template <class SEARCH, class THREAD>
00439 inline void GoUctPlayer<SEARCH, THREAD>::SetMaxGames(std::size_t maxGames)
00440 {
00441 m_maxGames = maxGames;
00442 }
00443
00444 template <class SEARCH, class THREAD>
00445 inline void GoUctPlayer<SEARCH, THREAD>::SetUseRootFilter(bool enable)
00446 {
00447 m_useRootFilter = enable;
00448 }
00449
00450 template <class SEARCH, class THREAD>
00451 inline void GoUctPlayer<SEARCH, THREAD>::SetResignMinGames(std::size_t n)
00452 {
00453 m_resignMinGames = n;
00454 }
00455
00456 template <class SEARCH, class THREAD>
00457 inline void GoUctPlayer<SEARCH, THREAD>::SetResignThreshold(double threshold)
00458 {
00459 m_resignThreshold = threshold;
00460 }
00461
00462 template <class SEARCH, class THREAD>
00463 inline void GoUctPlayer<SEARCH, THREAD>::SetRootFilter(GoUctRootFilter*
00464 filter)
00465 {
00466 m_rootFilter.reset(filter);
00467 }
00468
00469 template <class SEARCH, class THREAD>
00470 inline void
00471 GoUctPlayer<SEARCH, THREAD>::SetSearchMode(GoUctGlobalSearchMode mode)
00472 {
00473 m_searchMode = mode;
00474 }
00475
00476 template <class SEARCH, class THREAD>
00477 inline void GoUctPlayer<SEARCH, THREAD>::SetMpiSynchronizer(const SgMpiSynchronizerHandle &handle)
00478 {
00479 m_mpiSynchronizer = SgMpiSynchronizerHandle(handle);
00480 m_search.SetMpiSynchronizer(handle);
00481 }
00482
00483 template <class SEARCH, class THREAD>
00484 inline SgMpiSynchronizerHandle
00485 GoUctPlayer<SEARCH, THREAD>::GetMpiSynchronizer()
00486 {
00487 return SgMpiSynchronizerHandle(m_mpiSynchronizer);
00488 }
00489
00490 template <class SEARCH, class THREAD>
00491 GoUctPlayer<SEARCH, THREAD>::Statistics::Statistics()
00492 {
00493 Clear();
00494 }
00495
00496 template <class SEARCH, class THREAD>
00497 void GoUctPlayer<SEARCH, THREAD>::Statistics::Clear()
00498 {
00499 m_nuGenMove = 0;
00500 m_gamesPerSecond.Clear();
00501 m_reuse.Clear();
00502 }
00503
00504 template <class SEARCH, class THREAD>
00505 bool GoUctPlayer<SEARCH, THREAD>::WriteDebugOutput() const
00506 {
00507 return m_writeDebugOutput;
00508 }
00509
00510 template <class SEARCH, class THREAD>
00511 void GoUctPlayer<SEARCH, THREAD>::SetWriteDebugOutput(bool flag)
00512 {
00513 m_writeDebugOutput = flag;
00514 }
00515
00516 template <class SEARCH, class THREAD>
00517 void GoUctPlayer<SEARCH, THREAD>::Statistics::Write(std::ostream& out) const
00518 {
00519 out << SgWriteLabel("NuGenMove") << m_nuGenMove << '\n'
00520 << SgWriteLabel("GamesPerSec");
00521 m_gamesPerSecond.Write(out);
00522 out << '\n'
00523 << SgWriteLabel("Reuse");
00524 m_reuse.Write(out);
00525 out << '\n';
00526 }
00527
00528 template <class SEARCH, class THREAD>
00529 GoUctPlayer<SEARCH, THREAD>::GoUctPlayer(GoBoard& bd)
00530 : GoPlayer(bd),
00531 m_searchMode(GOUCT_SEARCHMODE_UCT),
00532 m_autoParam(true),
00533 m_forcedOpeningMoves(true),
00534 m_ignoreClock(false),
00535 m_enablePonder(false),
00536 m_useRootFilter(true),
00537 m_reuseSubtree(false),
00538 m_earlyPass(true),
00539 m_lastBoardSize(-1),
00540 m_maxGames(999999999),
00541 m_resignMinGames(5000),
00542 m_search(Board(),
00543 new GoUctPlayoutPolicyFactory<GoUctBoard>(
00544 m_playoutPolicyParam),
00545 m_playoutPolicyParam),
00546
00547 m_timeControl(Board()),
00548 m_rootFilter(new GoUctDefaultRootFilter(Board())),
00549 m_mpiSynchronizer(SgMpiNullSynchronizer::Create()),
00550 m_writeDebugOutput(true)
00551 {
00552 SetDefaultParameters(Board().Size());
00553 m_search.SetMpiSynchronizer(m_mpiSynchronizer);
00554 }
00555
00556 template <class SEARCH, class THREAD>
00557 GoUctPlayer<SEARCH, THREAD>::~GoUctPlayer()
00558 {
00559 }
00560
00561 template <class SEARCH, class THREAD>
00562 void GoUctPlayer<SEARCH, THREAD>::ClearStatistics()
00563 {
00564 m_statistics.Clear();
00565 }
00566
00567
00568
00569
00570
00571
00572
00573
00574 template <class SEARCH, class THREAD>
00575 bool GoUctPlayer<SEARCH, THREAD>::DoEarlyPassSearch(size_t maxGames,
00576 double maxTime,
00577 SgPoint& move)
00578 {
00579 SgDebug() << "GoUctPlayer: doing a search if early pass is possible\n";
00580 GoBoard& bd = Board();
00581 bd.Play(SG_PASS);
00582 bool winAfterPass = false;
00583 bool passWins = GoBoardUtil::PassWins(bd, bd.ToPlay());
00584 m_mpiSynchronizer->SynchronizePassWins(passWins);
00585 if (passWins)
00586 {
00587
00588
00589
00590 winAfterPass = false;
00591 }
00592 else
00593 {
00594 SgRestorer<bool> restorer(&m_search.m_param.m_territoryStatistics);
00595 m_search.m_param.m_territoryStatistics = true;
00596 std::vector<SgPoint> sequence;
00597 float value = m_search.Search(maxGames, maxTime, sequence);
00598 value = m_search.InverseEval(value);
00599 winAfterPass = (value > 1 - m_resignThreshold);
00600 }
00601 bd.Undo();
00602
00603 bool earlyPassPossible = true;
00604 if (earlyPassPossible && ! winAfterPass)
00605 {
00606 SgDebug() << "GoUctPlayer: no early pass possible (no win)\n";
00607 earlyPassPossible = false;
00608 }
00609 move = SG_PASS;
00610 THREAD& threadState = dynamic_cast<THREAD&>(m_search.ThreadState(0));
00611 SgPointArray<SgUctStatistics> territory =
00612 threadState.m_territoryStatistics;
00613 if (earlyPassPossible)
00614 {
00615 for (GoBoard::Iterator it(bd); it; ++it)
00616 if (territory[*it].Count() == 0)
00617 {
00618
00619
00620 SgDebug()
00621 << "GoUctPlayer: no early pass possible (no stat)\n";
00622 earlyPassPossible = false;
00623 break;
00624 }
00625 }
00626
00627 if (earlyPassPossible)
00628 {
00629 const float threshold = 0.2;
00630 for (GoBoard::Iterator it(bd); it; ++it)
00631 {
00632 float mean = territory[*it].Mean();
00633 if (mean > threshold && mean < 1 - threshold)
00634 {
00635
00636 bool isSafeToPlayAdj = false;
00637 bool isSafeOppAdj = false;
00638 for (SgNb4Iterator it2(*it); it2; ++it2)
00639 if (! bd.IsBorder(*it2))
00640 {
00641 float mean = territory[*it2].Mean();
00642 if (mean < threshold)
00643 isSafeToPlayAdj = true;
00644 if (mean > 1 - threshold)
00645 isSafeOppAdj = true;
00646 }
00647 if (isSafeToPlayAdj && isSafeOppAdj)
00648 {
00649 if (bd.IsLegal(*it) && ! GoBoardUtil::SelfAtari(bd, *it))
00650 move = *it;
00651 else
00652 {
00653 SgDebug() <<
00654 "GoUctPlayer: no early pass possible"
00655 " (neutral illegal or self-atari)\n";
00656 earlyPassPossible = false;
00657 break;
00658 }
00659 }
00660 else
00661 {
00662 SgDebug()
00663 << "GoUctPlayer: no early pass possible (unsafe point)\n";
00664 earlyPassPossible = false;
00665 break;
00666 }
00667 }
00668 }
00669 }
00670
00671 m_mpiSynchronizer->SynchronizeEarlyPassPossible(earlyPassPossible);
00672 if (! earlyPassPossible)
00673 return false;
00674 m_mpiSynchronizer->SynchronizeMove(move);
00675 if (move == SG_PASS)
00676 SgDebug() << "GoUctPlayer: early pass is possible\n";
00677 else if (VerifyNeutralMove(maxGames, maxTime, move))
00678 SgDebug() << "GoUctPlayer: generate play on neutral point\n";
00679 else
00680 {
00681 SgDebug() << "GoUctPlayer: neutral move failed to verify\n";
00682 return false;
00683 }
00684 return true;
00685 }
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696 template <class SEARCH, class THREAD>
00697 SgPoint GoUctPlayer<SEARCH, THREAD>::DoSearch(SgBlackWhite toPlay,
00698 double maxTime,
00699 bool isDuringPondering)
00700 {
00701 SgUctTree* initTree = 0;
00702 SgTimer timer;
00703 double timeInitTree = 0;
00704 if (m_reuseSubtree)
00705 {
00706 initTree = &m_search.GetTempTree();
00707 timeInitTree = -timer.GetTime();
00708 FindInitTree(*initTree, toPlay, maxTime);
00709 timeInitTree += timer.GetTime();
00710 if (isDuringPondering)
00711 {
00712 bool aborted = SgUserAbort();
00713 m_mpiSynchronizer->SynchronizeUserAbort(aborted);
00714 if (aborted)
00715
00716
00717
00718
00719
00720 return SG_NULLMOVE;
00721 }
00722 }
00723 std::vector<SgMove> rootFilter;
00724 double timeRootFilter = 0;
00725 if (m_useRootFilter)
00726 {
00727 timeRootFilter = -timer.GetTime();
00728 rootFilter = m_rootFilter->Get();
00729 timeRootFilter += timer.GetTime();
00730 }
00731 maxTime -= timer.GetTime();
00732 m_search.SetToPlay(toPlay);
00733 std::vector<SgPoint> sequence;
00734 SgUctEarlyAbortParam earlyAbort;
00735 earlyAbort.m_threshold = 1 - m_resignThreshold;
00736 earlyAbort.m_minGames = m_resignMinGames;
00737 earlyAbort.m_reductionFactor = 3;
00738 float value = m_search.Search(m_maxGames, maxTime, sequence, rootFilter,
00739 initTree, &earlyAbort);
00740
00741 bool wasEarlyAbort = m_search.WasEarlyAbort();
00742 std::size_t rootMoveCount = m_search.Tree().Root().MoveCount();
00743 m_mpiSynchronizer->SynchronizeSearchStatus(value, wasEarlyAbort, rootMoveCount);
00744
00745 if (m_writeDebugOutput)
00746 {
00747
00748
00749 std::ostringstream out;
00750 m_search.WriteStatistics(out);
00751 out << SgWriteLabel("Value") << std::fixed << std::setprecision(2)
00752 << value << '\n' << SgWriteLabel("Sequence")
00753 << SgWritePointList(sequence, "", false);
00754 if (m_reuseSubtree)
00755 out << SgWriteLabel("TimeInitTree") << std::fixed
00756 << std::setprecision(2) << timeInitTree << '\n';
00757 if (m_useRootFilter)
00758 out << SgWriteLabel("TimeRootFilter") << std::fixed
00759 << std::setprecision(2) << timeRootFilter << '\n';
00760 SgDebug() << out.str();
00761 }
00762
00763 if ( value < m_resignThreshold
00764 && rootMoveCount > m_resignMinGames
00765 )
00766 return SG_RESIGN;
00767
00768 SgPoint move;
00769 if (sequence.empty())
00770 move = SG_PASS;
00771 else
00772 {
00773 move = *(sequence.begin());
00774 move = GoUctSearchUtil::TrompTaylorPassCheck(move, m_search);
00775 }
00776
00777
00778
00779 if (m_earlyPass && wasEarlyAbort)
00780 {
00781 maxTime -= timer.GetTime();
00782 SgPoint earlyPassMove;
00783 if (DoEarlyPassSearch(m_maxGames / earlyAbort.m_reductionFactor,
00784 maxTime, earlyPassMove))
00785 move = earlyPassMove;
00786 }
00787
00788 m_mpiSynchronizer->SynchronizeMove(move);
00789 return move;
00790 }
00791
00792
00793
00794
00795
00796
00797
00798 template <class SEARCH, class THREAD>
00799 void GoUctPlayer<SEARCH, THREAD>::FindInitTree(SgUctTree& initTree,
00800 SgBlackWhite toPlay,
00801 double maxTime)
00802 {
00803 Board().SetToPlay(toPlay);
00804 GoBoardHistory currentPosition;
00805 currentPosition.SetFromBoard(Board());
00806 std::vector<SgPoint> sequence;
00807 if (! currentPosition.IsAlternatePlayFollowUpOf(m_search.BoardHistory(),
00808 sequence))
00809 {
00810 SgDebug() << "GoUctPlayer: No tree to reuse found\n";
00811 return;
00812 }
00813 SgUctTreeUtil::ExtractSubtree(m_search.Tree(), initTree, sequence, true,
00814 maxTime);
00815 size_t initTreeNodes = initTree.NuNodes();
00816 size_t oldTreeNodes = m_search.Tree().NuNodes();
00817 if (oldTreeNodes > 1 && initTreeNodes > 1)
00818 {
00819 float reuse = static_cast<float>(initTreeNodes) / oldTreeNodes;
00820 int reusePercent = static_cast<int>(100 * reuse);
00821 SgDebug() << "GoUctPlayer: Reusing " << initTreeNodes
00822 << " nodes (" << reusePercent << "%)\n";
00823
00824
00825 m_statistics.m_reuse.Add(reuse);
00826 }
00827 else
00828 {
00829 SgDebug() << "GoUctPlayer: Subtree to reuse has 0 nodes\n";
00830 m_statistics.m_reuse.Add(0.f);
00831 }
00832
00833
00834 for (SgUctChildIterator it(initTree, initTree.Root()); it; ++it)
00835 if (! Board().IsLegal((*it).Move()))
00836 {
00837 SgWarning() <<
00838 "GoUctPlayer: illegal move in root child of init tree\n";
00839 initTree.Clear();
00840
00841 SG_ASSERT(false);
00842 }
00843 }
00844
00845 template <class SEARCH, class THREAD>
00846 SgPoint GoUctPlayer<SEARCH, THREAD>::GenMove(const SgTimeRecord& time,
00847 SgBlackWhite toPlay)
00848 {
00849 ++m_statistics.m_nuGenMove;
00850 if (m_searchMode == GOUCT_SEARCHMODE_PLAYOUTPOLICY)
00851 return GenMovePlayoutPolicy(toPlay);
00852 const GoBoard& bd = Board();
00853 SgMove move = SG_NULLMOVE;
00854 if (m_forcedOpeningMoves)
00855 {
00856 move = GoUctUtil::GenForcedOpeningMove(bd);
00857 if (move != SG_NULLMOVE)
00858 SgDebug() << "GoUctPlayer: Forced opening move\n";
00859 }
00860 if (move == SG_NULLMOVE && GoBoardUtil::PassWins(bd, toPlay))
00861 {
00862 move = SG_PASS;
00863 SgDebug() << "GoUctPlayer: Pass wins (Tromp-Taylor rules)\n";
00864 }
00865 if (move == SG_NULLMOVE)
00866 {
00867 double maxTime;
00868 if (m_ignoreClock)
00869 maxTime = std::numeric_limits<double>::max();
00870 else
00871 maxTime = m_timeControl.TimeForCurrentMove(time,
00872 !m_writeDebugOutput);
00873 float value;
00874 if (m_searchMode == GOUCT_SEARCHMODE_ONEPLY)
00875 {
00876 m_search.SetToPlay(toPlay);
00877 move = m_search.SearchOnePly(m_maxGames, maxTime, value);
00878 if (move == SG_NULLMOVE)
00879 move = SG_PASS;
00880 else
00881 {
00882 float value = m_search.Tree().Root().Mean();
00883 if (value < m_resignThreshold)
00884 move = SG_RESIGN;
00885 }
00886 }
00887 else
00888 {
00889 SG_ASSERT(m_searchMode == GOUCT_SEARCHMODE_UCT);
00890 move = DoSearch(toPlay, maxTime, false);
00891 m_statistics.m_gamesPerSecond.Add(
00892 m_search.Statistics().m_gamesPerSecond);
00893 }
00894 }
00895 return move;
00896 }
00897
00898 template <class SEARCH, class THREAD>
00899 SgMove GoUctPlayer<SEARCH, THREAD>::GenMovePlayoutPolicy(SgBlackWhite toPlay)
00900 {
00901 GoBoard& bd = Board();
00902 GoBoardRestorer restorer(bd);
00903 bd.SetToPlay(toPlay);
00904 if (m_playoutPolicy.get() == 0)
00905 m_playoutPolicy.reset(
00906 new GoUctPlayoutPolicy<GoBoard>(bd, m_playoutPolicyParam));
00907 m_playoutPolicy->StartPlayout();
00908 SgPoint move = m_playoutPolicy->GenerateMove();
00909 m_playoutPolicy->EndPlayout();
00910 if (move == SG_NULLMOVE)
00911 {
00912 SgDebug() <<
00913 "GoUctPlayer: GoUctPlayoutPolicy generated SG_NULLMOVE\n";
00914 return SG_PASS;
00915 }
00916 return move;
00917 }
00918
00919 template <class SEARCH, class THREAD>
00920 const typename GoUctPlayer<SEARCH, THREAD>::Statistics&
00921 GoUctPlayer<SEARCH, THREAD>::GetStatistics() const
00922 {
00923 return m_statistics;
00924 }
00925
00926 template <class SEARCH, class THREAD>
00927 std::string GoUctPlayer<SEARCH, THREAD>::Name() const
00928 {
00929 return "GoUctPlayer";
00930 }
00931
00932 template <class SEARCH, class THREAD>
00933 void GoUctPlayer<SEARCH, THREAD>::OnBoardChange()
00934 {
00935 int size = Board().Size();
00936 if (m_autoParam && size != m_lastBoardSize)
00937 {
00938 SgDebug() << "GoUctPlayer: Setting default parameters for size "
00939 << size << '\n';
00940 SetDefaultParameters(size);
00941 m_search.SetDefaultParameters(size);
00942 m_lastBoardSize = size;
00943 }
00944 }
00945
00946 template <class SEARCH, class THREAD>
00947 void GoUctPlayer<SEARCH, THREAD>::Ponder()
00948 {
00949 const GoBoard& bd = Board();
00950 if (! m_enablePonder || GoBoardUtil::EndOfGame(bd)
00951 || m_searchMode != GOUCT_SEARCHMODE_UCT)
00952 return;
00953
00954
00955
00956
00957 if (bd.TotalNumStones(SG_BLACK) == 0 && bd.TotalNumStones(SG_WHITE) == 0)
00958 return;
00959 if (! m_reuseSubtree)
00960 {
00961
00962
00963 SgWarning() << "Pondering needs reuse_subtree enabled.\n";
00964 return;
00965 }
00966 SgDebug() << "GoUctPlayer::Ponder: start\n";
00967
00968 double maxTime = 3600;
00969 DoSearch(bd.ToPlay(), maxTime, true);
00970 SgDebug() << "GoUctPlayer::Ponder: end\n";
00971 }
00972
00973 template <class SEARCH, class THREAD>
00974 GoUctSearch& GoUctPlayer<SEARCH, THREAD>::Search()
00975 {
00976 return m_search;
00977 }
00978
00979 template <class SEARCH, class THREAD>
00980 const GoUctSearch& GoUctPlayer<SEARCH, THREAD>::Search() const
00981 {
00982 return m_search;
00983 }
00984
00985 template <class SEARCH, class THREAD>
00986 void GoUctPlayer<SEARCH, THREAD>::SetDefaultParameters(int boardSize)
00987 {
00988 m_timeControl.SetFastOpenMoves(0);
00989 m_timeControl.SetMinTime(0);
00990 m_timeControl.SetRemainingConstant(0.5);
00991 if (boardSize < 15)
00992 {
00993 m_resignThreshold = 0.05;
00994 }
00995 else
00996 {
00997
00998
00999 m_resignThreshold = 0.08;
01000 }
01001 }
01002
01003 template <class SEARCH, class THREAD>
01004 void GoUctPlayer<SEARCH, THREAD>::SetReuseSubtree(bool enable)
01005 {
01006 m_reuseSubtree = enable;
01007 }
01008
01009 template <class SEARCH, class THREAD>
01010 SgDefaultTimeControl& GoUctPlayer<SEARCH, THREAD>::TimeControl()
01011 {
01012 return m_timeControl;
01013 }
01014
01015 template <class SEARCH, class THREAD>
01016 const SgDefaultTimeControl& GoUctPlayer<SEARCH, THREAD>::TimeControl() const
01017 {
01018 return m_timeControl;
01019 }
01020
01021
01022
01023
01024 template <class SEARCH, class THREAD>
01025 bool GoUctPlayer<SEARCH, THREAD>::VerifyNeutralMove(size_t maxGames,
01026 double maxTime,
01027 SgPoint move)
01028 {
01029 GoBoard& bd = Board();
01030 bd.Play(move);
01031 std::vector<SgPoint> sequence;
01032 double value = m_search.Search(maxGames, maxTime, sequence);
01033 value = m_search.InverseEval(value);
01034 bd.Undo();
01035 return value >= 1 - m_resignThreshold;
01036 }
01037
01038
01039
01040 #endif // GOUCT_PLAYER_H