00001
00002
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 }
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
00129
00130
00131
00132
00133
00134
00135
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
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
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
00382
00383
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
00407
00408
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
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
00462
00463
00464
00465
00466
00467
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