Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

HexHtpEngine.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file HexHtpEngine.cpp
00003  */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgSystem.h"
00007 
00008 #include <cmath>
00009 #include <iomanip>
00010 #include <sstream>
00011 #include <limits>
00012 #include <time.h>
00013 #include <signal.h>
00014 #include <iostream>
00015 
00016 #include "SgGameReader.h"
00017 #include "SgNode.h"
00018 #include "SgTimer.h"
00019 
00020 #include "BitsetIterator.hpp"
00021 #include "BoardUtils.hpp"
00022 #include "Groups.hpp"
00023 #include "HexSgUtil.hpp"
00024 #include "HexProgram.hpp"
00025 #include "HexHtpEngine.hpp"
00026 #include "Time.hpp"
00027 
00028 using namespace benzene;
00029 
00030 //----------------------------------------------------------------------------
00031 
00032 HexHtpEngine::HexHtpEngine(int boardsize)
00033     : GtpEngine(),
00034       m_board(boardsize, boardsize),
00035       m_game(m_board)
00036 {
00037     RegisterCmd("all_legal_moves", &HexHtpEngine::CmdAllLegalMoves);
00038     RegisterCmd("board_id", &HexHtpEngine::CmdBoardID);
00039     RegisterCmd("boardsize", &HexHtpEngine::CmdNewGame);
00040     RegisterCmd("clear_board", &HexHtpEngine::CmdClearBoard);
00041     RegisterCmd("exec", &HexHtpEngine::CmdExec);
00042     RegisterCmd("final_score", &HexHtpEngine::CmdFinalScore);
00043     RegisterCmd("genmove", &HexHtpEngine::CmdGenMove);
00044     RegisterCmd("reg_genmove", &HexHtpEngine::CmdRegGenMove);    
00045 #if GTPENGINE_INTERRUPT
00046     RegisterCmd("gogui-interrupt", &HexHtpEngine::CmdInterrupt);
00047 #endif
00048     RegisterCmd("loadsgf", &HexHtpEngine::CmdLoadSgf);
00049     RegisterCmd("name", &HexHtpEngine::CmdName);
00050     RegisterCmd("param_game", &HexHtpEngine::CmdParamGame);
00051     RegisterCmd("play", &HexHtpEngine::CmdPlay);
00052     RegisterCmd("showboard", &HexHtpEngine::CmdShowboard);
00053     RegisterCmd("time_left", &HexHtpEngine::CmdTimeLeft);
00054     RegisterCmd("undo", &HexHtpEngine::CmdUndo);
00055     RegisterCmd("version", &HexHtpEngine::CmdVersion);
00056 
00057     NewGame(m_board.Width(), m_board.Height());
00058 }
00059 
00060 HexHtpEngine::~HexHtpEngine()
00061 {
00062 }
00063 
00064 //----------------------------------------------------------------------------
00065 
00066 void HexHtpEngine::RegisterCmd(const std::string& name,
00067                                GtpCallback<HexHtpEngine>::Method method)
00068 {
00069     Register(name, new GtpCallback<HexHtpEngine>(this, method));
00070 }
00071 
00072 void HexHtpEngine::Play(HexColor color, HexPoint move)
00073 {
00074     bool illegal = false;
00075     std::string reason = "";
00076 
00077     // do nothing if a resign move
00078     if (move == RESIGN)
00079         return;
00080 
00081     Game::ReturnType result = m_game.PlayMove(color, move);
00082     if (result == Game::INVALID_MOVE) {
00083         illegal = true;
00084         reason = " (invalid)";
00085     } else if (result == Game::OCCUPIED_CELL) {
00086         illegal = true;
00087         reason = " (occupied)";
00088     }
00089     
00090     if (illegal) {
00091         throw HtpFailure() << "illegal move: " << ' '
00092                            << color << ' ' << move << reason;
00093     }
00094 }
00095 
00096 void HexHtpEngine::NewGame(int width, int height)
00097 {
00098     if (width != m_game.Board().Width() || 
00099         height != m_game.Board().Height()) 
00100     {
00101         m_board = StoneBoard(width, height);
00102         m_game.SetBoard(m_board);
00103     } 
00104     m_game.NewGame();
00105 }
00106 
00107 void HexHtpEngine::BeforeHandleCommand()
00108 {
00109     SgSetUserAbort(false);
00110 }
00111 
00112 void HexHtpEngine::BeforeWritingResponse()
00113 {
00114 }
00115 
00116 //----------------------------------------------------------------------------
00117 
00118 /** Returns program's name. */
00119 void HexHtpEngine::CmdName(HtpCommand& cmd)
00120 {
00121     cmd << HexProgram::Get().getName();
00122 }
00123 
00124 /** Returns program's version. */
00125 void HexHtpEngine::CmdVersion(HtpCommand& cmd)
00126 {
00127     cmd << HexProgram::Get().getVersion();
00128 }
00129 
00130 /** Executes HTP commands contained in given file. */
00131 void HexHtpEngine::CmdExec(HtpCommand& cmd)
00132 {
00133     cmd.CheckNuArg(1);
00134     std::string filename = cmd.Arg(0);
00135 
00136     try {
00137         ExecuteFile(filename, std::cerr);
00138     }
00139     catch (std::exception& e) {
00140         LogInfo() << "Errors occured." << '\n';
00141     }
00142 }
00143 
00144 #if GTPENGINE_INTERRUPT
00145 
00146 /** Does nothing, but lets gogui know we can be interrupted with the 
00147     "# interrupt" gtp command. */
00148 void HexHtpEngine::CmdInterrupt(HtpCommand& cmd)
00149 {
00150     cmd.CheckArgNone();
00151 }
00152 
00153 #endif
00154 
00155 /** Starts new game with the given board size. */
00156 void HexHtpEngine::CmdNewGame(HtpCommand& cmd)
00157 {
00158     cmd.CheckNuArgLessEqual(2);
00159     if (cmd.NuArg() == 0)
00160         throw HtpFailure() << "Must specify board dimensions!";
00161     int width = cmd.IntArg(0, 1, MAX_WIDTH);
00162     int height = width;
00163     if (cmd.NuArg() == 2)
00164         height = cmd.IntArg(1, 1, MAX_HEIGHT);
00165     NewGame(width, height);
00166 }
00167 
00168 /** Starts a new game with the same board size. */
00169 void HexHtpEngine::CmdClearBoard(HtpCommand& cmd)
00170 {
00171     cmd.CheckArgNone();
00172     NewGame(m_board.Width(), m_board.Height());
00173 }
00174 
00175 /** Plays a move. */
00176 void HexHtpEngine::CmdPlay(HtpCommand& cmd)
00177 {
00178     cmd.CheckNuArg(2);
00179     Play(HtpUtil::ColorArg(cmd, 0), HtpUtil::MoveArg(cmd, 1));
00180 }
00181 
00182 /** Generates a move and handles time remaining. */
00183 void HexHtpEngine::CmdGenMove(HtpCommand& cmd)
00184 {
00185     cmd.CheckNuArg(1);
00186     if (GameUtil::IsGameOver(m_game))
00187         cmd << RESIGN;
00188     else
00189     {
00190         HexColor color = HtpUtil::ColorArg(cmd, 0);
00191         SgTime::SetDefaultMode(SG_TIME_REAL);
00192         SgTimer timer;
00193         timer.Start();
00194         double oldTimeRemaining = m_game.TimeRemaining(color);
00195         HexPoint move = GenMove(color, true);
00196         timer.Stop();
00197         
00198         m_game.SetTimeRemaining(color, oldTimeRemaining - timer.GetTime());
00199         if (m_game.TimeRemaining(color) < 0)
00200             LogWarning() << "**** FLAG DROPPED ****\n";
00201         
00202         Play(color, move);
00203         cmd << move;
00204     }
00205 }
00206 
00207 /** Generates a move, but does not play it. Sets random seed. */
00208 void HexHtpEngine::CmdRegGenMove(HtpCommand& cmd)
00209 {
00210     cmd.CheckNuArg(1);
00211     SgRandom::SetSeed(SgRandom::Seed());
00212     if (GameUtil::IsGameOver(m_game))
00213         cmd << RESIGN;
00214     else
00215     {
00216         HexPoint move = GenMove(HtpUtil::ColorArg(cmd, 0), false);
00217         cmd << move;
00218     }
00219 }
00220 
00221 /** Undo the last move. */
00222 void HexHtpEngine::CmdUndo(HtpCommand& cmd)
00223 {
00224     cmd.CheckNuArg(0);
00225     m_game.UndoMove();
00226 }
00227 
00228 /** Displays the board. */
00229 void HexHtpEngine::CmdShowboard(HtpCommand& cmd)
00230 {
00231     cmd << "\n";
00232     cmd << m_game.Board();
00233 }
00234 
00235 /** Outputs BoardID of current position. */
00236 void HexHtpEngine::CmdBoardID(HtpCommand& cmd)
00237 {
00238     cmd.CheckNuArg(0);
00239     cmd << m_game.Board().GetBoardIDString();
00240 }
00241 
00242 /** Displays time left for both players or given player. */
00243 void HexHtpEngine::CmdTimeLeft(HtpCommand& cmd)
00244 {
00245     cmd.CheckNuArgLessEqual(2);
00246     if (cmd.NuArg() == 0) 
00247     {
00248         cmd << "Black: " << Time::Formatted(m_game.TimeRemaining(BLACK)) 
00249             << ", "
00250             << "White: " << Time::Formatted(m_game.TimeRemaining(WHITE));
00251     }
00252     else if (cmd.NuArg() == 1) 
00253     {
00254         HexColor color = HtpUtil::ColorArg(cmd, 0);
00255         cmd << Time::Formatted(m_game.TimeRemaining(color));
00256     } 
00257     else 
00258     {
00259         HexColor color = HtpUtil::ColorArg(cmd, 0);
00260         m_game.SetTimeRemaining(color, cmd.IntArg(1));
00261     }
00262 }
00263 
00264 /** Returns a string with what we think the outcome of the game is.
00265     Value will be "B+" for a black win, and "W+" for a white win.
00266  */
00267 void HexHtpEngine::CmdFinalScore(HtpCommand& cmd)
00268 {
00269     Groups groups;
00270     GroupBuilder::Build(m_game.Board(), groups);
00271     HexColor winner = groups.GetWinner();
00272     std::string ret = "cannot score";
00273     if (winner == BLACK)
00274         ret = "B+";
00275     else if (winner == WHITE)
00276         ret = "W+";
00277     cmd << ret;
00278 }
00279 
00280 /** Returns a list of all legal moves on current board position. */
00281 void HexHtpEngine::CmdAllLegalMoves(HtpCommand& cmd)
00282 {
00283     int c = 0;
00284     bitset_t legal = m_game.Board().GetLegal();
00285     for (BitsetIterator i(legal); i; ++i) 
00286     {
00287         cmd << " " << *i;
00288         if ((++c % 10) == 0) cmd << "\n";
00289     }
00290 }
00291 
00292 /** @bug This won't work if we're overwriting previosly played stones! */
00293 void HexHtpEngine::SetPosition(const SgNode* node)
00294 {
00295     std::vector<HexPoint> black, white, empty;
00296     HexSgUtil::GetSetupPosition(node, m_game.Board().Height(), 
00297                                 black, white, empty);
00298     for (unsigned i=0; ; ++i) 
00299     {
00300         bool bdone = (i >= black.size());
00301         bool wdone = (i >= white.size());
00302         if (!bdone) Play(BLACK, black[i]);
00303         if (!wdone) Play(WHITE, white[i]);
00304         if (bdone && wdone) break;
00305     }
00306 }
00307 
00308 /** Loads game or position from given sgf. Sets position to given move
00309     number or the last move of the game if none is given.
00310 */
00311 void HexHtpEngine::CmdLoadSgf(HtpCommand& cmd)
00312 {
00313     cmd.CheckNuArgLessEqual(2);
00314     std::string filename = cmd.Arg(0);
00315     int movenumber = 1024;
00316     if (cmd.NuArg() == 2) 
00317         movenumber = cmd.IntArg(1, 0);
00318 
00319     std::ifstream file(filename.c_str());
00320     if (!file) {
00321         throw HtpFailure() << "cannot load file";
00322         return;
00323     }
00324 
00325     SgGameReader sgreader(file, 11);
00326     SgNode* root = sgreader.ReadGame(); 
00327     if (root == 0) {
00328         throw HtpFailure() << "cannot load file";
00329         return;
00330     }
00331     sgreader.PrintWarnings(std::cerr);
00332 
00333     int size = root->GetIntProp(SG_PROP_SIZE);
00334 
00335     NewGame(size, size);
00336 
00337     const StoneBoard& brd = m_game.Board();
00338 
00339     if (HexSgUtil::NodeHasSetupInfo(root)) {
00340         LogWarning() << "Root has setup info!" << '\n';
00341         SetPosition(root);
00342     }
00343 
00344     // play movenumber moves; stop if we hit the end of the game
00345     SgNode* cur = root;
00346     for (int mn=0; mn<movenumber;) {
00347         cur = cur->NodeInDirection(SgNode::NEXT);
00348         if (!cur) break;
00349 
00350         if (HexSgUtil::NodeHasSetupInfo(cur)) {
00351             SetPosition(cur);
00352             continue;
00353         } else if (!cur->HasNodeMove()) {
00354             continue;
00355         }
00356 
00357         HexColor color = HexSgUtil::SgColorToHexColor(cur->NodePlayer());
00358         HexPoint point = HexSgUtil::SgPointToHexPoint(cur->NodeMove(), 
00359                                                       brd.Height());
00360         Play(color, point);
00361         mn++;
00362     }
00363 }
00364 
00365 /** Displays/changes parameters relating to the current game. 
00366 
00367     Parameters:
00368     @arg @c allow_swap See Game::AllowSwap
00369     @arg @c game_time See Game::GameTime
00370 */
00371 void HexHtpEngine::CmdParamGame(HtpCommand& cmd)
00372 {
00373     if (cmd.NuArg() == 0)
00374     {
00375         cmd << "\n"
00376             << "[bool] allow_swap "
00377             << m_game.AllowSwap() << '\n'
00378             << "[string] game_time "
00379             << m_game.GameTime() << '\n';
00380     }
00381     else if (cmd.NuArg() == 2)
00382     {
00383         std::string name = cmd.Arg(0);
00384         if (name == "allow_swap")
00385             m_game.SetAllowSwap(cmd.BoolArg(1));
00386         else if (name == "game_time")
00387         {
00388             if (!m_game.History().empty())
00389                 throw HtpFailure("Cannot set game time if game started!");
00390             m_game.SetGameTime(cmd.FloatArg(1));
00391         }
00392     }
00393     else
00394         throw HtpFailure("Expected 0 or 2 arguments");
00395 }
00396 
00397 #if GTPENGINE_INTERRUPT
00398 
00399 void HexHtpEngine::Interrupt()
00400 {
00401     SgSetUserAbort(true);
00402 }
00403 
00404 #endif // GTPENGINE_INTERRUPT
00405 
00406 //----------------------------------------------------------------------------
00407 
00408 HexColor HtpUtil::ColorArg(const HtpCommand& cmd, std::size_t number)
00409 {
00410     std::string value = cmd.ArgToLower(number);
00411     if (value == "e" || value == "empty")
00412             return EMPTY;
00413     if (value == "b" || value == "black")
00414         return BLACK;
00415     if (value == "w" || value == "white")
00416         return WHITE;
00417     throw HtpFailure() << "argument " << (number + 1) << " must be color";
00418 }
00419 
00420 HexPoint HtpUtil::MoveArg(const HtpCommand& cmd, std::size_t number)
00421 {
00422     return HexPointUtil::FromString(cmd.ArgToLower(number));
00423 }
00424 
00425 //----------------------------------------------------------------------------


6 Jan 2011 Doxygen 1.6.3