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