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


Sun Mar 13 2011 Doxygen 1.7.1