00001 
00002 
00003 
00004 
00005 
00006 #include "SgSystem.h"
00007 
00008 #include "BitsetIterator.hpp"
00009 #include "MoHexEngine.hpp"
00010 #include "MoHexPlayer.hpp"
00011 #include "PlayAndSolve.hpp"
00012 #include "SwapCheck.hpp"
00013 
00014 using namespace benzene;
00015 
00016 
00017 
00018 namespace {
00019 
00020 std::string KnowledgeThresholdToString(const std::vector<SgUctValue>& t)
00021 {
00022     if (t.empty())
00023         return "0";
00024     std::ostringstream os;
00025     os << '\"';
00026     for (std::size_t i = 0; i < t.size(); ++i)
00027     {
00028         if (i > 0) 
00029             os << ' ';
00030         os << t[i];
00031     }
00032     os << '\"';
00033     return os.str();
00034 }
00035 
00036 std::vector<SgUctValue> KnowledgeThresholdFromString(const std::string& val)
00037 {
00038     std::vector<SgUctValue> v;
00039     std::istringstream is(val);
00040     SgUctValue t;
00041     while (is >> t)
00042         v.push_back(t);
00043     if (v.size() == 1 && v[0] == 0)
00044         v.clear();
00045     return v;
00046 }
00047 
00048 }
00049 
00050 
00051 
00052 MoHexEngine::MoHexEngine(int boardsize, MoHexPlayer& player)
00053     : BenzeneHtpEngine(boardsize),
00054       m_player(player), 
00055       m_book(0),
00056       m_bookCheck(m_book),
00057       m_bookCommands(m_game, m_pe, m_book, m_bookCheck, m_player)
00058 {
00059     m_bookCommands.Register(*this);
00060     RegisterCmd("param_mohex", &MoHexEngine::MoHexParam);
00061     RegisterCmd("param_mohex_policy", &MoHexEngine::MoHexPolicyParam);
00062     RegisterCmd("mohex-save-tree", &MoHexEngine::SaveTree);
00063     RegisterCmd("mohex-save-games", &MoHexEngine::SaveGames);
00064     RegisterCmd("mohex-values", &MoHexEngine::Values);
00065     RegisterCmd("mohex-rave-values", &MoHexEngine::RaveValues);
00066     RegisterCmd("mohex-bounds", &MoHexEngine::Bounds);
00067 }
00068 
00069 MoHexEngine::~MoHexEngine()
00070 {
00071 }
00072 
00073 
00074 
00075 void MoHexEngine::RegisterCmd(const std::string& name,
00076                               GtpCallback<MoHexEngine>::Method method)
00077 {
00078     Register(name, new GtpCallback<MoHexEngine>(this, method));
00079 }
00080 
00081 double MoHexEngine::TimeForMove(HexColor color)
00082 {
00083     if (m_player.UseTimeManagement())
00084         return m_game.TimeRemaining(color) * 0.08;
00085     return m_player.MaxTime();
00086 }
00087 
00088 HexPoint MoHexEngine::GenMove(HexColor color, bool useGameClock)
00089 {
00090     SG_UNUSED(useGameClock);
00091     if (SwapCheck::PlaySwap(m_game, color))
00092         return SWAP_PIECES;
00093     HexPoint bookMove = m_bookCheck.BestMove(HexState(m_game.Board(), color));
00094     if (bookMove != INVALID_POINT)
00095         return bookMove;
00096     double maxTime = TimeForMove(color);
00097     return DoSearch(color, maxTime);
00098 }
00099 
00100 HexPoint MoHexEngine::DoSearch(HexColor color, double maxTime)
00101 {
00102     HexState state(m_game.Board(), color);
00103     if (m_useParallelSolver)
00104     {
00105         PlayAndSolve ps(*m_pe.brd, *m_se.brd, m_player, m_dfpnSolver, 
00106                         m_dfpnPositions, m_game);
00107         return ps.GenMove(state, maxTime);
00108     }
00109     else
00110     {
00111         double score;
00112         return m_player.GenMove(state, m_game, m_pe.SyncBoard(m_game.Board()),
00113                                 maxTime, score);
00114     }
00115 }
00116 
00117 
00118 
00119 void MoHexEngine::MoHexPolicyParam(HtpCommand& cmd)
00120 {
00121     HexUctPolicyConfig& config = m_player.SharedPolicy().Config();
00122     if (cmd.NuArg() == 0)
00123     {
00124         cmd << '\n'
00125             << "pattern_check_percent "
00126             << config.pattern_check_percent << '\n'
00127             << "pattern_heuristic "
00128             << config.patternHeuristic << '\n'
00129             << "response_heuristic "
00130             << config.responseHeuristic << '\n'
00131             << "response_threshold "
00132             << config.response_threshold << '\n';
00133     }
00134     else if (cmd.NuArg() == 2)
00135     {
00136         std::string name = cmd.Arg(0);
00137         if (name == "pattern_check_percent")
00138             config.pattern_check_percent = cmd.IntArg(1, 0, 100);
00139         else if (name == "pattern_heuristic")
00140             config.patternHeuristic = cmd.BoolArg(1);
00141         else if (name == "response_heuristic")
00142             config.responseHeuristic = cmd.BoolArg(1);
00143         else if (name == "response_threshold")
00144             config.response_threshold = cmd.SizeTypeArg(1, 0);
00145         else
00146             throw HtpFailure("Unknown option!");
00147     }
00148     else
00149         throw HtpFailure("Expected 0 or 2 arguments!");
00150 }
00151 
00152 void MoHexEngine::MoHexParam(HtpCommand& cmd)
00153 {
00154     HexUctSearch& search = m_player.Search();
00155 
00156     if (cmd.NuArg() == 0) 
00157     {
00158         cmd << '\n'
00159             << "[bool] backup_ice_info "
00160             << m_player.BackupIceInfo() << '\n'
00161             << "[bool] lock_free " 
00162             << search.LockFree() << '\n'
00163             << "[bool] keep_games "
00164             << search.KeepGames() << '\n'
00165             << "[bool] perform_pre_search " 
00166             << m_player.PerformPreSearch() << '\n'
00167             << "[bool] ponder "
00168             << m_player.Ponder() << '\n'
00169             << "[bool] reuse_subtree " 
00170             << m_player.ReuseSubtree() << '\n'
00171             << "[bool] search_singleton "
00172             << m_player.SearchSingleton() << '\n'
00173             << "[bool] use_livegfx "
00174             << search.LiveGfx() << '\n'
00175             << "[bool] use_parallel_solver "
00176             << m_useParallelSolver << '\n'
00177             << "[bool] use_rave "
00178             << search.Rave() << '\n'
00179             << "[bool] use_time_management "
00180             << m_player.UseTimeManagement() << '\n'
00181             << "[bool] weight_rave_updates "
00182             << search.WeightRaveUpdates() << '\n'
00183             << "[bool] virtual_loss "
00184             << search.VirtualLoss() << '\n'
00185             << "[string] bias_term "
00186             << search.BiasTermConstant() << '\n'
00187             << "[string] expand_threshold "
00188             << search.ExpandThreshold() << '\n'
00189             << "[string] knowledge_threshold "
00190             << KnowledgeThresholdToString(search.KnowledgeThreshold()) << '\n'
00191             << "[string] livegfx_interval "
00192             << search.LiveGfxInterval() << '\n'
00193             << "[string] max_games "
00194             << m_player.MaxGames() << '\n'
00195             << "[string] max_memory "
00196             << search.MaxNodes() * 2 * sizeof(SgUctNode) << '\n'
00197             << "[string] max_nodes "
00198             << search.MaxNodes() << '\n'
00199             << "[string] max_time "
00200             << m_player.MaxTime() << '\n'
00201             << "[string] num_threads "
00202             << search.NumberThreads() << '\n'
00203             << "[string] playout_update_radius "
00204             << search.PlayoutUpdateRadius() << '\n'
00205             << "[string] randomize_rave_frequency "
00206             << search.RandomizeRaveFrequency() << '\n'
00207             << "[string] rave_weight_final "
00208             << search.RaveWeightFinal() << '\n'
00209             << "[string] rave_weight_initial "
00210             << search.RaveWeightInitial() << '\n'
00211             << "[string] tree_update_radius " 
00212             << search.TreeUpdateRadius() << '\n';
00213     }
00214     else if (cmd.NuArg() == 2)
00215     {
00216         std::string name = cmd.Arg(0);
00217         if (name == "backup_ice_info")
00218             m_player.SetBackupIceInfo(cmd.BoolArg(1));
00219         else if (name == "lock_free")
00220             search.SetLockFree(cmd.BoolArg(1));
00221         else if (name == "keep_games")
00222             search.SetKeepGames(cmd.BoolArg(1));
00223         else if (name == "perform_pre_search")
00224             m_player.SetPerformPreSearch(cmd.BoolArg(1));
00225         else if (name == "ponder")
00226             m_player.SetPonder(cmd.BoolArg(1));
00227         else if (name == "use_livegfx")
00228             search.SetLiveGfx(cmd.BoolArg(1));
00229         else if (name == "use_rave")
00230             search.SetRave(cmd.BoolArg(1));
00231         else if (name == "randomize_rave_frequency")
00232             search.SetRandomizeRaveFrequency(cmd.IntArg(1, 0));
00233         else if (name == "reuse_subtree")
00234            m_player.SetReuseSubtree(cmd.BoolArg(1));
00235         else if (name == "bias_term")
00236             search.SetBiasTermConstant(cmd.FloatArg(1));
00237         else if (name == "expand_threshold")
00238             search.SetExpandThreshold(cmd.IntArg(1, 0));
00239         else if (name == "knowledge_threshold")
00240             search.SetKnowledgeThreshold
00241                 (KnowledgeThresholdFromString(cmd.Arg(1)));
00242         else if (name == "livegfx_interval")
00243             search.SetLiveGfxInterval(cmd.IntArg(1, 0));
00244         else if (name == "max_games")
00245             m_player.SetMaxGames(cmd.IntArg(1, 0));
00246         else if (name == "max_memory")
00247             search.SetMaxNodes(cmd.SizeTypeArg(1, 1) / sizeof(SgUctNode) / 2);
00248         else if (name == "max_time")
00249             m_player.SetMaxTime(cmd.FloatArg(1));
00250         else if (name == "max_nodes")
00251             search.SetMaxNodes(cmd.SizeTypeArg(1, 1));
00252         else if (name == "num_threads")
00253             search.SetNumberThreads(cmd.IntArg(1, 0));
00254         else if (name == "playout_update_radius")
00255             search.SetPlayoutUpdateRadius(cmd.IntArg(1, 0));
00256         else if (name == "rave_weight_final")
00257             search.SetRaveWeightFinal(cmd.IntArg(1, 0));
00258         else if (name == "rave_weight_initial")
00259             search.SetRaveWeightInitial(cmd.IntArg(1, 0));
00260         else if (name == "weight_rave_updates")
00261             search.SetWeightRaveUpdates(cmd.BoolArg(1));
00262         else if (name == "tree_update_radius")
00263             search.SetTreeUpdateRadius(cmd.IntArg(1, 0));
00264         else if (name == "search_singleton")
00265             m_player.SetSearchSingleton(cmd.BoolArg(1));
00266         else if (name == "use_parallel_solver")
00267             m_useParallelSolver = cmd.BoolArg(1);
00268         else if (name == "use_time_management")
00269             m_player.SetUseTimeManagement(cmd.BoolArg(1));
00270         else if (name == "virtual_loss")
00271             search.SetVirtualLoss(cmd.BoolArg(1));
00272         else
00273             throw HtpFailure() << "Unknown parameter: " << name;
00274     }
00275     else 
00276         throw HtpFailure("Expected 0 or 2 arguments");
00277 }
00278 
00279 
00280 
00281 
00282 
00283 void MoHexEngine::SaveTree(HtpCommand& cmd)
00284 {
00285     HexUctSearch& search = m_player.Search();
00286 
00287     cmd.CheckNuArg(1);
00288     std::string filename = cmd.Arg(0);
00289     int maxDepth = -1;
00290     std::ofstream file(filename.c_str());
00291     if (!file)
00292         throw HtpFailure() << "Could not open '" << filename << "'";
00293     if (cmd.NuArg() == 2)
00294         maxDepth = cmd.IntArg(1, 0);
00295     search.SaveTree(file, maxDepth);
00296 }
00297 
00298 
00299 void MoHexEngine::SaveGames(HtpCommand& cmd)
00300 {
00301     HexUctSearch& search = m_player.Search();
00302     cmd.CheckNuArg(1);
00303     std::string filename = cmd.Arg(0);
00304     search.SaveGames(filename);
00305 }
00306 
00307 void MoHexEngine::Values(HtpCommand& cmd)
00308 {
00309     HexUctSearch& search = m_player.Search();
00310     const SgUctTree& tree = search.Tree();
00311     for (SgUctChildIterator it(tree, tree.Root()); it; ++it)
00312     {
00313         const SgUctNode& child = *it;
00314         SgPoint p = child.Move();
00315         std::size_t count = child.MoveCount();
00316         float mean = 0.0;
00317         if (count > 0)
00318             mean = child.Mean();
00319         cmd << ' ' << static_cast<HexPoint>(p)
00320             << ' ' << std::fixed << std::setprecision(3) << mean
00321             << '@' << count;
00322     }
00323 }
00324 
00325 void MoHexEngine::RaveValues(HtpCommand& cmd)
00326 {
00327     HexUctSearch& search = m_player.Search();
00328     const SgUctTree& tree = search.Tree();
00329     for (SgUctChildIterator it(tree, tree.Root()); it; ++it)
00330     {
00331         const SgUctNode& child = *it;
00332         SgPoint p = child.Move();
00333         if (p == SG_PASS || ! child.HasRaveValue())
00334             continue;
00335         cmd << ' ' << static_cast<HexPoint>(p)
00336             << ' ' << std::fixed << std::setprecision(3) << child.RaveValue();
00337     }
00338 }
00339 
00340 void MoHexEngine::Bounds(HtpCommand& cmd)
00341 {
00342     HexUctSearch& search = m_player.Search();
00343     const SgUctTree& tree = search.Tree();
00344     for (SgUctChildIterator it(tree, tree.Root()); it; ++it)
00345     {
00346         const SgUctNode& child = *it;
00347         SgPoint p = child.Move();
00348         if (p == SG_PASS || ! child.HasRaveValue())
00349             continue;
00350         float bound = search.GetBound(search.Rave(), tree.Root(), child);
00351         cmd << ' ' << static_cast<HexPoint>(p) 
00352             << ' ' << std::fixed << std::setprecision(3) << bound;
00353     }
00354 }
00355 
00356 
00357 
00358 
00359 #if GTPENGINE_PONDER
00360 
00361 void MoHexEngine::InitPonder()
00362 {
00363     SgSetUserAbort(false);
00364 }
00365 
00366 void MoHexEngine::Ponder()
00367 {
00368     if (!m_player.Ponder())
00369         return;
00370     if (!m_player.ReuseSubtree())
00371     {
00372         LogWarning() << "Pondering requires reuse_subtree.\n";
00373         return;
00374     }
00375     
00376     
00377     boost::xtime time;
00378     boost::xtime_get(&time, boost::TIME_UTC);
00379     for (int i = 0; i < 200; ++i)
00380     {
00381         if (SgUserAbort())
00382             return;
00383         time.nsec += 1000000; 
00384         boost::thread::sleep(time);
00385     }
00386     LogInfo() << "MoHexEngine::Ponder: start\n";
00387     
00388     DoSearch(m_game.Board().WhoseTurn(), 600);
00389 }
00390 
00391 void MoHexEngine::StopPonder()
00392 {
00393     SgSetUserAbort(true);
00394 }
00395 
00396 #endif // GTPENGINE_PONDER
00397 
00398 
00399