00001
00002
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 }
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
00128
00129
00130
00131
00132
00133
00134
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
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
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
00383
00384
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
00406
00407
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
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
00458
00459
00460
00461
00462
00463
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