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