Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

SgBookBuilder.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file SgBookBuilder.cpp 
00003  */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgBookBuilder.h"
00007 #include "SgDebug.h"
00008 #include "SgPoint.h"
00009 #include "SgTimer.h"
00010 #include <sstream>
00011 #include <boost/numeric/conversion/bounds.hpp>
00012 
00013 //----------------------------------------------------------------------------
00014 
00015 bool SgBookNode::IsTerminal() const
00016 {
00017     if (m_value < -100 || m_value > 100)
00018         return true;
00019     return false;
00020 }
00021 
00022 bool SgBookNode::IsLeaf() const
00023 {
00024     return m_count == 0;
00025 }
00026 
00027 std::string SgBookNode::ToString() const
00028 {
00029     std::ostringstream os;
00030     os << std::showpos << std::fixed << std::setprecision(6);
00031     os << "Val " << m_value;
00032     os << std::noshowpos << " ExpP " << m_priority;
00033     os << std::showpos << " Heur " << m_heurValue << " Cnt " << m_count;
00034     return os.str();
00035 }
00036 
00037 void SgBookNode::FromString(const std::string& str)
00038 {
00039     std::istringstream is(str);
00040     std::string dummy;
00041     is >> dummy;
00042     is >> m_value;
00043     is >> dummy;
00044     is >> m_priority;
00045     is >> dummy;
00046     is >> m_heurValue;
00047     is >> dummy;
00048     is >> m_count;
00049 }
00050 
00051 //----------------------------------------------------------------------------
00052 
00053 SgBookBuilder::SgBookBuilder()
00054     : m_alpha(50),
00055       m_use_widening(true),
00056       m_expand_width(16),
00057       m_expand_threshold(1000),
00058       m_flush_iterations(100)
00059 {
00060 }
00061 
00062 SgBookBuilder::~SgBookBuilder()
00063 {
00064 }
00065 
00066 //----------------------------------------------------------------------------
00067 
00068 void SgBookBuilder::StartIteration(int iteration)
00069 {
00070     SG_UNUSED(iteration);
00071     // DEFAULT IMPLEMENTATION DOES NOTHING
00072 }
00073 
00074 void SgBookBuilder::EndIteration()
00075 {
00076     // DEFAULT IMPLEMENTATION DOES NOTHING
00077 }
00078 
00079 void SgBookBuilder::BeforeEvaluateChildren()
00080 {
00081     // DEFAULT IMPLEMENTATION DOES NOTHING
00082 }
00083 
00084 void SgBookBuilder::AfterEvaluateChildren()
00085 {
00086     // DEFAULT IMPLEMENTATION DOES NOTHING
00087 }
00088 
00089 void SgBookBuilder::Init()
00090 {
00091     // DEFAULT IMPLEMENTATION DOES NOTHING
00092 }
00093 
00094 void SgBookBuilder::Fini()
00095 {
00096     // DEFAULT IMPLEMENTATION DOES NOTHING
00097 }
00098 
00099 void SgBookBuilder::Expand(int numExpansions)
00100 {
00101     m_num_evals = 0;
00102     m_num_widenings = 0;
00103 
00104     SgTimer timer;
00105     Init();
00106     EnsureRootExists();
00107     int num = 0;
00108     for (; num < numExpansions; ++num) 
00109     {
00110         StartIteration(num);
00111     // If root position becomes a known win or loss, then there's
00112     // no point in continuing to expand the opening book.
00113         {
00114             SgBookNode root;
00115             GetNode(root);
00116             if (root.IsTerminal()) 
00117             {
00118                 PrintMessage("Root is terminal!\n");
00119                 break;
00120             }
00121         }
00122         std::vector<SgMove> pv;
00123         DoExpansion(pv);
00124         EndIteration();
00125         // Flush the book if we've performed enough iterations
00126         if (num && (num % m_flush_iterations) == 0) 
00127             FlushBook();
00128     }
00129     FlushBook();
00130     Fini();
00131     timer.Stop();
00132     double elapsed = timer.GetTime();
00133     std::ostringstream os;
00134     os << '\n'
00135        << "Statistics\n"
00136        << "Total Time     " << elapsed << '\n'
00137        << "Expansions     " << num 
00138        << std::fixed << std::setprecision(2) 
00139        << " (" << (num / elapsed) << "/s)\n"
00140        << "Evaluations    " << m_num_evals 
00141        << std::fixed << std::setprecision(2)
00142        << " (" << (m_num_evals / elapsed) << "/s)\n"
00143        << "Widenings      " << m_num_widenings << '\n';
00144     PrintMessage(os.str());
00145 }
00146 
00147 void SgBookBuilder::Refresh()
00148 {
00149     m_num_evals = 0;
00150     m_num_widenings = 0;
00151     m_value_updates = 0;
00152     m_priority_updates = 0;
00153     m_internal_nodes = 0;
00154     m_leaf_nodes = 0;
00155     m_terminal_nodes = 0;
00156 
00157     SgTimer timer;
00158     Init();
00159     ClearAllVisited();
00160     Refresh(true);
00161     FlushBook();
00162     Fini();
00163     timer.Stop();
00164 
00165     double elapsed = timer.GetTime();
00166     std::ostringstream os;
00167     os << '\n'
00168        << "Statistics\n"
00169        << "Total Time       " << elapsed << '\n'
00170        << "Value Updates    " << m_value_updates << '\n'
00171        << "Priority Updates " << m_priority_updates << '\n'
00172        << "Internal Nodes   " << m_internal_nodes << '\n'
00173        << "Terminal Nodes   " << m_terminal_nodes << '\n'
00174        << "Leaf Nodes       " << m_leaf_nodes << '\n'
00175        << "Evaluations      " << m_num_evals 
00176        << std::fixed << std::setprecision(2)
00177        << " (" << (m_num_evals / elapsed) << "/s)\n"
00178        << "Widenings        " << m_num_widenings << '\n';
00179     PrintMessage(os.str());
00180 }
00181 
00182 void SgBookBuilder::IncreaseWidth()
00183 {
00184     if (!m_use_widening)
00185     {
00186         PrintMessage("Widening not enabled!\n");
00187         return;
00188     }
00189 
00190     m_num_evals = 0;
00191     m_num_widenings = 0;
00192 
00193     SgTimer timer;
00194     Init();
00195     ClearAllVisited();
00196     IncreaseWidth(true);
00197     FlushBook();
00198     Fini();
00199     timer.Stop();
00200     double elapsed = timer.GetTime();
00201 
00202     std::ostringstream os;
00203     os << '\n'
00204        << "Statistics\n"
00205        << "Total Time     " << elapsed << '\n'
00206        << "Widenings      " << m_num_widenings << '\n'
00207        << "Evaluations    " << m_num_evals 
00208        << std::fixed << std::setprecision(2)
00209        << " (" << (m_num_evals / elapsed) << "/s)\n";
00210     PrintMessage(os.str());
00211 }
00212 
00213 //----------------------------------------------------------------------------
00214 
00215 /** Creates a node for each of the leaf's first count children that
00216     have not been created yet. Returns true if at least one new node
00217     was created, false otherwise. */
00218 bool SgBookBuilder::ExpandChildren(std::size_t count)
00219 {
00220     // It is possible the state is determined, even though it was
00221     // already evaluated. This can happen in Hex: it is not very likey
00222     // if the evaluation function is reasonably heavyweight, but if
00223     // just using fillin and vcs, it is possible that the fillin
00224     // prevents a winning vc from being created.
00225     float value = 0;
00226     std::vector<SgMove> children;
00227     if (GenerateMoves(children, value))
00228     {
00229         PrintMessage("ExpandChildren: State is determined!\n");
00230         WriteNode(SgBookNode(value));
00231         return false;
00232     }
00233     std::size_t limit = std::min(count, children.size());
00234     std::vector<SgMove> childrenToDo;
00235     for (std::size_t i = 0; i < limit; ++i)
00236     {
00237         PlayMove(children[i]);
00238         SgBookNode child;
00239         if (!GetNode(child))
00240             childrenToDo.push_back(children[i]);
00241         UndoMove(children[i]);
00242     }
00243     if (!childrenToDo.empty())
00244     {
00245         BeforeEvaluateChildren();
00246         std::vector<std::pair<SgMove, float> > scores;
00247         EvaluateChildren(childrenToDo, scores);
00248         AfterEvaluateChildren();
00249         for (std::size_t i = 0; i < scores.size(); ++i)
00250         {
00251             PlayMove(scores[i].first);
00252             WriteNode(scores[i].second);
00253             UndoMove(scores[i].first);
00254         }
00255         m_num_evals += childrenToDo.size();
00256         return true;
00257     }
00258     else
00259         PrintMessage("Children already evaluated.\n");
00260     return false;
00261 }
00262 
00263 std::size_t SgBookBuilder::NumChildren(const std::vector<SgMove>& legal)
00264 {
00265     std::size_t num = 0;
00266     for (size_t i = 0; i < legal.size(); ++i) 
00267     {
00268     PlayMove(legal[i]);
00269     SgBookNode child;
00270         if (GetNode(child))
00271             ++num;
00272         UndoMove(legal[i]);
00273     }
00274     return num;
00275 }
00276 
00277 void SgBookBuilder::UpdateValue(SgBookNode& node, 
00278                                 const std::vector<SgMove>& legal)
00279 {
00280     bool hasChild = false;
00281     float bestValue = boost::numeric::bounds<float>::lowest();
00282     for (std::size_t i = 0; i < legal.size(); ++i)
00283     {
00284     PlayMove(legal[i]);
00285     SgBookNode child;
00286         if (GetNode(child))
00287         {
00288             hasChild = true;
00289             float value = InverseEval(Value(child));
00290             if (value > bestValue)
00291         bestValue = value;
00292         }
00293         UndoMove(legal[i]);
00294     }
00295     if (hasChild)
00296         node.m_value = bestValue;
00297 }
00298 
00299 /** Updates the node's value, taking special care if the value is a
00300     loss. In this case, widenings are performed until a non-loss child
00301     is added or no new children are added. The node is then set with
00302     the proper value. */
00303 void SgBookBuilder::UpdateValue(SgBookNode& node)
00304 {
00305     while (true)
00306     {
00307         std::vector<SgMove> legal;
00308         GetAllLegalMoves(legal);
00309         UpdateValue(node, legal);
00310         if (!IsLoss(Value(node)))
00311             break;
00312         // Round up to next nearest multiple of m_expand_width that is
00313         // larger than the current number of children.
00314         std::size_t numChildren = NumChildren(legal);
00315         std::size_t width = (numChildren / m_expand_width + 1) 
00316             * m_expand_width;
00317         {
00318             std::ostringstream os;
00319             os << "Forced Widening[" << numChildren << "->" << width << "]\n";
00320             PrintMessage(os.str());
00321         }
00322         if (!ExpandChildren(width))
00323             break;
00324         ++m_num_widenings;
00325     }
00326 }
00327 
00328 float SgBookBuilder::ComputePriority(const SgBookNode& parent,
00329                                      const float childValue,
00330                                      const float childPriority) const
00331 {
00332     float delta = parent.m_value - InverseEval(childValue);
00333     SG_ASSERT(delta >= 0.0);
00334     return m_alpha * delta + childPriority + 1;
00335 }
00336 
00337 /** Re-computes node's priority and returns the best child to
00338     expand. Requires that UpdateValue() has been called on this
00339     node. Returns SG_NULLMOVE if node has no children. */
00340 SgMove SgBookBuilder::UpdatePriority(SgBookNode& node)
00341 {
00342     bool hasChild = false;
00343     float bestPriority = boost::numeric::bounds<float>::highest();
00344     SgMove bestChild = SG_NULLMOVE;
00345     std::vector<SgMove> legal;
00346     GetAllLegalMoves(legal);
00347     for (std::size_t i = 0; i < legal.size(); ++i)
00348     {
00349     PlayMove(legal[i]);
00350     SgBookNode child;
00351         if (GetNode(child))
00352         {
00353             hasChild = true;
00354             // Must adjust child value for swap, but not the parent
00355             // because we are comparing with the best child's value,
00356             // ie, the minmax value.
00357             float childValue = Value(child);
00358             float childPriority = child.m_priority;
00359             float priority = ComputePriority(node, childValue, childPriority);
00360             if (priority < bestPriority)
00361             {
00362                 bestPriority = priority;
00363                 bestChild = legal[i];
00364             }
00365         }
00366         UndoMove(legal[i]);
00367     }
00368     if (hasChild)
00369         node.m_priority = bestPriority;
00370     return bestChild;
00371 }
00372 
00373 void SgBookBuilder::DoExpansion(std::vector<SgMove>& pv)
00374 {
00375     SgBookNode node;
00376     if (!GetNode(node))
00377         SG_ASSERT(false);
00378     if (node.IsTerminal())
00379         return;
00380     if (node.IsLeaf())
00381     {
00382         ExpandChildren(m_expand_width);
00383     }
00384     else
00385     {
00386         // Widen this non-terminal non-leaf node if necessary
00387         if (m_use_widening && (node.m_count % m_expand_threshold == 0))
00388         {
00389             std::size_t width = (node.m_count / m_expand_threshold + 1)
00390                               * m_expand_width;
00391             ++m_num_widenings;
00392             ExpandChildren(width);
00393         }
00394         // Compute value and priority. It's possible this node is newly
00395         // terminal if one of its new children is a winning move.
00396         GetNode(node);
00397         UpdateValue(node);
00398         SgMove mostUrgent = UpdatePriority(node);
00399         WriteNode(node);
00400 
00401         // Recurse on most urgent child only if non-terminal.
00402         if (!node.IsTerminal())
00403         {
00404             PlayMove(mostUrgent);
00405             pv.push_back(mostUrgent);
00406             DoExpansion(pv);
00407             pv.pop_back();
00408             UndoMove(mostUrgent);
00409         }
00410     }
00411     GetNode(node);
00412     UpdateValue(node);
00413     UpdatePriority(node);
00414     node.IncrementCount();
00415     WriteNode(node);
00416 }
00417 
00418 //----------------------------------------------------------------------------
00419 
00420 /** Refresh's each child of the given state. UpdateValue() and
00421     UpdatePriority() are called on internal nodes. Returns true if
00422     state exists in book.  
00423     @ref bookrefresh
00424 */
00425 bool SgBookBuilder::Refresh(bool root)
00426 {
00427     if (HasBeenVisited())
00428         return true;
00429     MarkAsVisited();
00430     SgBookNode node;
00431     if (!GetNode(node))
00432         return false;
00433     if (node.IsLeaf())
00434     {
00435         m_leaf_nodes++;
00436         if (node.IsTerminal())
00437             m_terminal_nodes++;
00438         return true;
00439     }
00440     double oldValue = Value(node);
00441     double oldPriority = node.m_priority;
00442     std::vector<SgMove> legal;
00443     GetAllLegalMoves(legal);
00444     for (std::size_t i = 0; i < legal.size(); ++i)
00445     {
00446         PlayMove(legal[i]);
00447         Refresh(false);
00448         if (root)
00449         {
00450             std::ostringstream os;
00451             os << "Finished " << SgWritePoint(legal[i]) << '\n';
00452             PrintMessage(os.str());
00453         }
00454         UndoMove(legal[i]);
00455     }
00456     UpdateValue(node);
00457     UpdatePriority(node);
00458     if (fabs(oldValue - Value(node)) > 0.0001)
00459         m_value_updates++;
00460     if (fabs(oldPriority - node.m_priority) > 0.0001)
00461         m_priority_updates++;
00462     WriteNode(node);
00463     if (node.IsTerminal())
00464         m_terminal_nodes++;
00465     else
00466         m_internal_nodes++;
00467     return true;
00468 }
00469 
00470 //----------------------------------------------------------------------------
00471 
00472 void SgBookBuilder::IncreaseWidth(bool root)
00473 {
00474     if (HasBeenVisited())
00475         return;
00476     MarkAsVisited();
00477     SgBookNode node;
00478     if (!GetNode(node))
00479         return;
00480     if (node.IsTerminal() || node.IsLeaf())
00481         return;
00482     std::vector<SgMove> legal;
00483     GetAllLegalMoves(legal);
00484     for (std::size_t i = 0; i < legal.size(); ++i)
00485     {
00486         PlayMove(legal[i]);
00487         IncreaseWidth(false);
00488         if (root)
00489         {
00490             std::ostringstream os;
00491             os << "Finished " << SgWritePoint(legal[i]) << '\n';
00492             PrintMessage(os.str());
00493         }
00494         UndoMove(legal[i]);
00495     }
00496     std::size_t width = (node.m_count / m_expand_threshold + 1)
00497         * m_expand_width;
00498     if (ExpandChildren(width))
00499         ++m_num_widenings;
00500 }
00501 
00502 //----------------------------------------------------------------------------


17 Jun 2010 Doxygen 1.4.7