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