Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoBook.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoBook.cpp
00003 */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgSystem.h"
00007 #include "GoBook.h"
00008 
00009 #include <algorithm>
00010 #include <fstream>
00011 #include <sstream>
00012 #include "GoBoard.h"
00013 #include "GoBoardUtil.h"
00014 #include "GoGtpCommandUtil.h"
00015 #include "GoGtpEngine.h"
00016 #include "GoModBoard.h"
00017 #include "SgDebug.h"
00018 #include "SgException.h"
00019 #include "SgWrite.h"
00020 
00021 using namespace std;
00022 
00023 //----------------------------------------------------------------------------
00024 
00025 namespace {
00026 
00027 template<typename T>
00028 bool Contains(const vector<T>& v, const T& elem)
00029 {
00030     typename vector<T>::const_iterator end = v.end();
00031     return (find(v.begin(), end, elem) != end);
00032 }
00033 
00034 template<typename T>
00035 bool Erase(vector<T>& v, const T& elem)
00036 {
00037     typename vector<T>::iterator end = v.end();
00038     typename vector<T>::iterator pos = find(v.begin(), end, elem);
00039     if (pos != end)
00040     {
00041         v.erase(pos);
00042         return true;
00043     }
00044     return false;
00045 }
00046 
00047 vector<SgPoint> GetSequence(const GoBoard& bd)
00048 {
00049     vector<SgPoint> result;
00050     SgBlackWhite toPlay = SG_BLACK;
00051     for (int i = 0; i < bd.MoveNumber(); ++i)
00052     {
00053         GoPlayerMove move = bd.Move(i);
00054         if (move.Color() != toPlay)
00055             throw SgException("cannot add position "
00056                               "(non-alternating move sequence)");
00057         result.push_back(move.Point());
00058         toPlay = SgOppBW(toPlay);
00059     }
00060     return result;
00061 }
00062 
00063 } // namepsace
00064 
00065 //----------------------------------------------------------------------------
00066 
00067 void GoBook::Entry::ApplyTo(GoBoard& bd) const
00068 {
00069     if (bd.Size() != m_size)
00070         bd.Init(m_size);
00071     GoBoardUtil::UndoAll(bd);
00072     for (vector<SgPoint>::const_iterator it = m_sequence.begin();
00073          it != m_sequence.end(); ++it)
00074     {
00075         SG_ASSERT(bd.IsLegal(*it));
00076         bd.Play(*it);
00077     }
00078 }
00079 
00080 //----------------------------------------------------------------------------
00081 
00082 void GoBook::Add(const GoBoard& bd, SgPoint move)
00083 {
00084     if (move != SG_PASS && bd.Occupied(move))
00085         throw SgException("point is not empty");
00086     if (! bd.IsLegal(move))
00087         throw SgException("move is not legal");
00088     const GoBook::MapEntry* mapEntry = LookupEntry(bd);
00089     if (mapEntry == 0)
00090     {
00091         vector<SgPoint> moves;
00092         moves.push_back(move);
00093         GoBoard tempBoard;
00094         InsertEntry(GetSequence(bd), moves, bd.Size(), tempBoard, 0);
00095     }
00096     else
00097     {
00098         size_t id = mapEntry->m_id;
00099         SG_ASSERT(id < m_entries.size());
00100         Entry& entry = m_entries[id];
00101         int invRotation = SgPointUtil::InvRotation(mapEntry->m_rotation);
00102         SgPoint rotMove = SgPointUtil::Rotate(invRotation, move, bd.Size());
00103         if (! Contains(entry.m_moves, rotMove))
00104             entry.m_moves.push_back(rotMove);
00105     }
00106 }
00107 
00108 void GoBook::Clear()
00109 {
00110     m_entries.clear();
00111     m_map.clear();
00112 }
00113 
00114 void GoBook::Delete(const GoBoard& bd, SgPoint move)
00115 {
00116     const GoBook::MapEntry* mapEntry = LookupEntry(bd);
00117     if (mapEntry == 0)
00118         return;
00119     size_t id = mapEntry->m_id;
00120     SG_ASSERT(id < m_entries.size());
00121     Entry& entry = m_entries[id];
00122     int invRotation = SgPointUtil::InvRotation(mapEntry->m_rotation);
00123     SgPoint rotMove = SgPointUtil::Rotate(invRotation, move, bd.Size());
00124     if (! Erase(entry.m_moves, rotMove))
00125         throw SgException("GoBook::Delete: move not found");
00126 }
00127 
00128 /** Insert a new position entry and all its transformations
00129     @param sequence A move sequence that leads to the position
00130     @param moves The moves to play in this position
00131     @param size
00132     @param tempBoard A local temporary work board, reused for efficiency
00133     (does not have to be initialized)
00134     @param line Line number of entry from the file (0 if unknown or entry is
00135     not from a file)
00136 */
00137 void GoBook::InsertEntry(const vector<SgPoint>& sequence,
00138                          const vector<SgPoint>& moves, int size,
00139                          GoBoard& tempBoard, int line)
00140 {
00141     if (moves.size() == 0)
00142         ThrowError("Line contains no moves");
00143     if (tempBoard.Size() != size)
00144         tempBoard.Init(size);
00145     Entry entry;
00146     entry.m_size = size;
00147     entry.m_line = line;
00148     entry.m_sequence = sequence;
00149     entry.m_moves = moves;
00150     m_entries.push_back(entry);
00151     size_t id = m_entries.size() - 1;
00152     vector<Map::iterator> newEntries;
00153     for (int rot = 0; rot < 8; ++rot)
00154     {
00155         GoBoardUtil::UndoAll(tempBoard);
00156         for (vector<SgPoint>::const_iterator it = sequence.begin();
00157              it != sequence.end(); ++it)
00158         {
00159             SgPoint p = SgPointUtil::Rotate(rot, *it, size);
00160             if (! tempBoard.IsLegal(p))
00161                 ThrowError("Illegal move in variation");
00162             tempBoard.Play(p);
00163         }
00164         // It is enough to check the moves for legality for one rotation
00165         if (rot == 0)
00166             for (vector<SgPoint>::const_iterator it = moves.begin();
00167                  it != moves.end(); ++it)
00168                 if (! tempBoard.IsLegal(SgPointUtil::Rotate(rot, *it, size)))
00169                     ThrowError("Illegal move in move list");
00170         MapEntry mapEntry;
00171         mapEntry.m_size = size;
00172         mapEntry.m_id = id;
00173         mapEntry.m_rotation = rot;
00174         SgHashCode hashCode = tempBoard.GetHashCodeInclToPlay();
00175         pair<Map::iterator,Map::iterator> e = m_map.equal_range(hashCode);
00176         bool isInNewEntries = false;
00177         for (Map::iterator it = e.first; it != e.second; ++it)
00178             if (it->second.m_size == size)
00179             {
00180                 if (! Contains(newEntries, it))
00181                 {
00182                     ostringstream o;
00183                     o << "Entry duplicates line "
00184                       << m_entries[it->second.m_id].m_line;
00185                     ThrowError(o.str());
00186                 }
00187                 isInNewEntries = true;
00188                 break;
00189             }
00190         if (! isInNewEntries)
00191         {
00192             Map::iterator newIt =
00193                 m_map.insert(Map::value_type(hashCode, mapEntry));
00194             newEntries.push_back(newIt);
00195         }
00196     }
00197 }
00198 
00199 int GoBook::Line(const GoBoard& bd) const
00200 {
00201     const GoBook::MapEntry* mapEntry = LookupEntry(bd);
00202     if (mapEntry == 0)
00203         return 0;
00204     size_t id = mapEntry->m_id;
00205     SG_ASSERT(id < m_entries.size());
00206     return m_entries[id].m_line;
00207 }
00208 
00209 const GoBook::MapEntry* GoBook::LookupEntry(const GoBoard& bd) const
00210 {
00211     SgHashCode hashCode = bd.GetHashCodeInclToPlay();
00212     typedef Map::const_iterator Iterator;
00213     pair<Iterator,Iterator> e = m_map.equal_range(hashCode);
00214     int size = bd.Size();
00215     for (Iterator it = e.first; it != e.second; ++it)
00216         if (it->second.m_size == size)
00217             return &it->second;
00218     return 0;
00219 }
00220 
00221 SgPoint GoBook::LookupMove(const GoBoard& bd) const
00222 {
00223     vector<SgPoint> moves = LookupAllMoves(bd);
00224     int nuMoves = moves.size();
00225     if (nuMoves == 0)
00226         return SG_NULLMOVE;
00227     SgPoint p = moves[rand() % nuMoves];
00228     return p;
00229 }
00230 
00231 vector<SgPoint> GoBook::LookupAllMoves(const GoBoard& bd) const
00232 {
00233     vector<SgPoint> result;
00234     const GoBook::MapEntry* mapEntry = LookupEntry(bd);
00235     if (mapEntry == 0)
00236         return result;
00237     size_t id = mapEntry->m_id;
00238     SG_ASSERT(id < m_entries.size());
00239     const vector<SgPoint>& moves = m_entries[id].m_moves;
00240     const int rotation = mapEntry->m_rotation;
00241     const int size = mapEntry->m_size;
00242     for (vector<SgPoint>::const_iterator it = moves.begin();
00243          it != moves.end(); ++it)
00244     {
00245         SgPoint p = SgPointUtil::Rotate(rotation, *it, size);
00246         if (! bd.IsLegal(p))
00247         {
00248             // Should not happen with 64-bit hashes, but not impossible
00249             SgWarning() << "illegal book move (hash code collision?)\n";
00250             result.clear();
00251             break;
00252         }
00253         result.push_back(p);
00254     }
00255     return result;
00256 }
00257 
00258 void GoBook::ParseLine(const string& line, GoBoard& tempBoard)
00259 {
00260     istringstream in(line);
00261     int size;
00262     in >> size;
00263     if (size < 1)
00264         ThrowError("Invalid size");
00265     if (size > SG_MAX_SIZE && ! m_warningMaxSizeShown)
00266     {
00267         SgDebug() << "GoBook::ParseLine: Ignoring size=" << size << '\n';
00268         m_warningMaxSizeShown = true;
00269         return;
00270     }
00271     vector<SgPoint> variation = ReadPoints(in);
00272     vector<SgPoint> moves = ReadPoints(in);
00273     InsertEntry(variation, moves, size, tempBoard, m_lineCount);
00274 }
00275 
00276 void GoBook::Read(istream& in, const string& streamName)
00277 {
00278     Clear();
00279     m_warningMaxSizeShown = false;
00280     m_streamName = streamName;
00281     m_lineCount = 0;
00282     GoBoard tempBoard;
00283     while (in)
00284     {
00285         string line;
00286         getline(in, line);
00287         ++m_lineCount;
00288         if (line == "")
00289             continue;
00290         ParseLine(line, tempBoard);
00291     }
00292 }
00293 
00294 void GoBook::Read(const string& filename)
00295 {
00296     ifstream in(filename.c_str());
00297     if (! in)
00298         throw SgException("Cannot find file " + filename);
00299     Read(in, filename);
00300 }
00301 
00302 vector<SgPoint> GoBook::ReadPoints(istream& in) const
00303 {
00304     vector<SgPoint> result;
00305     while (true)
00306     {
00307         string s;
00308         in >> s;
00309         if (! in || s == "|")
00310             break;
00311         istringstream in2(s);
00312         SgPoint p;
00313         in2 >> SgReadPoint(p);
00314         if (! in2)
00315             ThrowError("Invalid point");
00316         result.push_back(p);
00317     }
00318     return result;
00319 }
00320 
00321 void GoBook::ThrowError(const string& message) const
00322 {
00323     ostringstream out;
00324     if (m_streamName != "")
00325         out << m_streamName << ':';
00326     out << m_lineCount << ": " << message;
00327     throw SgException(out.str());
00328 }
00329 
00330 void GoBook::Write(ostream& out) const
00331 {
00332     for (vector<Entry>::const_iterator it = m_entries.begin();
00333          it != m_entries.end(); ++it)
00334     {
00335         if (it->m_moves.empty())
00336         {
00337             SgDebug() << "pruning empty entry line=" << it->m_line << '\n';
00338             continue;
00339         }
00340         out << it->m_size << ' ';
00341         for (vector<SgPoint>::const_iterator it2 = it->m_sequence.begin();
00342              it2 != it->m_sequence.end(); ++it2)
00343             out << SgWritePoint(*it2) << ' ';
00344         out << '|';
00345         for (vector<SgPoint>::const_iterator it2 = it->m_moves.begin();
00346              it2 != it->m_moves.end(); ++it2)
00347             out << ' ' << SgWritePoint(*it2);
00348         out << '\n';
00349     }
00350 }
00351 
00352 void GoBook::WriteInfo(ostream& out) const
00353 {
00354     out << SgWriteLabel("NuBasic") << m_entries.size() << '\n'
00355         << SgWriteLabel("NuTransformed") << m_map.size() << '\n';
00356 }
00357 
00358 //----------------------------------------------------------------------------
00359 
00360 GoBookCommands::GoBookCommands(GoGtpEngine &engine, const GoBoard& bd, GoBook& book)
00361     : m_engine(engine),
00362       m_bd(bd),
00363       m_book(book)
00364 {
00365 }
00366 
00367 void GoBookCommands::AddGoGuiAnalyzeCommands(GtpCommand& cmd)
00368 {
00369     cmd <<
00370         "gfx/Book Add/book_add %p\n"
00371         "none/Book Clear/book_clear\n"
00372         "gfx/Book Delete/book_delete %p\n"
00373         "hstring/Book Info/book_info\n"
00374         "none/Book Load/book_load %r\n"
00375         "plist/Book Moves/book_moves\n"
00376         "gfx/Book Position/book_position\n"
00377         "none/Book Save/book_save\n"
00378         "none/Book Save As/book_save_as %w\n";
00379 }
00380 
00381 /** Add a move for the current position to the book.
00382     Arguments: point <br>
00383     Returns: Position information after the move deletion as in CmdPosition()
00384 */
00385 void GoBookCommands::CmdAdd(GtpCommand& cmd)
00386 {
00387     cmd.CheckNuArg(1);
00388     SgPoint p = GoGtpCommandUtil::PointArg(cmd, m_bd);
00389     try
00390     {
00391         m_book.Add(m_bd, p);
00392     }
00393     catch (const SgException& e)
00394     {
00395         throw GtpFailure(e.what());
00396     }
00397     PositionInfo(cmd);
00398 }
00399 
00400 void GoBookCommands::CmdClear(GtpCommand& cmd)
00401 {
00402     cmd.CheckArgNone();
00403     m_book.Clear();
00404 }
00405 
00406 /** Delete a move for the current position to the book.
00407     Arguments: point <br>
00408     Returns: Position information after the move deletion as in CmdPosition()
00409 */
00410 void GoBookCommands::CmdDelete(GtpCommand& cmd)
00411 {
00412     cmd.CheckNuArg(1);
00413     vector<SgPoint> moves = m_book.LookupAllMoves(m_bd);
00414     if (moves.empty())
00415         throw GtpFailure("book contains no moves for current position");
00416     SgPoint p = GoGtpCommandUtil::PointArg(cmd, m_bd);
00417     try
00418     {
00419         m_book.Delete(m_bd, p);
00420     }
00421     catch (const SgException& e)
00422     {
00423         throw GtpFailure(e.what()) << SgWritePoint(p)
00424                                    << " is not a book move";
00425     }
00426     PositionInfo(cmd);
00427 }
00428 
00429 /** Show information about current book. */
00430 void GoBookCommands::CmdInfo(GtpCommand& cmd)
00431 {
00432     cmd.CheckArgNone();
00433     cmd << SgWriteLabel("FileName") << m_fileName << '\n';
00434     m_book.WriteInfo(cmd);
00435 }
00436 
00437 void GoBookCommands::CmdLoad(GtpCommand& cmd)
00438 {
00439     cmd.CheckNuArg(1);
00440     m_fileName = cmd.Arg(0);
00441     try
00442     {
00443         m_book.Read(m_fileName);
00444     }
00445     catch (const SgException& e)
00446     {
00447         m_fileName = "";
00448         throw GtpFailure() << "loading opening book failed: " << e.what();
00449     }
00450 }
00451 
00452 void GoBookCommands::CmdMoves(GtpCommand& cmd)
00453 {
00454     cmd.CheckArgNone();
00455     vector<SgPoint> active = m_book.LookupAllMoves(m_bd);
00456     for (vector<SgPoint>::const_iterator it = active.begin();
00457          it != active.end(); ++it)
00458         cmd << SgWritePoint(*it) << ' ';
00459 }
00460 
00461 /** Show book information for current positions.
00462     This command is compatible with the GoGui analyze command type "gfx".
00463     Moves in the book for the current position are marked with a green
00464     color (active moves), moves that lead to a known position in the book
00465     with yellow (other moves). The status line shows the line number of the
00466     position in the file (0, if unknown) and the number of active and other
00467     moves.
00468 */
00469 void GoBookCommands::CmdPosition(GtpCommand& cmd)
00470 {
00471     cmd.CheckArgNone();
00472     PositionInfo(cmd);
00473 }
00474 
00475 void GoBookCommands::CmdSave(GtpCommand& cmd)
00476 {
00477     if (m_engine.MpiSynchronizer()->IsRootProcess())
00478     {
00479     cmd.CheckArgNone();
00480     if (m_fileName == "")
00481         throw GtpFailure("no filename associated with current book");
00482     ofstream out(m_fileName.c_str());
00483     m_book.Write(out);
00484     if (! out)
00485     {
00486         throw GtpFailure() << "error writing to file '" << m_fileName << "'";
00487     }
00488     }
00489 }
00490 
00491 void GoBookCommands::CmdSaveAs(GtpCommand& cmd)
00492 {
00493     if (m_engine.MpiSynchronizer()->IsRootProcess())
00494     {
00495     cmd.CheckNuArg(1);
00496     m_fileName = cmd.Arg(0);
00497     ofstream out(m_fileName.c_str());
00498     m_book.Write(out);
00499     if (! out)
00500     {
00501         m_fileName = "";
00502         throw GtpFailure("write error");
00503     }
00504     }
00505 }
00506 
00507 void GoBookCommands::PositionInfo(GtpCommand& cmd)
00508 {
00509     vector<SgPoint> active = m_book.LookupAllMoves(m_bd);
00510     vector<SgPoint> other;
00511     {
00512         GoModBoard modBoard(m_bd);
00513         GoBoard& bd = modBoard.Board();
00514         for (GoBoard::Iterator it(bd); it; ++it)
00515             if (bd.IsLegal(*it) && ! Contains(active, *it))
00516             {
00517                 bd.Play(*it);
00518                 if (! m_book.LookupAllMoves(bd).empty())
00519                     other.push_back(*it);
00520                 bd.Undo();
00521             }
00522     }
00523     cmd << "COLOR green";
00524     for (vector<SgPoint>::const_iterator it = active.begin();
00525          it != active.end(); ++it)
00526         cmd << ' ' << SgWritePoint(*it);
00527     cmd << "\nCOLOR yellow";
00528     for (vector<SgPoint>::const_iterator it = other.begin();
00529          it != other.end(); ++it)
00530         cmd << ' ' << SgWritePoint(*it);
00531     int line = m_book.Line(m_bd);
00532     cmd << "\nTEXT Line=" << line << " Active=" << active.size() << " Other="
00533         << other.size() << '\n';
00534 }
00535 
00536 void GoBookCommands::Register(GtpEngine& e)
00537 {
00538     e.Register("book_add", &GoBookCommands::CmdAdd, this);
00539     e.Register("book_clear", &GoBookCommands::CmdClear, this);
00540     e.Register("book_delete", &GoBookCommands::CmdDelete, this);
00541     e.Register("book_info", &GoBookCommands::CmdInfo, this);
00542     e.Register("book_load", &GoBookCommands::CmdLoad, this);
00543     e.Register("book_moves", &GoBookCommands::CmdMoves, this);
00544     e.Register("book_position", &GoBookCommands::CmdPosition, this);
00545     e.Register("book_save", &GoBookCommands::CmdSave, this);
00546     e.Register("book_save_as", &GoBookCommands::CmdSaveAs, this);
00547 }
00548 
00549 //----------------------------------------------------------------------------


17 Jun 2010 Doxygen 1.4.7