00001
00002
00003
00004
00005
00006
00007 #include "GtpEngine.h"
00008
00009 #include <iomanip>
00010 #include <cassert>
00011 #include <cctype>
00012 #include <fstream>
00013
00014 #if GTPENGINE_PONDER || GTPENGINE_INTERRUPT
00015 #include <boost/thread/barrier.hpp>
00016 #include <boost/thread/condition.hpp>
00017 #include <boost/thread/mutex.hpp>
00018 #include <boost/thread/thread.hpp>
00019 #include <boost/thread/xtime.hpp>
00020
00021 using boost::barrier;
00022 using boost::condition;
00023 using boost::mutex;
00024 using boost::thread;
00025 using boost::xtime;
00026 using boost::xtime_get;
00027 #endif
00028
00029 using namespace std;
00030
00031 #if WIN32
00032
00033
00034
00035 #pragma warning( disable : 4355 )
00036 #endif
00037
00038
00039
00040
00041 namespace {
00042
00043 void Trim(string& str);
00044
00045
00046
00047
00048
00049
00050 bool IsCommandLine(const string& line)
00051 {
00052 string trimmedLine = line;
00053 Trim(trimmedLine);
00054 return (! trimmedLine.empty() && trimmedLine[0] != '#');
00055 }
00056
00057 #if ! GTPENGINE_INTERRUPT
00058
00059
00060
00061
00062
00063
00064 bool ReadCommand(GtpCommand& cmd, GtpInputStream& in)
00065 {
00066 string line;
00067 while (in.GetLine(line) && ! IsCommandLine(line))
00068 {
00069 }
00070 if (in.EndOfInput())
00071 return false;
00072 Trim(line);
00073 cmd.Init(line);
00074 return true;
00075 }
00076 #endif
00077
00078
00079
00080
00081
00082
00083
00084 string ReplaceEmptyLines(const string& text)
00085 {
00086 if (text.find("\n\n") == string::npos)
00087 return text;
00088 istringstream in(text);
00089 ostringstream result;
00090 bool lastWasNewLine = false;
00091 char c;
00092 while (in.get(c))
00093 {
00094 bool isNewLine = (c == '\n');
00095 if (isNewLine && lastWasNewLine)
00096 result.put(' ');
00097 result.put(c);
00098 lastWasNewLine = isNewLine;
00099 }
00100 return result.str();
00101 }
00102
00103
00104
00105
00106
00107 void Trim(string& str)
00108 {
00109 char const* whiteSpace = " \t\r";
00110 size_t pos = str.find_first_not_of(whiteSpace);
00111 str.erase(0, pos);
00112 pos = str.find_last_not_of(whiteSpace);
00113 str.erase(pos + 1);
00114 }
00115
00116 }
00117
00118
00119
00120 #if GTPENGINE_PONDER || GTPENGINE_INTERRUPT
00121
00122
00123 namespace {
00124
00125 void Notify(mutex& aMutex, condition& aCondition)
00126 {
00127 mutex::scoped_lock lock(aMutex);
00128 aCondition.notify_all();
00129 }
00130
00131 }
00132
00133 #endif // GTPENGINE_PONDER || GTPENGINE_INTERRUPT
00134
00135
00136
00137 #if GTPENGINE_PONDER
00138
00139 namespace {
00140
00141
00142
00143
00144
00145
00146 class PonderThread
00147 {
00148 public:
00149 PonderThread(GtpEngine& engine);
00150
00151 void StartPonder();
00152
00153 void StopPonder();
00154
00155 void Quit();
00156
00157 private:
00158 class Function
00159 {
00160 public:
00161 Function(PonderThread& ponderThread);
00162
00163 void operator()();
00164
00165 private:
00166 PonderThread& m_ponderThread;
00167 };
00168
00169 friend class PonderThread::Function;
00170
00171 GtpEngine& m_engine;
00172
00173 barrier m_threadReady;
00174
00175 mutex m_startPonderMutex;
00176
00177 mutex m_ponderFinishedMutex;
00178
00179 condition m_startPonder;
00180
00181 condition m_ponderFinished;
00182
00183 mutex::scoped_lock m_ponderFinishedLock;
00184
00185
00186
00187
00188
00189 boost::thread m_thread;
00190 };
00191
00192 PonderThread::Function::Function(PonderThread& ponderThread)
00193 : m_ponderThread(ponderThread)
00194 {
00195 }
00196
00197 void PonderThread::Function::operator()()
00198 {
00199 mutex::scoped_lock lock(m_ponderThread.m_startPonderMutex);
00200 m_ponderThread.m_threadReady.wait();
00201 while (true)
00202 {
00203 m_ponderThread.m_startPonder.wait(lock);
00204 GtpEngine& engine = m_ponderThread.m_engine;
00205 if (engine.IsQuitSet())
00206 return;
00207 engine.Ponder();
00208 Notify(m_ponderThread.m_ponderFinishedMutex,
00209 m_ponderThread.m_ponderFinished);
00210 }
00211 }
00212
00213 PonderThread::PonderThread(GtpEngine& engine)
00214 : m_engine(engine),
00215 m_threadReady(2),
00216 m_ponderFinishedLock(m_ponderFinishedMutex),
00217 m_thread(Function(*this))
00218 {
00219 m_threadReady.wait();
00220 }
00221
00222 void PonderThread::StartPonder()
00223 {
00224 m_engine.InitPonder();
00225 Notify(m_startPonderMutex, m_startPonder);
00226 }
00227
00228 void PonderThread::StopPonder()
00229 {
00230 m_engine.StopPonder();
00231 m_ponderFinished.wait(m_ponderFinishedLock);
00232 }
00233
00234 void PonderThread::Quit()
00235 {
00236 Notify(m_startPonderMutex, m_startPonder);
00237 m_thread.join();
00238 }
00239
00240 }
00241
00242 #endif // GTPENGINE_PONDER
00243
00244
00245
00246 #if GTPENGINE_INTERRUPT
00247
00248 namespace {
00249
00250
00251
00252
00253
00254
00255
00256 class ReadThread
00257 {
00258 public:
00259 ReadThread(GtpInputStream& in, GtpEngine& engine);
00260
00261 bool ReadCommand(GtpCommand& cmd);
00262
00263 void JoinThread();
00264
00265 private:
00266 class Function
00267 {
00268 public:
00269 Function(ReadThread& readThread);
00270
00271 void operator()();
00272
00273 private:
00274 ReadThread& m_readThread;
00275
00276 void ExecuteSleepLine(const string& line);
00277 };
00278
00279 friend class ReadThread::Function;
00280
00281 GtpInputStream& m_in;
00282
00283 GtpEngine& m_engine;
00284
00285 string m_line;
00286
00287 bool m_isStreamGood;
00288
00289 barrier m_threadReady;
00290
00291 mutex m_waitCommandMutex;
00292
00293 condition m_waitCommand;
00294
00295 mutex m_commandReceivedMutex;
00296
00297 condition m_commandReceived;
00298
00299 mutex::scoped_lock m_commandReceivedLock;
00300
00301
00302
00303
00304
00305 boost::thread m_thread;
00306 };
00307
00308 ReadThread::Function::Function(ReadThread& readThread)
00309 : m_readThread(readThread)
00310 {
00311 }
00312
00313 void ReadThread::Function::operator()()
00314 {
00315 mutex::scoped_lock lock(m_readThread.m_waitCommandMutex);
00316 m_readThread.m_threadReady.wait();
00317 GtpEngine& engine = m_readThread.m_engine;
00318 GtpInputStream& in = m_readThread.m_in;
00319 string line;
00320 while (true)
00321 {
00322 while (in.GetLine(line))
00323 {
00324 Trim(line);
00325 if (line == "# interrupt")
00326 engine.Interrupt();
00327 else if (line.find("# gtpengine-sleep ") == 0)
00328 ExecuteSleepLine(line);
00329 else if (IsCommandLine(line))
00330 break;
00331 }
00332 m_readThread.m_waitCommand.wait(lock);
00333 m_readThread.m_isStreamGood = ! in.EndOfInput();
00334 m_readThread.m_line = line;
00335 Notify(m_readThread.m_commandReceivedMutex,
00336 m_readThread.m_commandReceived);
00337 if (in.EndOfInput())
00338 return;
00339
00340 GtpCommand cmd(line);
00341 if (cmd.Name() == "quit")
00342 return;
00343 }
00344 }
00345
00346 void ReadThread::Function::ExecuteSleepLine(const string& line)
00347 {
00348 istringstream buffer(line);
00349 string s;
00350 buffer >> s;
00351 assert(s == "#");
00352 buffer >> s;
00353 assert(s == "gtpengine-sleep");
00354 int seconds;
00355 buffer >> seconds;
00356 if (seconds > 0)
00357 {
00358 cerr << "GtpEngine: sleep " << seconds << '\n';
00359 xtime time;
00360 xtime_get(&time, boost::TIME_UTC);
00361 time.sec += seconds;
00362 thread::sleep(time);
00363 cerr << "GtpEngine: sleep done\n";
00364 }
00365 }
00366
00367 void ReadThread::JoinThread()
00368 {
00369 m_thread.join();
00370 }
00371
00372 ReadThread::ReadThread(GtpInputStream& in, GtpEngine& engine)
00373 : m_in(in),
00374 m_engine(engine),
00375 m_threadReady(2),
00376 m_commandReceivedLock(m_commandReceivedMutex),
00377 m_thread(Function(*this))
00378 {
00379 m_threadReady.wait();
00380 }
00381
00382 bool ReadThread::ReadCommand(GtpCommand& cmd)
00383 {
00384 Notify(m_waitCommandMutex, m_waitCommand);
00385 m_commandReceived.wait(m_commandReceivedLock);
00386 if (! m_isStreamGood)
00387 return false;
00388 cmd.Init(m_line);
00389 return true;
00390 }
00391
00392 }
00393
00394 #endif // GTPENGINE_INTERRUPT
00395
00396
00397
00398 GtpFailure::GtpFailure()
00399 {
00400 }
00401
00402 GtpFailure::GtpFailure(const GtpFailure& failure)
00403 {
00404 m_response << failure.Response();
00405 m_response.copyfmt(failure.m_response);
00406 }
00407
00408 GtpFailure::GtpFailure(const string& response)
00409 {
00410 m_response << response;
00411 }
00412
00413 GtpFailure::~GtpFailure() throw()
00414 {
00415 }
00416
00417
00418
00419 GtpCommand::Argument::Argument(const string& value, std::size_t end)
00420 : m_value(value),
00421 m_end(end)
00422 {
00423 }
00424
00425
00426
00427 ostringstream GtpCommand::s_dummy;
00428
00429 const string& GtpCommand::Arg(std::size_t number) const
00430 {
00431 size_t index = number + 1;
00432 if (number >= NuArg())
00433 throw GtpFailure() << "missing argument " << index;
00434 return m_arguments[index].m_value;
00435 }
00436
00437 const string& GtpCommand::Arg() const
00438 {
00439 CheckNuArg(1);
00440 return Arg(0);
00441 }
00442
00443 std::string GtpCommand::ArgLine() const
00444 {
00445 string result = m_line.substr(m_arguments[0].m_end);
00446 Trim(result);
00447 return result;
00448 }
00449
00450 string GtpCommand::ArgToLower(std::size_t number) const
00451 {
00452 string value = Arg(number);
00453 for (size_t i = 0; i < value.length(); ++i)
00454 value[i] = tolower(value[i]);
00455 return value;
00456 }
00457
00458 bool GtpCommand::BoolArg(std::size_t number) const
00459 {
00460 string value = Arg(number);
00461 if (value == "1")
00462 return true;
00463 if (value == "0")
00464 return false;
00465 throw GtpFailure() << "argument " << (number + 1) << " must be 0 or 1";
00466 }
00467
00468 void GtpCommand::CheckNuArg(std::size_t number) const
00469 {
00470 if (NuArg() == number)
00471 return;
00472 if (number == 0)
00473 throw GtpFailure() << "no arguments allowed";
00474 else if (number == 1)
00475 throw GtpFailure() << "command needs one argument";
00476 else
00477 throw GtpFailure() << "command needs " << number << " arguments";
00478 }
00479
00480 void GtpCommand::CheckNuArgLessEqual(std::size_t number) const
00481 {
00482 if (NuArg() <= number)
00483 return;
00484 if (number == 1)
00485 throw GtpFailure() << "command needs at most one argument";
00486 else
00487 throw GtpFailure() << "command needs at most " << number << " arguments";
00488 }
00489
00490 double GtpCommand::FloatArg(std::size_t number) const
00491 {
00492 istringstream in(Arg(number));
00493 double result;
00494 in >> result;
00495 if (! in)
00496 throw GtpFailure() << "argument " << (number + 1)
00497 << " must be a float";
00498 return result;
00499 }
00500
00501 int GtpCommand::IntArg(std::size_t number) const
00502 {
00503 istringstream in(Arg(number));
00504 int result;
00505 in >> result;
00506 if (! in)
00507 throw GtpFailure() << "argument " << (number + 1)
00508 << " must be a number";
00509 return result;
00510 }
00511
00512 int GtpCommand::IntArg(std::size_t number, int min) const
00513 {
00514 int result = IntArg(number);
00515 if (result < min)
00516 throw GtpFailure() << "argument " << (number + 1)
00517 << " must be greater or equal " << min;
00518 return result;
00519 }
00520
00521 int GtpCommand::IntArg(std::size_t number, int min, int max) const
00522 {
00523 int result = IntArg(number);
00524 if (result < min)
00525 throw GtpFailure() << "argument " << (number + 1)
00526 << " must be greater or equal " << min;
00527 if (result > max)
00528 throw GtpFailure() << "argument " << (number + 1)
00529 << " must be less or equal " << max;
00530 return result;
00531 }
00532
00533 void GtpCommand::Init(const string& line)
00534 {
00535 m_line = line;
00536 Trim(m_line);
00537 SplitLine(m_line);
00538 assert(m_arguments.size() > 0);
00539 ParseCommandId();
00540 assert(m_arguments.size() > 0);
00541 m_response.str("");
00542 m_response.copyfmt(s_dummy);
00543 }
00544
00545 void GtpCommand::ParseCommandId()
00546 {
00547 m_id = "";
00548 if (m_arguments.size() < 2)
00549 return;
00550 istringstream in(m_arguments[0].m_value);
00551 int id;
00552 in >> id;
00553 if (in)
00554 {
00555 m_id = m_arguments[0].m_value;
00556 m_arguments.erase(m_arguments.begin());
00557 }
00558 }
00559
00560 string GtpCommand::RemainingLine(std::size_t number) const
00561 {
00562 size_t index = number + 1;
00563 if (number >= NuArg())
00564 throw GtpFailure() << "missing argument " << index;
00565 string result = m_line.substr(m_arguments[index].m_end);
00566 Trim(result);
00567 return result;
00568 }
00569
00570 void GtpCommand::SetResponse(const string& response)
00571 {
00572 m_response.str(response);
00573 }
00574
00575 void GtpCommand::SetResponseBool(bool value)
00576 {
00577 m_response.str(value ? "true" : "false");
00578 }
00579
00580 std::size_t GtpCommand::SizeTypeArg(std::size_t number) const
00581 {
00582 string arg = Arg(number);
00583
00584
00585
00586 bool fail = (! arg.empty() && arg[0] == '-');
00587 std::size_t result;
00588 if (! fail)
00589 {
00590 istringstream in(arg);
00591 in >> result;
00592 fail = ! in;
00593 }
00594 if (fail)
00595 throw GtpFailure() << "argument " << (number + 1)
00596 << " must be a number";
00597 return result;
00598 }
00599
00600 std::size_t GtpCommand::SizeTypeArg(std::size_t number, std::size_t min) const
00601 {
00602 std::size_t result = SizeTypeArg(number);
00603 if (result < min)
00604 throw GtpFailure() << "argument " << (number + 1)
00605 << " must be greater or equal " << min;
00606 return result;
00607 }
00608
00609
00610
00611
00612
00613
00614
00615 void GtpCommand::SplitLine(const string& line)
00616 {
00617 m_arguments.clear();
00618 bool escape = false;
00619 bool inString = false;
00620 ostringstream element;
00621 for (size_t i = 0; i < line.size(); ++i)
00622 {
00623 char c = line[i];
00624 if (c == '"' && ! escape)
00625 {
00626 if (inString)
00627 {
00628 m_arguments.push_back(Argument(element.str(), i + 1));
00629 element.str("");
00630 }
00631 inString = ! inString;
00632 }
00633 else if (isspace(c) && ! inString)
00634 {
00635 if (! element.str().empty())
00636 {
00637 m_arguments.push_back(Argument(element.str(), i + 1));
00638 element.str("");
00639 }
00640 }
00641 else
00642 element << c;
00643 escape = (c == '\\' && ! escape);
00644 }
00645 if (! element.str().empty())
00646 m_arguments.push_back(Argument(element.str(), line.size()));
00647 }
00648
00649
00650
00651 GtpCallbackBase::~GtpCallbackBase() throw()
00652 {
00653 }
00654
00655
00656
00657 GtpEngine::GtpEngine(GtpInputStream& in, GtpOutputStream& out)
00658 : m_quit(false),
00659 m_in(in),
00660 m_out(out)
00661 {
00662 Register("known_command", &GtpEngine::CmdKnownCommand, this);
00663 Register("list_commands", &GtpEngine::CmdListCommands, this);
00664 Register("name", &GtpEngine::CmdName, this);
00665 Register("protocol_version", &GtpEngine::CmdProtocolVersion, this);
00666 Register("quit", &GtpEngine::CmdQuit, this);
00667 Register("version", &GtpEngine::CmdVersion, this);
00668 }
00669
00670 GtpEngine::~GtpEngine()
00671 {
00672 typedef CallbackMap::iterator Iterator;
00673 for (Iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i)
00674 {
00675 delete i->second;
00676 #ifndef NDEBUG
00677 i->second = 0;
00678 #endif
00679 }
00680 }
00681
00682 void GtpEngine::BeforeHandleCommand()
00683 {
00684
00685 }
00686
00687 void GtpEngine::BeforeWritingResponse()
00688 {
00689
00690 }
00691
00692
00693 void GtpEngine::CmdKnownCommand(GtpCommand& cmd)
00694 {
00695 cmd.CheckNuArg(1);
00696 cmd.SetResponseBool(IsRegistered(cmd.Arg(0)));
00697 }
00698
00699
00700 void GtpEngine::CmdListCommands(GtpCommand& cmd)
00701 {
00702 cmd.CheckArgNone();
00703 typedef CallbackMap::const_iterator Iterator;
00704 for (Iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i)
00705 cmd << i->first << '\n';
00706 }
00707
00708
00709 void GtpEngine::CmdName(GtpCommand& cmd)
00710 {
00711 cmd.CheckArgNone();
00712 cmd << "Unknown";
00713 }
00714
00715
00716 void GtpEngine::CmdProtocolVersion(GtpCommand& cmd)
00717 {
00718 cmd.CheckArgNone();
00719 cmd << "2";
00720 }
00721
00722
00723 void GtpEngine::CmdQuit(GtpCommand& cmd)
00724 {
00725 cmd.CheckArgNone();
00726 SetQuit();
00727 }
00728
00729
00730
00731
00732
00733 void GtpEngine::CmdVersion(GtpCommand& cmd)
00734 {
00735 cmd.CheckArgNone();
00736 }
00737
00738 string GtpEngine::ExecuteCommand(const string& cmdline, ostream& log)
00739 {
00740 if (! IsCommandLine(cmdline))
00741 throw GtpFailure() << "Bad command: " << cmdline;
00742 GtpCommand cmd;
00743 cmd.Init(cmdline);
00744 log << cmd.Line() << '\n';
00745 GtpOutputStream gtpLog(log);
00746 bool status = HandleCommand(cmd, gtpLog);
00747 string response = cmd.Response();
00748 if (! status)
00749 throw GtpFailure() << "Executing " << cmd.Line() << " failed";
00750 return response;
00751 }
00752
00753 void GtpEngine::ExecuteFile(const string& name, ostream& log)
00754 {
00755 ifstream in(name.c_str());
00756 if (! in)
00757 throw GtpFailure() << "Cannot read " << name;
00758 string line;
00759 GtpCommand cmd;
00760 GtpOutputStream gtpLog(log);
00761 while (getline(in, line))
00762 {
00763 if (! IsCommandLine(line))
00764 continue;
00765 cmd.Init(line);
00766 log << cmd.Line() << '\n';
00767
00768 bool status = HandleCommand(cmd, gtpLog);
00769 if (! status)
00770 throw GtpFailure() << "Executing " << cmd.Line() << " failed";
00771 }
00772 }
00773
00774 bool GtpEngine::HandleCommand(GtpCommand& cmd, GtpOutputStream& out)
00775 {
00776 BeforeHandleCommand();
00777 bool status = true;
00778 string response;
00779 try
00780 {
00781 CallbackMap::const_iterator pos = m_callbacks.find(cmd.Name());
00782 if (pos == m_callbacks.end())
00783 {
00784 status = false;
00785 response = "unknown command: " + cmd.Name();
00786 }
00787 else
00788 {
00789 GtpCallbackBase* callback = pos->second;
00790 (*callback)(cmd);
00791 response = cmd.Response();
00792 }
00793 }
00794 catch (const GtpFailure& failure)
00795 {
00796 status = false;
00797 response = failure.Response();
00798 }
00799 response = ReplaceEmptyLines(response);
00800 BeforeWritingResponse();
00801 ostringstream ostr;
00802 ostr << (status ? '=' : '?') << cmd.ID() << ' ' << response;
00803 size_t size = response.size();
00804 if (size == 0 || response[size - 1] != '\n')
00805 ostr << '\n';
00806 ostr << '\n' << flush;
00807 out.Write(ostr.str());
00808 out.Flush();
00809 return status;
00810 }
00811
00812 bool GtpEngine::IsRegistered(const string& command) const
00813 {
00814 return (m_callbacks.find(command) != m_callbacks.end());
00815 }
00816
00817 void GtpEngine::MainLoop()
00818 {
00819 m_quit = false;
00820 #if GTPENGINE_PONDER
00821 PonderThread ponderThread(*this);
00822 #endif
00823 #if GTPENGINE_INTERRUPT
00824 ReadThread readThread(m_in, *this);
00825 #endif
00826 GtpCommand cmd;
00827 while (true)
00828 {
00829 #if GTPENGINE_PONDER
00830 ponderThread.StartPonder();
00831 #endif
00832 #if GTPENGINE_INTERRUPT
00833 bool isStreamGood = readThread.ReadCommand(cmd);
00834 #else
00835 bool isStreamGood = ReadCommand(cmd, m_in);
00836 #endif
00837 #if GTPENGINE_PONDER
00838 ponderThread.StopPonder();
00839 #endif
00840 if (isStreamGood)
00841 HandleCommand(cmd, m_out);
00842 else
00843 SetQuit();
00844 if (m_quit)
00845 {
00846 #if GTPENGINE_PONDER
00847 ponderThread.Quit();
00848 #endif
00849 #if GTPENGINE_INTERRUPT
00850 readThread.JoinThread();
00851 #endif
00852 break;
00853 }
00854 }
00855 }
00856
00857 void GtpEngine::Register(const string& command, GtpCallbackBase* callback)
00858 {
00859 CallbackMap::iterator pos = m_callbacks.find(command);
00860 if (pos != m_callbacks.end())
00861 {
00862 delete pos->second;
00863 #ifndef NDEBUG
00864 pos->second = 0;
00865 #endif
00866 m_callbacks.erase(pos);
00867 }
00868 m_callbacks.insert(make_pair(command, callback));
00869 }
00870
00871 void GtpEngine::SetQuit()
00872 {
00873 m_quit = true;
00874 }
00875
00876 bool GtpEngine::IsQuitSet() const
00877 {
00878 return m_quit;
00879 }
00880
00881
00882 #if GTPENGINE_PONDER
00883
00884 void GtpEngine::Ponder()
00885 {
00886
00887 #ifdef GTPENGINE_TEST
00888 cerr << "GtpEngine::Ponder()\n";
00889 #endif
00890 }
00891
00892 void GtpEngine::StopPonder()
00893 {
00894
00895 #ifdef GTPENGINE_TEST
00896 cerr << "GtpEngine::StopPonder()\n";
00897 #endif
00898 }
00899
00900 void GtpEngine::InitPonder()
00901 {
00902
00903 #ifdef GTPENGINE_TEST
00904 cerr << "GtpEngine::InitPonder()\n";
00905 #endif
00906 }
00907
00908 #endif // GTPENGINE_PONDER
00909
00910
00911 #if GTPENGINE_INTERRUPT
00912
00913 void GtpEngine::Interrupt()
00914 {
00915
00916 #ifdef GTPENGINE_TEST
00917 cerr << "GtpEngine::Interrupt()\n";
00918 #endif
00919 }
00920
00921 #endif // GTPENGINE_INTERRUPT
00922
00923
00924
00925 #ifdef GTPENGINE_MAIN
00926
00927
00928 int main()
00929 {
00930 try
00931 {
00932 GtpEngine engine(cin, cout);
00933 engine.MainLoop();
00934 }
00935 catch (const exception& e)
00936 {
00937 cerr << e.what() << '\n';
00938 return 1;
00939 }
00940 return 0;
00941 }
00942
00943 #endif // GTPENGINE_MAIN
00944
00945