00001
00002
00003
00004
00005
00006 #include "SgSystem.h"
00007 #include "GoGtpEngine.h"
00008
00009 #include <algorithm>
00010 #include <cmath>
00011 #include <exception>
00012 #include <fstream>
00013 #include <iomanip>
00014 #include <limits>
00015 #include <time.h>
00016 #include <boost/filesystem/operations.hpp>
00017 #include "GoEyeUtil.h"
00018 #include "GoGtpCommandUtil.h"
00019 #include "GoModBoard.h"
00020 #include "GoNodeUtil.h"
00021 #include "GoPlayer.h"
00022 #include "GoTimeControl.h"
00023 #include "GoUtil.h"
00024 #include "SgDebug.h"
00025 #include "SgEBWArray.h"
00026 #include "SgException.h"
00027 #include "SgGameReader.h"
00028 #include "SgGameWriter.h"
00029 #include "SgPointSetUtil.h"
00030 #include "SgTime.h"
00031 #include "SgWrite.h"
00032
00033 #if GTPENGINE_PONDER
00034 #include <boost/thread/thread.hpp>
00035 #include <boost/thread/xtime.hpp>
00036 #endif
00037
00038 using namespace std;
00039 using boost::filesystem::exists;
00040 using boost::filesystem::path;
00041 using boost::filesystem::remove;
00042 using SgPointUtil::Pt;
00043
00044
00045
00046 namespace {
00047
00048 GoRules::KoRule KoRuleArg(GtpCommand& cmd, size_t number)
00049 {
00050 string arg = cmd.ArgToLower(number);
00051 if (arg == "simple")
00052 return GoRules::SIMPLEKO;
00053 if (arg == "superko")
00054 return GoRules::SUPERKO;
00055 if (arg == "pos_superko")
00056 return GoRules::POS_SUPERKO;
00057 throw GtpFailure() << "unknown ko rule \"" << arg << '"';
00058 }
00059
00060 string KoRuleToString(GoRules::KoRule rule)
00061 {
00062 switch (rule)
00063 {
00064 case GoRules::SIMPLEKO:
00065 return "simple";
00066 case GoRules::SUPERKO:
00067 return "superko";
00068 case GoRules::POS_SUPERKO:
00069 return "pos_superko";
00070 default:
00071 SG_ASSERT(false);
00072 return "?";
00073 }
00074 }
00075
00076 }
00077
00078
00079
00080 GoGtpEngine::GoGtpEngine(int fixedBoardSize, const char* programPath,
00081 bool noPlayer, bool noHandicap)
00082 : m_player(0),
00083 m_autoBook(0),
00084 m_noPlayer(noPlayer),
00085 m_acceptIllegal(false),
00086 m_autoSave(false),
00087 m_autoShowBoard(false),
00088 m_debugToComment(false),
00089 m_useBook(true),
00090 m_isPonderPosition(true),
00091 m_fixedBoardSize(fixedBoardSize),
00092 m_maxClearBoard(-1),
00093 m_numberClearBoard(0),
00094 m_timeLastMove(0),
00095 m_timeLimit(10),
00096 m_overhead(0),
00097 m_game(fixedBoardSize > 0 ? fixedBoardSize : GO_DEFAULT_SIZE),
00098 m_sgCommands(*this, programPath),
00099 m_bookCommands(*this, Board(), m_book),
00100 m_mpiSynchronizer(SgMpiNullSynchronizer::Create())
00101 {
00102 Init(Board().Size());
00103
00104 Register("all_legal", &GoGtpEngine::CmdAllLegal, this);
00105 Register("boardsize", &GoGtpEngine::CmdBoardSize, this);
00106 Register("clear_board", &GoGtpEngine::CmdClearBoard, this);
00107 Register("cgos-gameover", &GoGtpEngine::CmdGameOver, this);
00108 Register("get_komi", &GoGtpEngine::CmdGetKomi, this);
00109 Register("gg-undo", &GoGtpEngine::CmdGGUndo, this);
00110 Register("go_board", &GoGtpEngine::CmdBoard, this);
00111 Register("go_param", &GoGtpEngine::CmdParam, this);
00112 Register("go_param_rules", &GoGtpEngine::CmdParamRules, this);
00113 Register("go_player_board", &GoGtpEngine::CmdPlayerBoard, this);
00114 Register("go_point_info", &GoGtpEngine::CmdPointInfo, this);
00115 Register("go_point_numbers", &GoGtpEngine::CmdPointNumbers, this);
00116 Register("go_rules", &GoGtpEngine::CmdRules, this);
00117 Register("go_sentinel_file", &GoGtpEngine::CmdSentinelFile, this);
00118 Register("go_set_info", &GoGtpEngine::CmdSetInfo, this);
00119 Register("gogui-analyze_commands", &GoGtpEngine::CmdAnalyzeCommands,
00120 this);
00121 Register("gogui-interrupt", &GoGtpEngine::CmdInterrupt, this);
00122 Register("gogui-play_sequence", &GoGtpEngine::CmdPlaySequence, this);
00123 Register("gogui-setup", &GoGtpEngine::CmdSetup, this);
00124 Register("gogui-setup_player", &GoGtpEngine::CmdSetupPlayer, this);
00125 Register("is_legal", &GoGtpEngine::CmdIsLegal, this);
00126 Register("kgs-genmove_cleanup", &GoGtpEngine::CmdGenMoveCleanup, this);
00127 Register("kgs-time_settings", &GoGtpEngine::CmdKgsTimeSettings, this);
00128 Register("komi", &GoGtpEngine::CmdKomi, this);
00129 Register("list_stones", &GoGtpEngine::CmdListStones, this);
00130 Register("loadsgf", &GoGtpEngine::CmdLoadSgf, this);
00131 Register("play", &GoGtpEngine::CmdPlay, this);
00132 Register("savesgf", &GoGtpEngine::CmdSaveSgf, this);
00133 Register("showboard", &GoGtpEngine::CmdShowBoard, this);
00134 Register("time_left", &GoGtpEngine::CmdTimeLeft, this);
00135 Register("time_settings", &GoGtpEngine::CmdTimeSettings, this);
00136 Register("undo", &GoGtpEngine::CmdUndo, this);
00137 m_sgCommands.Register(*this);
00138 if (! noPlayer)
00139 {
00140 Register("all_move_values", &GoGtpEngine::CmdAllMoveValues, this);
00141 Register("final_score", &GoGtpEngine::CmdFinalScore, this);
00142 Register("genmove", &GoGtpEngine::CmdGenMove, this);
00143 Register("go_clock", &GoGtpEngine::CmdClock, this);
00144 Register("go_param_timecontrol", &GoGtpEngine::CmdParamTimecontrol,
00145 this);
00146 Register("reg_genmove", &GoGtpEngine::CmdRegGenMove, this);
00147 Register("reg_genmove_toplay", &GoGtpEngine::CmdRegGenMoveToPlay,
00148 this);
00149 Register("time_lastmove", &GoGtpEngine::CmdTimeLastMove, this);
00150 m_bookCommands.Register(*this);
00151 }
00152 if (! noHandicap)
00153 {
00154 Register("fixed_handicap", &GoGtpEngine::CmdFixedHandicap, this);
00155 Register("place_free_handicap", &GoGtpEngine::CmdPlaceFreeHandicap,
00156 this);
00157 Register("set_free_handicap", &GoGtpEngine::CmdSetFreeHandicap, this);
00158 }
00159 }
00160
00161 GoGtpEngine::~GoGtpEngine()
00162 {
00163 delete m_player;
00164 }
00165
00166 void GoGtpEngine::AddPlayStatistics()
00167 {
00168
00169 }
00170
00171 void GoGtpEngine::AddStatistics(const std::string& key,
00172 const std::string& value)
00173 {
00174 SG_ASSERT(m_statisticsValues.size() == m_statisticsSlots.size());
00175 if (value.find('\t') != string::npos)
00176 throw SgException("GoGtpEngine::AddStatistics: value contains tab: '"
00177 + value + "'");
00178 for (size_t i = 0; i < m_statisticsSlots.size(); ++i)
00179 if (m_statisticsSlots[i] == key)
00180 {
00181 m_statisticsValues[i] = value;
00182 return;
00183 }
00184 throw SgException("GoGtpEngine::AddStatistics: invalid key '" + key
00185 + "'");
00186 }
00187
00188 void GoGtpEngine::ApplyTimeSettings()
00189 {
00190 SG_ASSERT(Board().MoveNumber() == 0);
00191 m_game.SetTimeSettingsGlobal(m_timeSettings);
00192 }
00193
00194 void GoGtpEngine::AutoSave() const
00195 {
00196 if (! m_autoSave)
00197 return;
00198 try
00199 {
00200 SaveGame(m_autoSaveFileName);
00201 }
00202 catch (const GtpFailure& failure)
00203 {
00204 SgWarning() << failure.Response() << '\n';
00205 }
00206 }
00207
00208 void GoGtpEngine::BoardChanged()
00209 {
00210 const GoBoard& bd = Board();
00211 if (m_autoShowBoard)
00212 SgDebug() << bd;
00213 if (m_player != 0)
00214 m_player->UpdateSubscriber();
00215 AutoSave();
00216 m_isPonderPosition = (! GoBoardUtil::IsBoardEmpty(bd)
00217 && ! GoBoardUtil::EndOfGame(bd)
00218 && GenBookMove(bd.ToPlay()) == SG_NULLMOVE);
00219 }
00220
00221 void GoGtpEngine::BeforeHandleCommand()
00222 {
00223 SgSetUserAbort(false);
00224 SgDebug() << flush;
00225 }
00226
00227 void GoGtpEngine::BeforeWritingResponse()
00228 {
00229 SgDebug() << flush;
00230 }
00231
00232 void GoGtpEngine::CheckBoardEmpty() const
00233 {
00234 if (! GoBoardUtil::IsBoardEmpty(Board()))
00235 throw GtpFailure("board is not empty");
00236 }
00237
00238
00239
00240
00241
00242
00243
00244
00245 void GoGtpEngine::CheckLegal(string message, SgBlackWhite color, SgPoint move,
00246 bool checkOnlyOccupied)
00247 {
00248 GoModBoard modBoard(Board());
00249 GoBoard& bd = modBoard.Board();
00250 bool illegal = false;
00251 string reason = "";
00252 if (move != SG_PASS)
00253 {
00254 if (bd.Occupied(move))
00255 {
00256 illegal = true;
00257 reason = " (occupied)";
00258 }
00259 else if (! checkOnlyOccupied)
00260 {
00261 bd.Play(move, color);
00262 GoMoveInfo moveInfo = bd.GetLastMoveInfo();
00263 bd.Undo();
00264 if (moveInfo.test(GO_MOVEFLAG_ILLEGAL))
00265 {
00266 illegal = true;
00267 if (moveInfo.test(GO_MOVEFLAG_SUICIDE))
00268 reason = " (suicide)";
00269 else if (moveInfo.test(GO_MOVEFLAG_REPETITION))
00270 {
00271 reason =
00272 " (" + KoRuleToString(bd.Rules().GetKoRule()) + ")";
00273 }
00274 }
00275 }
00276 }
00277 if (illegal)
00278 {
00279 int moveNumber = m_game.CurrentMoveNumber() + 1;
00280 throw GtpFailure() << message << moveNumber << ' ' << SgBW(color)
00281 << ' ' << SgWritePoint(move) << reason;
00282 }
00283 }
00284
00285 void GoGtpEngine::CheckMaxClearBoard()
00286 {
00287 if (m_maxClearBoard >= 0 && m_numberClearBoard > m_maxClearBoard - 1)
00288 throw GtpFailure() << "maximum number of " << m_maxClearBoard
00289 << " reached";
00290 ++m_numberClearBoard;
00291 }
00292
00293
00294
00295
00296 void GoGtpEngine::CmdAllLegal(GtpCommand& cmd)
00297 {
00298 cmd.CheckNuArg(1);
00299 SgBlackWhite color = BlackWhiteArg(cmd, 0);
00300 SgVector<SgPoint> allLegal;
00301 for (GoBoard::Iterator p(Board()); p; ++p)
00302 if (Board().IsLegal(*p, color))
00303 allLegal.PushBack(*p);
00304 cmd << SgWritePointList(allLegal, "", false);
00305 }
00306
00307
00308 void GoGtpEngine::CmdAllMoveValues(GtpCommand& cmd)
00309 {
00310 cmd.CheckArgNone();
00311 const GoBoard& bd = Board();
00312 GoPlayer& player = Player();
00313 for (GoBoard::Iterator it(bd); it; ++it)
00314 if (! bd.Occupied(*it))
00315 {
00316 int value = player.MoveValue(*it);
00317 if (value > numeric_limits<int>::min())
00318 cmd << SgWritePoint(*it) << ' ' << value << '\n';
00319 }
00320 }
00321
00322
00323
00324 void GoGtpEngine::CmdAnalyzeCommands(GtpCommand& cmd)
00325 {
00326 cmd.CheckArgNone();
00327 cmd <<
00328 "hpstring/Go Board/go_board\n"
00329 "param/Go Param/go_param\n"
00330 "param/Go Param Rules/go_param_rules\n"
00331 "hpstring/Go Point Info/go_point_info %p\n"
00332 "sboard/Go Point Numbers/go_point_numbers\n"
00333 "none/Go Rules/go_rules %s\n"
00334 "plist/All Legal/all_legal %c\n"
00335 "string/ShowBoard/showboard\n"
00336 "string/CpuTime/cputime\n"
00337 "string/Get Komi/get_komi\n"
00338 "string/Get Random Seed/get_random_seed\n"
00339 "plist/List Stones/list_stones %c\n"
00340 "none/Set Random Seed/set_random_seed %s\n"
00341 "none/SaveSgf/savesgf %w\n";
00342 m_sgCommands.AddGoGuiAnalyzeCommands(cmd);
00343 if (! m_noPlayer)
00344 {
00345 m_bookCommands.AddGoGuiAnalyzeCommands(cmd);
00346 cmd <<
00347 "pspairs/All Move Values/all_move_values\n"
00348 "string/Final Score/final_score\n"
00349 "param/Go Param TimeControl/go_param_timecontrol\n"
00350 "varc/Reg GenMove/reg_genmove %c\n";
00351 }
00352 }
00353
00354
00355
00356 void GoGtpEngine::CmdBoard(GtpCommand& cmd)
00357 {
00358 WriteBoardInfo(cmd, Board());
00359 }
00360
00361
00362 void GoGtpEngine::CmdBoardSize(GtpCommand& cmd)
00363 {
00364 cmd.CheckNuArg(1);
00365 int size = cmd.ArgMinMax<int>(0, SG_MIN_SIZE, SG_MAX_SIZE);
00366 if (m_fixedBoardSize > 0 && size != m_fixedBoardSize)
00367 throw GtpFailure() << "Boardsize " << m_fixedBoardSize << " fixed";
00368 if (Board().MoveNumber() > 0)
00369 GameFinished();
00370 Init(size);
00371 }
00372
00373
00374
00375 void GoGtpEngine::CmdClearBoard(GtpCommand& cmd)
00376 {
00377 cmd.CheckArgNone();
00378 CheckMaxClearBoard();
00379 if (! m_sentinelFile.empty() && exists(m_sentinelFile))
00380 throw GtpFailure() << "Detected sentinel file '"
00381 << m_sentinelFile.native_file_string() << "'";
00382 if (Board().MoveNumber() > 0)
00383 GameFinished();
00384 Init(Board().Size());
00385 if (m_player != 0)
00386 m_player->OnNewGame();
00387 BoardChanged();
00388 }
00389
00390
00391 void GoGtpEngine::CmdClock(GtpCommand& cmd)
00392 {
00393 cmd.CheckArgNone();
00394 cmd << '\n' << m_game.Time();
00395 }
00396
00397
00398
00399
00400
00401
00402 void GoGtpEngine::CmdFinalScore(GtpCommand& cmd)
00403 {
00404 cmd.CheckArgNone();
00405 const GoBoard& bd = Board();
00406 if (! bd.Rules().CaptureDead() || bd.Rules().JapaneseScoring())
00407 throw GtpFailure("can only score if Tromp-Taylor rules");
00408 float komi = bd.Rules().Komi().ToFloat();
00409 float score = GoBoardUtil::TrompTaylorScore(bd, komi);
00410 cmd << GoUtil::ScoreToString(score);
00411 }
00412
00413
00414 void GoGtpEngine::CmdFixedHandicap(GtpCommand& cmd)
00415 {
00416 int n = cmd.ArgMin<int>(0, 2);
00417 int size = Board().Size();
00418 SgVector<SgPoint> stones = GoGtpCommandUtil::GetHandicapStones(size, n);
00419 PlaceHandicap(stones);
00420 }
00421
00422
00423
00424
00425
00426 void GoGtpEngine::CmdGameOver(GtpCommand& cmd)
00427 {
00428 cmd.CheckNuArg(1);
00429 string result = cmd.Arg(0);
00430 m_game.UpdateResult(result);
00431 m_isPonderPosition = false;
00432 AutoSave();
00433 }
00434
00435
00436 void GoGtpEngine::CmdGenMove(GtpCommand& cmd)
00437 {
00438 cmd.CheckNuArg(1);
00439 SgBlackWhite color = BlackWhiteArg(cmd, 0);
00440 auto_ptr<SgDebugToString> debugStrToString;
00441 if (m_debugToComment)
00442 debugStrToString.reset(new SgDebugToString(true));
00443 SgPoint move = GenMove(color, false);
00444 if (move == SG_RESIGN)
00445 {
00446 cmd << "resign";
00447 const SgNode& node = m_game.AddResignNode(color);
00448 if (debugStrToString.get() != 0)
00449 {
00450 m_game.AddComment(node, "\n\n");
00451 m_game.AddComment(node, debugStrToString->GetString());
00452 }
00453 AutoSave();
00454 }
00455 else
00456 {
00457 m_game.AddMove(move, color);
00458 if (debugStrToString.get() != 0)
00459 m_game.AddComment(debugStrToString->GetString());
00460 BoardChanged();
00461 cmd << SgWritePoint(move);
00462 }
00463
00464
00465 if (m_game.GetPlayerName(color).empty())
00466 m_game.UpdatePlayerName(color, Player().Name());
00467 }
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479 void GoGtpEngine::CmdGenMoveCleanup(GtpCommand& cmd)
00480 {
00481 GoRules rules = Board().Rules();
00482 bool oldCaptureDead = rules.CaptureDead();
00483 rules.SetCaptureDead(true);
00484 m_game.SetRulesGlobal(rules);
00485 RulesChanged();
00486 try
00487 {
00488 CmdGenMove(cmd);
00489 }
00490 catch (const GtpFailure& failure)
00491 {
00492 rules.SetCaptureDead(oldCaptureDead);
00493 m_game.SetRulesGlobal(rules);
00494 RulesChanged();
00495 throw failure;
00496 }
00497 rules.SetCaptureDead(oldCaptureDead);
00498 m_game.SetRulesGlobal(rules);
00499 RulesChanged();
00500 }
00501
00502
00503
00504 void GoGtpEngine::CmdGetKomi(GtpCommand& cmd)
00505 {
00506 cmd.CheckArgNone();
00507 cmd << Board().Rules().Komi();
00508 }
00509
00510
00511
00512
00513
00514
00515 void GoGtpEngine::CmdGGUndo(GtpCommand& cmd)
00516 {
00517 cmd.CheckNuArgLessEqual(1);
00518 Undo(cmd.NuArg() == 0 ? 0 : cmd.ArgMin<int>(0, 0));
00519 BoardChanged();
00520 }
00521
00522
00523
00524
00525
00526 void GoGtpEngine::CmdKgsTimeSettings(GtpCommand& cmd)
00527 {
00528 if (cmd.NuArg() < 1)
00529 throw GtpFailure("Need at least one argument!");
00530 if (Board().MoveNumber() > 0)
00531 throw GtpFailure("cannot change time settings during game");
00532 std::string type = cmd.Arg(0);
00533 if (type == "none")
00534 {
00535 cmd.CheckNuArg(1);
00536 m_timeSettings = GoTimeSettings();
00537 ApplyTimeSettings();
00538 }
00539 else if (type == "absolute")
00540 {
00541 cmd.CheckNuArg(2);
00542 int mainTime = cmd.ArgMin<int>(1, 0);
00543 GoTimeSettings timeSettings(mainTime);
00544 if (m_timeSettings == timeSettings)
00545 return;
00546 m_timeSettings = timeSettings;
00547 ApplyTimeSettings();
00548 }
00549 else if (type == "byoyomi")
00550 {
00551 cmd.CheckNuArg(4);
00552
00553 int mainTime = cmd.ArgMin<int>(1, 0);
00554 int overtime = cmd.ArgMin<int>(2, 0);
00555
00556 GoTimeSettings timeSettings(mainTime, overtime, 1);
00557 if (m_timeSettings == timeSettings)
00558 return;
00559 m_timeSettings = timeSettings;
00560 ApplyTimeSettings();
00561 }
00562 else if (type == "canadian")
00563 {
00564 cmd.CheckNuArg(4);
00565 int mainTime = cmd.ArgMin<int>(1, 0);
00566 int overtime = cmd.ArgMin<int>(2, 0);
00567 int overtimeMoves = cmd.ArgMin<int>(3, 0);
00568 GoTimeSettings timeSettings(mainTime, overtime, overtimeMoves);
00569 if (m_timeSettings == timeSettings)
00570 return;
00571 m_timeSettings = timeSettings;
00572 ApplyTimeSettings();
00573 }
00574 else
00575 throw GtpFailure("Unknown type of time control");
00576 }
00577
00578
00579
00580
00581
00582
00583 void GoGtpEngine::CmdInterrupt(GtpCommand& cmd)
00584 {
00585 cmd.CheckArgNone();
00586 }
00587
00588
00589
00590
00591
00592 void GoGtpEngine::CmdIsLegal(GtpCommand& cmd)
00593 {
00594 cmd.CheckNuArg(2);
00595 SgBlackWhite color = BlackWhiteArg(cmd, 0);
00596 SgPoint move = MoveArg(cmd, 1);
00597 cmd << SgWriteBoolAsInt(Board().IsLegal(move, color));
00598 }
00599
00600
00601
00602 void GoGtpEngine::CmdKomi(GtpCommand& cmd)
00603 {
00604 try
00605 {
00606 GoKomi komi(cmd.Arg());
00607 m_game.SetKomiGlobal(komi);
00608 m_defaultRules.SetKomi(komi);
00609 RulesChanged();
00610 }
00611 catch (const GoKomi::InvalidKomi& e)
00612 {
00613 throw GtpFailure(e.what());
00614 }
00615 }
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625 void GoGtpEngine::CmdListStones(GtpCommand& cmd)
00626 {
00627 cmd.CheckNuArg(1);
00628 SgBlackWhite color = BlackWhiteArg(cmd, 0);
00629 const GoBoard& bd = Board();
00630 const SgPointSet& points = bd.All(color);
00631 bool isFirst = true;
00632 for (int row = bd.Size(); row >= 1; --row)
00633 for (int col = 1; col <= bd.Size();++col)
00634 {
00635 SgPoint p = Pt(col, row);
00636 if (points.Contains(p))
00637 {
00638 if (! isFirst)
00639 cmd << ' ';
00640 cmd << SgWritePoint(p);
00641 isFirst = false;
00642 }
00643 }
00644 }
00645
00646
00647 void GoGtpEngine::CmdLoadSgf(GtpCommand& cmd)
00648 {
00649 cmd.CheckNuArgLessEqual(2);
00650 string fileName = cmd.Arg(0);
00651 int moveNumber = -1;
00652 if (cmd.NuArg() == 2)
00653 moveNumber = cmd.ArgMin<int>(1, 1);
00654 ifstream in(fileName.c_str());
00655 if (! in)
00656 throw GtpFailure("could not open file");
00657 SgGameReader reader(in);
00658 SgNode* root = reader.ReadGame();
00659 if (root == 0)
00660 throw GtpFailure("no games in file");
00661 if (reader.GetWarnings().any())
00662 {
00663 SgWarning() << fileName << ":\n";
00664 reader.PrintWarnings(SgDebug());
00665 }
00666 if (Board().MoveNumber() > 0)
00667 GameFinished();
00668 m_game.Init(root);
00669 if (! GoGameUtil::GotoBeforeMove(&m_game, moveNumber))
00670 throw GtpFailure("invalid move number");
00671 GoRules rules = m_defaultRules;
00672 rules.SetKomi(GoNodeUtil::GetKomi(m_game.CurrentNode()));
00673 rules.SetHandicap(GoNodeUtil::GetHandicap(m_game.CurrentNode()));
00674 m_game.SetRulesGlobal(rules);
00675 RulesChanged();
00676 if (m_player != 0)
00677 m_player->OnNewGame();
00678 BoardChanged();
00679 }
00680
00681
00682 void GoGtpEngine::CmdName(GtpCommand& cmd)
00683 {
00684 cmd.CheckArgNone();
00685 if (m_player == 0)
00686 GtpEngine::CmdName(cmd);
00687 else
00688 cmd << m_player->Name();
00689 }
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699 void GoGtpEngine::CmdParam(GtpCommand& cmd)
00700 {
00701 cmd.CheckNuArgLessEqual(2);
00702 if (cmd.NuArg() == 0)
00703 {
00704 cmd << "[bool] accept_illegal " << m_acceptIllegal << '\n'
00705 << "[bool] debug_to_comment " << m_debugToComment << '\n'
00706 << "[bool] use_book " << m_useBook << '\n'
00707 << "[string] auto_save " << (m_autoSave ? m_autoSavePrefix : "")
00708 << '\n'
00709 << "[string] overhead " << m_overhead << '\n'
00710 << "[string] statistics_file " << m_statisticsFile << '\n'
00711 << "[string] timelimit " << m_timeLimit << '\n';
00712 }
00713 else if (cmd.NuArg() >= 1 && cmd.NuArg() <= 2)
00714 {
00715 string name = cmd.Arg(0);
00716 if (name == "accept_illegal")
00717 m_acceptIllegal = cmd.Arg<bool>(1);
00718 else if (name == "debug_to_comment")
00719 m_debugToComment = cmd.Arg<bool>(1);
00720 else if (name == "use_book")
00721 m_useBook = cmd.Arg<bool>(1);
00722 else if (name == "auto_save")
00723 {
00724 string prefix = cmd.RemainingLine(0);
00725 if (prefix == "")
00726 m_autoSave = false;
00727 else
00728 SetAutoSave(prefix);
00729 }
00730 else if (name == "overhead")
00731 {
00732 m_overhead = cmd.Arg<double>(1);
00733 m_game.Time().SetOverhead(m_overhead);
00734 }
00735 else if (name == "statistics_file")
00736 SetStatisticsFile(cmd.RemainingLine(0));
00737 else if (name == "timelimit")
00738 m_timeLimit = cmd.Arg<double>(1);
00739 else
00740 throw GtpFailure() << "unknown parameter: " << name;
00741 }
00742 else
00743 throw GtpFailure() << "need 0 or 2 arguments";
00744 }
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756 void GoGtpEngine::CmdParamRules(GtpCommand& cmd)
00757 {
00758 cmd.CheckNuArgLessEqual(2);
00759 if (cmd.NuArg() == 0)
00760 {
00761 const GoRules& r = Board().Rules();
00762 cmd << "[bool] allow_suicide "
00763 << SgWriteBoolAsInt(r.AllowSuicide()) << '\n'
00764 << "[bool] capture_dead "
00765 << SgWriteBoolAsInt(r.CaptureDead()) << '\n'
00766 << "[bool] extra_handicap_komi "
00767 << SgWriteBoolAsInt(r.ExtraHandicapKomi()) << '\n'
00768 << "[bool] japanese_scoring "
00769 << SgWriteBoolAsInt(r.JapaneseScoring()) << '\n'
00770 << "[bool] two_passes_end_game "
00771 << SgWriteBoolAsInt(r.TwoPassesEndGame()) << '\n'
00772 << "[list/simple/superko/pos_superko] ko_rule "
00773 << KoRuleToString(r.GetKoRule()) << '\n';
00774 }
00775 else if (cmd.NuArg() == 2)
00776 {
00777 GoRules r = Board().Rules();
00778 string name = cmd.Arg(0);
00779 if (name == "allow_suicide")
00780 {
00781 r.SetAllowSuicide(cmd.Arg<bool>(1));
00782 m_defaultRules.SetAllowSuicide(cmd.Arg<bool>(1));
00783 }
00784 else if (name == "capture_dead")
00785 {
00786 r.SetCaptureDead(cmd.Arg<bool>(1));
00787 m_defaultRules.SetCaptureDead(cmd.Arg<bool>(1));
00788 }
00789 else if (name == "extra_handicap_komi")
00790 {
00791 r.SetExtraHandicapKomi(cmd.Arg<bool>(1));
00792 m_defaultRules.SetExtraHandicapKomi(cmd.Arg<bool>(1));
00793 }
00794 else if (name == "japanese_scoring")
00795 {
00796 r.SetJapaneseScoring(cmd.Arg<bool>(1));
00797 m_defaultRules.SetJapaneseScoring(cmd.Arg<bool>(1));
00798 }
00799 else if (name == "two_passes_end_game")
00800 {
00801 r.SetTwoPassesEndGame(cmd.Arg<bool>(1));
00802 m_defaultRules.SetTwoPassesEndGame(cmd.Arg<bool>(1));
00803 }
00804 else if (name == "ko_rule")
00805 {
00806 r.SetKoRule(KoRuleArg(cmd, 1));
00807 m_defaultRules.SetKoRule(KoRuleArg(cmd, 1));
00808 }
00809 else
00810 throw GtpFailure() << "unknown parameter: " << name;
00811 m_game.SetRulesGlobal(r);
00812 RulesChanged();
00813 }
00814 else
00815 throw GtpFailure() << "need 0 or 2 arguments";
00816 }
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827 void GoGtpEngine::CmdParamTimecontrol(GtpCommand& cmd)
00828 {
00829 SgObjectWithDefaultTimeControl* object =
00830 dynamic_cast<SgObjectWithDefaultTimeControl*>(&Player());
00831 if (object == 0)
00832 throw GtpFailure("current player is not a "
00833 "SgObjectWithDefaultTimeControl");
00834 GoTimeControl* c = dynamic_cast<GoTimeControl*>(&object->TimeControl());
00835 if (c == 0)
00836 throw GtpFailure("current player does not have a GoTimeControl");
00837 cmd.CheckNuArgLessEqual(2);
00838 if (cmd.NuArg() == 0)
00839 {
00840 cmd << "fast_open_factor " << c->FastOpenFactor() << '\n'
00841 << "fast_open_moves " << c->FastOpenMoves() << '\n'
00842 << "final_space " << c->FinalSpace() << '\n'
00843 << "remaining_constant " << c->RemainingConstant() << '\n';
00844 }
00845 else if (cmd.NuArg() == 2)
00846 {
00847 string name = cmd.Arg(0);
00848 if (name == "fast_open_factor")
00849 c->SetFastOpenFactor(cmd.Arg<double>(1));
00850 else if (name == "fast_open_moves")
00851 c->SetFastOpenMoves(cmd.ArgMin<int>(1, 0));
00852 else if (name == "final_space")
00853 c->SetFinalSpace(max(cmd.Arg<float>(1), 0.f));
00854 else if (name == "remaining_constant")
00855 c->SetRemainingConstant(max(cmd.Arg<double>(1), 0.));
00856 else
00857 throw GtpFailure() << "unknown parameter: " << name;
00858 }
00859 else
00860 throw GtpFailure() << "need 0 or 2 arguments";
00861 }
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871 void GoGtpEngine::CmdPlaceFreeHandicap(GtpCommand& cmd)
00872 {
00873 CheckBoardEmpty();
00874 int n = cmd.ArgMin<int>(0, 2);
00875 int size = Board().Size();
00876 SgVector<SgPoint> stones;
00877 try
00878 {
00879 stones = GoGtpCommandUtil::GetHandicapStones(size, n);
00880 }
00881 catch (const GtpFailure&)
00882 {
00883 }
00884 if (stones.Length() < n && m_player != 0)
00885 {
00886
00887 if (n >= 9 && size % 2 != 0 && size >= 9 && size <= 25)
00888 stones = GoGtpCommandUtil::GetHandicapStones(size, 9);
00889
00890 else if (n >= 4 && size % 2 == 0 && (size >= 8 || size == 7)
00891 && size <= 25)
00892 stones = GoGtpCommandUtil::GetHandicapStones(size, 4);
00893 SgDebug() << "GoGtpEngine: Generating missing handicap\n";
00894 GoSetup setup;
00895 for (SgVectorIterator<SgPoint> it(stones); it; ++it)
00896 setup.AddBlack(*it);
00897 GoBoard& playerBd = m_player->Board();
00898 playerBd.Init(playerBd.Size(), setup);
00899 for (int i = stones.Length(); i < n; ++i)
00900 {
00901 SgPoint p = GenMove(SG_BLACK, true);
00902 SgDebug() << "GoGtpEngine: " << i << ' ' << SgWritePoint(p)
00903 << '\n';
00904 if (p == SG_PASS)
00905 break;
00906 playerBd.Play(p, SG_BLACK);
00907 stones.PushBack(p);
00908 }
00909 }
00910 SG_ASSERT(stones.Length() <= n);
00911 PlaceHandicap(stones);
00912 cmd << SgWritePointList(stones, "", false);
00913 }
00914
00915
00916 void GoGtpEngine::CmdPlay(GtpCommand& cmd)
00917 {
00918 cmd.CheckNuArg(2);
00919 SgBlackWhite color = BlackWhiteArg(cmd, 0);
00920 SgPoint move = MoveArg(cmd, 1);
00921 Play(color, move);
00922 BoardChanged();
00923 }
00924
00925
00926
00927
00928
00929
00930 void GoGtpEngine::CmdPlaySequence(GtpCommand& cmd)
00931 {
00932 const SgNode* oldCurrentNode = m_game.CurrentNode();
00933 try
00934 {
00935 for (size_t i = 0; i < cmd.NuArg(); i += 2)
00936 Play(BlackWhiteArg(cmd, i), MoveArg(cmd, i + 1));
00937 }
00938 catch (GtpFailure fail)
00939 {
00940 if (oldCurrentNode != m_game.CurrentNode())
00941 {
00942 m_game.GoToNode(oldCurrentNode);
00943 BoardChanged();
00944 }
00945 throw fail;
00946 }
00947 BoardChanged();
00948 }
00949
00950
00951 void GoGtpEngine::CmdPointInfo(GtpCommand& cmd)
00952 {
00953 SgPoint p = PointArg(cmd);
00954 const GoBoard& bd = Board();
00955 cmd << "Point:\n"
00956 << SgWriteLabel("Color") << SgEBW(bd.GetColor(p)) << '\n'
00957 << SgWriteLabel("InCenter") << bd.InCenter(p) << '\n'
00958 << SgWriteLabel("InCorner") << bd.InCorner(p) << '\n'
00959 << SgWriteLabel("Line") << bd.Line(p) << '\n'
00960 << SgWriteLabel("OnEdge") << bd.OnEdge(p) << '\n'
00961 << SgWriteLabel("EmptyNb") << bd.NumEmptyNeighbors(p) << '\n'
00962 << SgWriteLabel("EmptyNb8") << bd.Num8EmptyNeighbors(p) << '\n'
00963 << SgWriteLabel("Pos") << bd.Pos(p) << '\n';
00964 if (bd.Occupied(p))
00965 {
00966 SgVector<SgPoint> adjBlocks;
00967 GoBoardUtil::AdjacentBlocks(bd, p, SG_MAXPOINT, &adjBlocks);
00968 cmd << "Block:\n"
00969 << SgWritePointList(adjBlocks, "AdjBlocks", true)
00970 << SgWriteLabel("Anchor") << SgWritePoint(bd.Anchor(p)) << '\n'
00971 << SgWriteLabel("InAtari") << bd.InAtari(p) << '\n'
00972 << SgWriteLabel("IsSingleStone") << bd.IsSingleStone(p) << '\n'
00973 << SgWriteLabel("Liberties") << bd.NumLiberties(p) << '\n'
00974 << SgWriteLabel("Stones") << bd.NumStones(p) << '\n';
00975 }
00976 else
00977 cmd << "EmptyPoint:\n"
00978 << SgWriteLabel("IsFirst") << bd.IsFirst(p) << '\n'
00979 << SgWriteLabel("IsLegal/B") << bd.IsLegal(p, SG_BLACK) << '\n'
00980 << SgWriteLabel("IsLegal/W") << bd.IsLegal(p, SG_WHITE) << '\n'
00981 << SgWriteLabel("IsSuicide") << bd.IsSuicide(p) << '\n'
00982 << SgWriteLabel("MakesNakadeShape/B")
00983 << GoEyeUtil::MakesNakadeShape(bd, p, SG_BLACK) << '\n'
00984 << SgWriteLabel("MakesNakadeShape/W")
00985 << GoEyeUtil::MakesNakadeShape(bd, p, SG_WHITE) << '\n'
00986 << SgWriteLabel("IsSimpleEye/B")
00987 << GoEyeUtil::IsSimpleEye(bd, p, SG_BLACK) << '\n'
00988 << SgWriteLabel("IsSimpleEye/W")
00989 << GoEyeUtil::IsSimpleEye(bd, p, SG_WHITE) << '\n'
00990 << SgWriteLabel("IsSinglePointEye/B")
00991 << GoEyeUtil::IsSinglePointEye(bd, p, SG_BLACK) << '\n'
00992 << SgWriteLabel("IsSinglePointEye/W")
00993 << GoEyeUtil::IsSinglePointEye(bd, p, SG_WHITE) << '\n'
00994 << SgWriteLabel("IsPossibleEye/B")
00995 << GoEyeUtil::IsPossibleEye(bd, SG_BLACK, p) << '\n'
00996 << SgWriteLabel("IsPossibleEye/W")
00997 << GoEyeUtil::IsPossibleEye(bd, SG_WHITE, p) << '\n'
00998 ;
00999 }
01000
01001
01002
01003 void GoGtpEngine::CmdPlayerBoard(GtpCommand& cmd)
01004 {
01005 WriteBoardInfo(cmd, Player().Board());
01006 }
01007
01008
01009 void GoGtpEngine::CmdPointNumbers(GtpCommand& cmd)
01010 {
01011 cmd.CheckArgNone();
01012 SgPointArray<int> array(0);
01013 for (GoBoard::Iterator p(Board()); p; ++p)
01014 array[*p] = *p;
01015 cmd << SgWritePointArray<int>(array, Board().Size());
01016 }
01017
01018 void GoGtpEngine::CmdQuit(GtpCommand& cmd)
01019 {
01020 cmd.CheckArgNone();
01021 if (Board().MoveNumber() > 0)
01022 GameFinished();
01023 GtpEngine::CmdQuit(cmd);
01024 }
01025
01026
01027
01028
01029
01030 void GoGtpEngine::CmdRegGenMove(GtpCommand& cmd)
01031 {
01032 cmd.CheckNuArg(1);
01033 SgRandom::SetSeed(SgRandom::Seed());
01034 SgPoint move = GenMove(BlackWhiteArg(cmd, 0), true);
01035 if (move == SG_RESIGN)
01036 cmd << "resign";
01037 else
01038 cmd << SgWritePoint(move);
01039 }
01040
01041
01042
01043
01044
01045 void GoGtpEngine::CmdRegGenMoveToPlay(GtpCommand& cmd)
01046 {
01047 cmd.CheckArgNone();
01048 SgRandom::SetSeed(SgRandom::Seed());
01049 SgPoint move = GenMove(Board().ToPlay(), true);
01050 cmd << SgWritePoint(move);
01051 }
01052
01053
01054
01055 void GoGtpEngine::CmdRules(GtpCommand& cmd)
01056 {
01057 cmd.CheckNuArg(1);
01058 string arg = cmd.Arg(0);
01059 try
01060 {
01061 SetNamedRules(arg);
01062 }
01063 catch (const SgException&)
01064 {
01065 throw GtpFailure() << "unknown rules: " << arg;
01066 }
01067 }
01068
01069
01070
01071
01072
01073 void GoGtpEngine::CmdSaveSgf(GtpCommand& cmd)
01074 {
01075 cmd.CheckNuArg(1);
01076 SaveGame(cmd.Arg(0));
01077 }
01078
01079
01080
01081
01082
01083
01084
01085
01086 void GoGtpEngine::CmdSentinelFile(GtpCommand& cmd)
01087 {
01088 cmd.CheckNuArg(1);
01089 path sentinelFile = path(cmd.Arg(0));
01090 if (! sentinelFile.empty())
01091 try
01092 {
01093 remove(sentinelFile);
01094 }
01095 catch (const exception& e)
01096 {
01097 throw GtpFailure() << "could not remove sentinel file: "
01098 << e.what();
01099 }
01100 m_sentinelFile = sentinelFile;
01101 }
01102
01103
01104
01105 void GoGtpEngine::CmdSetFreeHandicap(GtpCommand& cmd)
01106 {
01107 SgVector<SgPoint> stones = PointListArg(cmd);
01108 if (stones.RemoveDuplicates())
01109 throw GtpFailure("duplicate handicap stones not allowed");
01110 PlaceHandicap(stones);
01111 }
01112
01113
01114
01115
01116
01117
01118
01119
01120 void GoGtpEngine::CmdSetInfo(GtpCommand& cmd)
01121 {
01122 string key = cmd.Arg(0);
01123 string value = cmd.RemainingLine(0);
01124 if (key == "game_name")
01125 m_game.UpdateGameName(value);
01126 else if (key == "player_black")
01127 m_game.UpdatePlayerName(SG_BLACK, value);
01128 else if (key == "player_white")
01129 m_game.UpdatePlayerName(SG_WHITE, value);
01130 else if (key == "result")
01131 {
01132 m_game.UpdateResult(value);
01133 m_isPonderPosition = false;
01134 }
01135 AutoSave();
01136 }
01137
01138
01139
01140
01141
01142 void GoGtpEngine::CmdSetup(GtpCommand& cmd)
01143 {
01144 const GoBoard& bd = Board();
01145 if (bd.MoveNumber() > 0)
01146 throw GtpFailure("setup only allowed on empty board");
01147 if (cmd.NuArg() % 2 != 0)
01148 throw GtpFailure("need even number of arguments");
01149 SgBWArray<SgPointSet> points;
01150 for (size_t i = 0; i < cmd.NuArg(); i += 2)
01151 {
01152 SgBlackWhite c = BlackWhiteArg(cmd, i);
01153 SgPoint p = PointArg(cmd, i + 1);
01154 for (SgBWIterator it; it; ++it)
01155 points[*it].Exclude(p);
01156 points[c].Include(p);
01157 }
01158 m_game.SetupPosition(points);
01159 BoardChanged();
01160 }
01161
01162
01163
01164
01165 void GoGtpEngine::CmdSetupPlayer(GtpCommand& cmd)
01166 {
01167 cmd.CheckNuArg(1);
01168 m_game.SetToPlay(BlackWhiteArg(cmd, 0));
01169 BoardChanged();
01170 }
01171
01172
01173 void GoGtpEngine::CmdShowBoard(GtpCommand& cmd)
01174 {
01175 cmd.CheckArgNone();
01176 cmd << '\n' << Board();
01177 }
01178
01179
01180 void GoGtpEngine::CmdTimeLastMove(GtpCommand& cmd)
01181 {
01182 cmd.CheckArgNone();
01183 cmd << setprecision(2) << m_timeLastMove;
01184 }
01185
01186
01187 void GoGtpEngine::CmdTimeLeft(GtpCommand& cmd)
01188 {
01189 cmd.CheckNuArg(3);
01190 SgBlackWhite color = BlackWhiteArg(cmd, 0);
01191
01192
01193
01194 int timeLeft = max(0, cmd.Arg<int>(1));
01195 int movesLeft = cmd.ArgMin<int>(2, 0);
01196 SgTimeRecord& time = m_game.Time();
01197 time.SetTimeLeft(color, timeLeft);
01198 time.SetMovesLeft(color, movesLeft);
01199 }
01200
01201
01202 void GoGtpEngine::CmdTimeSettings(GtpCommand& cmd)
01203 {
01204 cmd.CheckNuArg(3);
01205 int mainTime = cmd.ArgMin<int>(0, 0);
01206 int overtime = cmd.ArgMin<int>(1, 0);
01207 int overtimeMoves = cmd.ArgMin<int>(2, 0);
01208 GoTimeSettings timeSettings(mainTime, overtime, overtimeMoves);
01209 if (m_timeSettings == timeSettings)
01210 return;
01211 if (Board().MoveNumber() > 0)
01212 throw GtpFailure("cannot change time settings during game");
01213 m_timeSettings = timeSettings;
01214 ApplyTimeSettings();
01215 }
01216
01217
01218 void GoGtpEngine::CmdUndo(GtpCommand& cmd)
01219 {
01220 cmd.CheckArgNone();
01221 Undo(1);
01222 BoardChanged();
01223 }
01224
01225 void GoGtpEngine::CheckMoveStackOverflow() const
01226 {
01227 const int RESERVE = 50;
01228 if (Board().MoveNumber() >= GO_MAX_NUM_MOVES - RESERVE)
01229 throw GtpFailure("too many moves");
01230 if (Board().StackOverflowLikely())
01231 throw GtpFailure("move stack overflow");
01232 }
01233
01234 std::vector<std::string> GoGtpEngine::CreateStatisticsSlots()
01235 {
01236 return vector<string>();
01237 }
01238
01239 SgBlackWhite GoGtpEngine::BlackWhiteArg(const GtpCommand& cmd,
01240 std::size_t number) const
01241 {
01242 return GoGtpCommandUtil::BlackWhiteArg(cmd, number);
01243 }
01244
01245 void GoGtpEngine::CreateAutoSaveFileName()
01246 {
01247 time_t timeValue = time(0);
01248 struct tm* timeStruct = localtime(&timeValue);
01249 char timeBuffer[128];
01250 strftime(timeBuffer, sizeof(timeBuffer), "%Y%m%d%H%M%S", timeStruct);
01251 ostringstream fileName;
01252 fileName << m_autoSavePrefix << timeBuffer << ".sgf";
01253 m_autoSaveFileName = fileName.str();
01254 }
01255
01256 void GoGtpEngine::DumpState(ostream& out) const
01257 {
01258 out << "GoGtpEngine board:\n";
01259 GoBoardUtil::DumpBoard(Board(), out);
01260 if (m_player != 0)
01261 {
01262 out << "GoPlayer board:\n";
01263 GoBoardUtil::DumpBoard(m_player->Board(), out);
01264 }
01265 }
01266
01267 SgEmptyBlackWhite GoGtpEngine::EmptyBlackWhiteArg(const GtpCommand& cmd,
01268 std::size_t number) const
01269 {
01270 return GoGtpCommandUtil::EmptyBlackWhiteArg(cmd, number);
01271 }
01272
01273 SgPoint GoGtpEngine::EmptyPointArg(const GtpCommand& cmd,
01274 std::size_t number) const
01275 {
01276 return GoGtpCommandUtil::EmptyPointArg(cmd, number, Board());
01277 }
01278
01279
01280
01281
01282
01283
01284 void GoGtpEngine::GameFinished()
01285 {
01286 if (m_player != 0)
01287 m_player->OnGameFinished();
01288 }
01289
01290 SgPoint GoGtpEngine::GenBookMove(SgBlackWhite toPlay)
01291 {
01292 if (! m_useBook || toPlay != Board().ToPlay())
01293 return SG_NULLMOVE;
01294 if (m_autoBook.get() != 0)
01295 return m_autoBook->LookupMove(Board());
01296 return m_book.LookupMove(Board());
01297 }
01298
01299 SgPoint GoGtpEngine::GenMove(SgBlackWhite color, bool ignoreClock)
01300 {
01301 SG_ASSERT_BW(color);
01302 CheckMoveStackOverflow();
01303 StartStatistics();
01304 GoPlayer& player = Player();
01305 double startTime = SgTime::Get();
01306 SgTimeRecord time;
01307 if (ignoreClock || m_timeSettings.IsUnknown())
01308 time = SgTimeRecord(true, m_timeLimit);
01309 else
01310 time = m_game.Time();
01311 AddStatistics("GAME", m_autoSaveFileName);
01312 AddStatistics("MOVE", m_game.CurrentMoveNumber() + 1);
01313 SgPoint move = GenBookMove(color);
01314 m_mpiSynchronizer->SynchronizeMove(move);
01315 if (move != SG_NULLMOVE)
01316 {
01317 SgDebug() << "GoGtpEngine: Using move from opening book\n";
01318 AddStatistics("BOOK", 1);
01319 }
01320 else
01321 AddStatistics("BOOK", 0);
01322 if (move == SG_NULLMOVE)
01323 {
01324 player.ClearSearchTraces();
01325 move = player.GenMove(time, color);
01326 SgNode* searchTraces = player.TransferSearchTraces();
01327 if (searchTraces != 0)
01328 m_game.AppendChild(searchTraces);
01329 }
01330 m_mpiSynchronizer->SynchronizeMove(move);
01331 m_timeLastMove = SgTime::Get() - startTime;
01332 AddStatistics("TIME", m_timeLastMove);
01333 if (move == SG_NULLMOVE)
01334 throw GtpFailure() << player.Name() << " generated NULLMOVE";
01335 if (move == SG_RESIGN)
01336 m_isPonderPosition = false;
01337 else
01338 CheckLegal(player.Name() + " generated illegal move: ", color, move,
01339 false);
01340 AddPlayStatistics();
01341 SaveStatistics();
01342 return move;
01343 }
01344
01345 void GoGtpEngine::Init(int size)
01346 {
01347 m_game.Init(size, m_defaultRules);
01348 m_game.UpdateDate(SgTime::TodaysDate());
01349 ApplyTimeSettings();
01350 CreateAutoSaveFileName();
01351 BoardChanged();
01352 }
01353
01354 void GoGtpEngine::InitStatistics()
01355 {
01356 m_statisticsSlots.clear();
01357 m_statisticsSlots.push_back("GAME");
01358 m_statisticsSlots.push_back("MOVE");
01359 m_statisticsSlots.push_back("TIME");
01360 m_statisticsSlots.push_back("BOOK");
01361 vector<string> slots = CreateStatisticsSlots();
01362 for (vector<string>::const_iterator i = slots.begin(); i != slots.end();
01363 ++i)
01364 {
01365 if (i->find('\t') != string::npos)
01366 throw SgException("GoGtpEngine::InitStatistics: statistics slot"
01367 " contains tab: '" + (*i) + "'");
01368 if (find(m_statisticsSlots.begin(), m_statisticsSlots.end(), *i)
01369 != m_statisticsSlots.end())
01370 throw SgException("GoGtpEngine::InitStatistics: duplicate"
01371 " statistics slot '" + (*i) + "'");
01372 m_statisticsSlots.push_back(*i);
01373 }
01374 if (MpiSynchronizer()->IsRootProcess())
01375 {
01376 ofstream out(m_statisticsFile.c_str(), ios::app);
01377
01378
01379
01380
01381 out << '#';
01382 for (size_t i = 0; i < m_statisticsSlots.size(); ++i)
01383 {
01384 out << m_statisticsSlots[i];
01385 if (i < m_statisticsSlots.size() - 1)
01386 out << '\t';
01387 else
01388 out << '\n';
01389 }
01390 }
01391 }
01392
01393 SgPoint GoGtpEngine::MoveArg(const GtpCommand& cmd, std::size_t number) const
01394 {
01395 return GoGtpCommandUtil::MoveArg(cmd, number, Board());
01396 }
01397
01398 void GoGtpEngine::PlaceHandicap(const SgVector<SgPoint>& stones)
01399 {
01400 CheckBoardEmpty();
01401 m_game.PlaceHandicap(stones);
01402 RulesChanged();
01403 BoardChanged();
01404 }
01405
01406 void GoGtpEngine::Play(SgBlackWhite color, SgPoint move)
01407 {
01408 if (move == SG_RESIGN)
01409 {
01410
01411
01412
01413
01414 m_isPonderPosition = false;
01415 return;
01416 }
01417 CheckMoveStackOverflow();
01418 CheckLegal("illegal move: ", color, move, m_acceptIllegal);
01419 m_game.AddMove(move, color);
01420 }
01421
01422 GoPlayer& GoGtpEngine::Player() const
01423 {
01424 if (m_player == 0)
01425 throw GtpFailure("no player set");
01426 return *m_player;
01427 }
01428
01429 SgPoint GoGtpEngine::PointArg(const GtpCommand& cmd) const
01430 {
01431 return GoGtpCommandUtil::PointArg(cmd, Board());
01432 }
01433
01434 SgPoint GoGtpEngine::PointArg(const GtpCommand& cmd, std::size_t number) const
01435 {
01436 return GoGtpCommandUtil::PointArg(cmd, number, Board());
01437 }
01438
01439 SgVector<SgPoint> GoGtpEngine::PointListArg(const GtpCommand& cmd,
01440 std::size_t number) const
01441 {
01442 return GoGtpCommandUtil::PointListArg(cmd, number, Board());
01443 }
01444
01445 SgVector<SgPoint> GoGtpEngine::PointListArg(const GtpCommand& cmd) const
01446 {
01447 return GoGtpCommandUtil::PointListArg(cmd, Board());
01448 }
01449
01450 void GoGtpEngine::RespondNumberArray(GtpCommand& cmd,
01451 const SgPointArray<int>& array,
01452 int scale)
01453 {
01454 GoGtpCommandUtil::RespondNumberArray(cmd, array, scale, Board());
01455 }
01456
01457 void GoGtpEngine::RulesChanged()
01458 {
01459 if (m_player != 0)
01460 m_player->UpdateSubscriber();
01461 }
01462
01463 void GoGtpEngine::SaveGame(const std::string& fileName) const
01464 {
01465 if (MpiSynchronizer()->IsRootProcess())
01466 {
01467 try
01468 {
01469 ofstream out(fileName.c_str());
01470 SgGameWriter writer(out);
01471 writer.WriteGame(m_game.Root(), true, 0, 1, 19);
01472 }
01473 catch (const SgException& e)
01474 {
01475 throw GtpFailure(e.what());
01476 }
01477 }
01478 }
01479
01480 void GoGtpEngine::SaveStatistics()
01481 {
01482 if (MpiSynchronizer()->IsRootProcess())
01483 {
01484 if (m_statisticsFile == "")
01485 return;
01486 SG_ASSERT(m_statisticsValues.size() == m_statisticsSlots.size());
01487 ofstream out(m_statisticsFile.c_str(), ios::app);
01488 for (size_t i = 0; i < m_statisticsSlots.size(); ++i)
01489 {
01490 out << m_statisticsValues[i];
01491 if (i < m_statisticsSlots.size() - 1)
01492 out << '\t';
01493 else
01494 out << '\n';
01495 }
01496 }
01497 }
01498
01499 void GoGtpEngine::SetAutoSave(const std::string& prefix)
01500 {
01501 m_autoSave = true;
01502 m_autoSavePrefix = prefix;
01503 CreateAutoSaveFileName();
01504 }
01505
01506 void GoGtpEngine::SetAutoShowBoard(bool showBoard)
01507 {
01508 m_autoShowBoard = showBoard;
01509 if (m_autoShowBoard)
01510 SgDebug() << Board();
01511 }
01512
01513 inline void GoGtpEngine::SetStatisticsFile(const std::string& fileName)
01514 {
01515 m_statisticsFile = fileName;
01516 InitStatistics();
01517 }
01518
01519 void GoGtpEngine::SetPlayer(GoPlayer* player)
01520 {
01521 if (player != m_player)
01522 {
01523 delete m_player;
01524 m_player = player;
01525 }
01526 if (m_player != 0)
01527 {
01528 m_player->UpdateSubscriber();
01529 m_player->OnNewGame();
01530 }
01531 InitStatistics();
01532 }
01533
01534 void GoGtpEngine::SetNamedRules(const string& namedRules)
01535 {
01536 m_defaultRules.SetNamedRules(namedRules);
01537 m_game.SetRulesGlobal(m_defaultRules);
01538 RulesChanged();
01539 }
01540
01541 void GoGtpEngine::StartStatistics()
01542 {
01543 m_statisticsValues.clear();
01544 m_statisticsValues.resize(m_statisticsSlots.size(), "-");
01545 }
01546
01547 SgPoint GoGtpEngine::StoneArg(const GtpCommand& cmd, std::size_t number) const
01548 {
01549 return GoGtpCommandUtil::StoneArg(cmd, number, Board());
01550 }
01551
01552 void GoGtpEngine::Undo(int n)
01553 {
01554 SG_ASSERT(n >= 0);
01555 const SgNode* node = m_game.CurrentNode();
01556 for (int i = 0; i < n; ++i)
01557 {
01558 if (! node->HasNodeMove() || ! node->HasFather())
01559 throw GtpFailure() << "cannot undo " << n << " move(s)";
01560 node = node->Father();
01561 }
01562 m_game.GoToNode(node);
01563 }
01564
01565
01566
01567
01568 void GoGtpEngine::WriteBoardInfo(GtpCommand& cmd, const GoBoard& bd)
01569 {
01570 cmd.CheckNuArgLessEqual(1);
01571 if (cmd.NuArg() == 1)
01572 {
01573 string arg = cmd.Arg(0);
01574 if (arg == "countplay")
01575 cmd << bd.CountPlay();
01576 else
01577 throw GtpFailure() << "unknown argument " << arg;
01578 return;
01579 }
01580 cmd << "Board:\n"
01581 << SgWriteLabel("Hash") << bd.GetHashCode() << '\n'
01582 << SgWriteLabel("HashToPlay") << bd.GetHashCodeInclToPlay() << '\n'
01583 << SgWriteLabel("KoColor") << SgEBW(bd.KoColor()) << '\n'
01584 << SgWriteLabel("MoveNumber") << bd.MoveNumber() << '\n'
01585 << SgWriteLabel("NumStones[B]") << bd.TotalNumStones(SG_BLACK) << '\n'
01586 << SgWriteLabel("NumStones[W]") << bd.TotalNumStones(SG_WHITE) << '\n'
01587 << SgWriteLabel("NumEmpty") << bd.TotalNumEmpty() << '\n'
01588 << SgWriteLabel("ToPlay") << SgBW(bd.ToPlay()) << '\n'
01589 << SgWriteLabel("CountPlay") << bd.CountPlay() << '\n'
01590 << "Sets:\n"
01591 << SgWritePointSet(bd.AllPoints(), "AllPoints")
01592 << SgWritePointSet(bd.All(SG_BLACK), "AllBlack")
01593 << SgWritePointSet(bd.All(SG_WHITE), "AllWhite")
01594 << SgWritePointSet(bd.AllEmpty(), "AllEmpty")
01595 << SgWritePointSet(bd.Corners(), "Corners")
01596 << SgWritePointSet(bd.Edges(), "Edges")
01597 << SgWritePointSet(bd.Centers(), "Centers")
01598 << SgWritePointSet(bd.SideExtensions(), "SideExtensions")
01599 << SgWritePointSet(bd.Occupied(), "Occupied");
01600 }
01601
01602 #if GTPENGINE_PONDER
01603
01604 void GoGtpEngine::Ponder()
01605 {
01606 if (m_player == 0 || ! m_isPonderPosition)
01607 return;
01608
01609
01610 boost::xtime time;
01611 boost::xtime_get(&time, boost::TIME_UTC);
01612 bool aborted = false;
01613 for (int i = 0; i < 200; ++i)
01614 {
01615 if (SgUserAbort())
01616 {
01617 aborted = true;
01618 break;
01619 }
01620 time.nsec += 1000000;
01621 boost::thread::sleep(time);
01622 }
01623 m_mpiSynchronizer->SynchronizeUserAbort(aborted);
01624 if (! aborted)
01625 {
01626 m_mpiSynchronizer->OnStartPonder();
01627 m_player->Ponder();
01628 m_mpiSynchronizer->OnEndPonder();
01629 }
01630 }
01631
01632 void GoGtpEngine::StopPonder()
01633 {
01634 SgSetUserAbort(true);
01635 }
01636
01637 void GoGtpEngine::InitPonder()
01638 {
01639 SgSetUserAbort(false);
01640 }
01641
01642 #endif // GTPENGINE_PONDER
01643
01644 #if GTPENGINE_INTERRUPT
01645
01646 void GoGtpEngine::Interrupt()
01647 {
01648 SgSetUserAbort(true);
01649 }
01650
01651 #endif // GTPENGINE_INTERRUPT
01652
01653 void GoGtpEngine::SetMpiSynchronizer(const SgMpiSynchronizerHandle &handle)
01654 {
01655 m_mpiSynchronizer = SgMpiSynchronizerHandle(handle);
01656 }
01657
01658 SgMpiSynchronizerHandle GoGtpEngine::MpiSynchronizer()
01659 {
01660 return SgMpiSynchronizerHandle(m_mpiSynchronizer);
01661 }
01662
01663 const SgMpiSynchronizerHandle GoGtpEngine::MpiSynchronizer() const
01664 {
01665 return SgMpiSynchronizerHandle(m_mpiSynchronizer);
01666 }
01667
01668
01669
01670
01671 GoGtpAssertionHandler::GoGtpAssertionHandler(const GoGtpEngine& engine)
01672 : m_engine(engine)
01673 {
01674 }
01675
01676 void GoGtpAssertionHandler::Run()
01677 {
01678 m_engine.DumpState(SgDebug());
01679 SgDebug() << flush;
01680 }
01681
01682