00001 //---------------------------------------------------------------------------- 00002 /** @file Book.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include <cmath> 00007 #include <boost/numeric/conversion/bounds.hpp> 00008 00009 #include "BitsetIterator.hpp" 00010 #include "Book.hpp" 00011 #include "HexModState.hpp" 00012 #include "Time.hpp" 00013 00014 using namespace benzene; 00015 00016 //---------------------------------------------------------------------------- 00017 00018 /** Dump debug info. */ 00019 #define OUTPUT_OB_INFO 1 00020 00021 //---------------------------------------------------------------------------- 00022 00023 /** Current version for book databases. 00024 Update this if BookNode changes to prevent old out-of-date books 00025 being loaded. */ 00026 const std::string Book::BOOK_DB_VERSION = "BENZENE_BOOK_VER_0001"; 00027 00028 //---------------------------------------------------------------------------- 00029 00030 float BookUtil::Value(const SgBookNode& node, const HexState& state) 00031 { 00032 if (state.Position().IsLegal(SWAP_PIECES)) 00033 return std::max(node.m_value, BookUtil::InverseEval(node.m_value)); 00034 return node.m_value; 00035 } 00036 00037 float BookUtil::Score(const SgBookNode& node, const HexState& state, 00038 float countWeight) 00039 { 00040 float score = BookUtil::InverseEval(Value(node, state)); 00041 if (!node.IsTerminal()) 00042 score += log(node.m_count + 1) * countWeight; 00043 return score; 00044 } 00045 00046 float BookUtil::InverseEval(float eval) 00047 { 00048 if (HexEvalUtil::IsWinOrLoss(eval)) 00049 return -eval; 00050 if (eval < 0 || eval > 1) 00051 LogInfo() << "eval = " << eval << '\n'; 00052 HexAssert(0 <= eval && eval <= 1.0); 00053 return 1.0 - eval; 00054 } 00055 00056 //---------------------------------------------------------------------------- 00057 00058 int BookUtil::GetMainLineDepth(const Book& book, const HexState& origState) 00059 { 00060 int depth = 0; 00061 HexState state(origState); 00062 for (;;) 00063 { 00064 HexBookNode node; 00065 if (!book.Get(state, node)) 00066 break; 00067 HexPoint move = INVALID_POINT; 00068 float value = -1e9; 00069 for (BitsetIterator p(state.Position().GetEmpty()); p; ++p) 00070 { 00071 state.PlayMove(*p); 00072 HexBookNode child; 00073 if (book.Get(state, child)) 00074 { 00075 float curValue = InverseEval(BookUtil::Value(child, state)); 00076 if (curValue > value) 00077 { 00078 value = curValue; 00079 move = *p; 00080 } 00081 } 00082 state.UndoMove(*p); 00083 } 00084 if (move == INVALID_POINT) 00085 break; 00086 state.PlayMove(move); 00087 depth++; 00088 } 00089 return depth; 00090 } 00091 00092 namespace 00093 { 00094 00095 std::size_t TreeSize(const Book& book, HexState& state, 00096 StateMap<std::size_t>& solved) 00097 { 00098 if (solved.Exists(state)) 00099 return solved[state]; 00100 HexBookNode node; 00101 if (!book.Get(state, node)) 00102 return 0; 00103 std::size_t ret = 1; 00104 for (BitsetIterator p(state.Position().GetEmpty()); p; ++p) 00105 { 00106 state.PlayMove(*p); 00107 ret += TreeSize(book, state, solved); 00108 state.UndoMove(*p); 00109 } 00110 solved[state] = ret; 00111 return ret; 00112 } 00113 00114 } 00115 00116 std::size_t BookUtil::GetTreeSize(const Book& book, const HexState& origState) 00117 { 00118 StateMap<std::size_t> solved; 00119 HexState state(origState); 00120 return TreeSize(book, state, solved); 00121 } 00122 00123 //---------------------------------------------------------------------------- 00124 00125 HexPoint BookUtil::BestMove(const Book& book, const HexState& origState, 00126 unsigned minCount, float countWeight) 00127 { 00128 HexBookNode node; 00129 if (!book.Get(origState, node) || node.m_count < minCount) 00130 return INVALID_POINT; 00131 00132 float bestScore = -1e9; 00133 HexPoint bestChild = INVALID_POINT; 00134 HexState state(origState); 00135 for (BitsetIterator p(state.Position().GetEmpty()); p; ++p) 00136 { 00137 state.PlayMove(*p); 00138 HexBookNode child; 00139 if (book.Get(state, child)) 00140 { 00141 float score = BookUtil::Score(child, state, countWeight); 00142 if (score > bestScore) 00143 { 00144 bestScore = score; 00145 bestChild = *p; 00146 } 00147 } 00148 state.UndoMove(*p); 00149 } 00150 HexAssert(bestChild != INVALID_POINT); 00151 return bestChild; 00152 } 00153 00154 //---------------------------------------------------------------------------- 00155 00156 void BookUtil::DumpVisualizationData(const Book& book, 00157 const HexState& origState, int depth, 00158 std::ostream& out) 00159 { 00160 HexBookNode node; 00161 if (!book.Get(origState, node)) 00162 return; 00163 if (node.IsLeaf()) 00164 { 00165 out << BookUtil::Value(node, origState) << " " << depth << '\n'; 00166 return; 00167 } 00168 HexModState modState(origState); 00169 HexState state = modState.State(); 00170 for (BitsetIterator i(state.Position().GetEmpty()); i; ++i) 00171 { 00172 state.PlayMove( *i); 00173 DumpVisualizationData(book, state, depth + 1, out); 00174 state.UndoMove(*i); 00175 } 00176 } 00177 00178 namespace { 00179 00180 void DumpPolarizedLeafs(const Book& book, HexState& state, 00181 float polarization, StateSet& seen, 00182 PointSequence& pv, std::ostream& out, 00183 const StateSet& ignoreSet) 00184 { 00185 if (seen.Exists(state)) 00186 return; 00187 HexBookNode node; 00188 if (!book.Get(state, node)) 00189 return; 00190 if (fabs(BookUtil::Value(node, state) - 0.5) >= polarization 00191 && node.IsLeaf() && !node.IsTerminal() 00192 && ignoreSet.Exists(state)) 00193 { 00194 out << HexPointUtil::ToString(pv) << '\n'; 00195 seen.Insert(state); 00196 } 00197 else 00198 { 00199 if (node.IsLeaf() || node.IsTerminal()) 00200 return; 00201 for (BitsetIterator i(state.Position().GetEmpty()); i; ++i) 00202 { 00203 state.PlayMove(*i); 00204 pv.push_back(*i); 00205 DumpPolarizedLeafs(book, state, polarization, seen, pv, out, 00206 ignoreSet); 00207 pv.pop_back(); 00208 state.UndoMove(*i); 00209 } 00210 seen.Insert(state); 00211 } 00212 } 00213 00214 } 00215 00216 void BookUtil::DumpPolarizedLeafs(const Book& book, const HexState& origState, 00217 float polarization, PointSequence& pv, 00218 std::ostream& out, 00219 const StateSet& ignoreSet) 00220 { 00221 StateSet seen; 00222 HexModState modState(origState); 00223 HexState state = modState.State(); 00224 ::DumpPolarizedLeafs(book, state, polarization, seen, pv, out, ignoreSet); 00225 } 00226 00227 void BookUtil::ImportSolvedStates(Book& book, const ConstBoard& constBoard, 00228 std::istream& positions) 00229 { 00230 StoneBoard brd(constBoard.Width(), constBoard.Height()); 00231 HexState state(brd, FIRST_TO_PLAY); 00232 std::string text; 00233 std::size_t lineNumber = 0; 00234 std::size_t numParsed = 0; 00235 std::size_t numReplaced = 0; 00236 std::size_t numNew = 0; 00237 while (std::getline(positions, text)) 00238 { 00239 ++lineNumber; 00240 std::istringstream is(text); 00241 PointSequence points; 00242 HexColor winner = EMPTY; 00243 bool parsed = false; 00244 while (true) 00245 { 00246 std::string token; 00247 is >> token; 00248 if (token == "black") 00249 { 00250 winner = BLACK; 00251 parsed = true; 00252 break; 00253 } 00254 else if (token == "white") 00255 { 00256 winner = WHITE; 00257 parsed = true; 00258 break; 00259 } 00260 else 00261 { 00262 HexPoint p = HexPointUtil::FromString(token); 00263 if (p == INVALID_POINT) 00264 break; 00265 points.push_back(p); 00266 } 00267 } 00268 if (!parsed) 00269 { 00270 LogInfo() << "Skipping badly formed line " << lineNumber << ".\n"; 00271 continue; 00272 } 00273 HexAssert(winner != EMPTY); 00274 00275 ++numParsed; 00276 state.Position().StartNewGame(); 00277 state.SetToPlay(FIRST_TO_PLAY); 00278 for (std::size_t i = 0; i < points.size(); ++i) 00279 state.PlayMove(points[i]); 00280 HexEval ourValue = (state.ToPlay() == winner) 00281 ? IMMEDIATE_WIN : IMMEDIATE_LOSS; 00282 HexBookNode node; 00283 if (book.Get(state, node)) 00284 { 00285 HexAssert(node.IsLeaf()); 00286 HexAssert(!node.IsTerminal()); 00287 node.m_value = ourValue; 00288 ++numReplaced; 00289 } 00290 else 00291 { 00292 node = HexBookNode(ourValue); 00293 ++numNew; 00294 } 00295 book.Put(state, node); 00296 } 00297 book.Flush(); 00298 LogInfo() << " Lines: " << lineNumber << '\n'; 00299 LogInfo() << " Parsed: " << numParsed << '\n'; 00300 LogInfo() << "Replaced: " << numReplaced << '\n'; 00301 LogInfo() << " New: " << numNew << '\n'; 00302 } 00303 00304 //----------------------------------------------------------------------------