Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

DfpnSolver.hpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file DfpnSolver.hpp
00003  */
00004 //----------------------------------------------------------------------------
00005 
00006 #ifndef SOLVERDFPN_HPP
00007 #define SOLVERDFPN_HPP
00008 
00009 #include "SgSystem.h"
00010 #include "SgStatistics.h"
00011 #include "SgTimer.h"
00012 
00013 #include "Hex.hpp"
00014 #include "HexBoard.hpp"
00015 #include "HexState.hpp"
00016 #include "Game.hpp"
00017 #include "TransTable.hpp"
00018 #include "StateDB.hpp"
00019 #include "SolverDB.hpp"
00020 
00021 #include <limits>
00022 #include <boost/scoped_ptr.hpp>
00023 
00024 _BEGIN_BENZENE_NAMESPACE_
00025 
00026 //----------------------------------------------------------------------------
00027 
00028 /** @defgroup dfpn Depth-First Proof Number Search
00029     Hex Solver Using DFPN
00030     
00031     Based on [reference Martin & Kishi's paper]. 
00032 */
00033 
00034 //----------------------------------------------------------------------------
00035 
00036 /** Statistics tracker used in dfpn search.
00037     @ingroup dfpn
00038 */
00039 typedef SgStatisticsExt<float, std::size_t> DfpnStatistics;
00040 
00041 //----------------------------------------------------------------------------
00042 
00043 /** Type used for bounds. 
00044     @ingroup dfpn
00045 */
00046 typedef unsigned DfpnBoundType;
00047 
00048 /** Bounds used in Dfpn search. 
00049     @ingroup dfpn
00050 */
00051 struct DfpnBounds
00052 {
00053     /** Denotes a proven state. */
00054     static const DfpnBoundType INFTY = 2000000000;
00055 
00056     /** Maximum amount of work. Must be less than INFTY. */ 
00057     static const DfpnBoundType MAX_WORK = INFTY / 2;
00058 
00059     /** Proof number.
00060         Estimated amount of work to prove this state winning. */
00061     DfpnBoundType phi;
00062 
00063     /** Disproof number.
00064         Estimated amount of work to prove this state losing. */
00065     DfpnBoundType delta;
00066 
00067     DfpnBounds();
00068 
00069     DfpnBounds(DfpnBoundType p, DfpnBoundType d);
00070 
00071 
00072     /** Returns true if phi is greater than other's phi and delta is
00073         greater than other's delta. */
00074     bool GreaterThan(const DfpnBounds& other) const;
00075 
00076     /** Returns true if bounds are winning (phi is 0). */
00077     bool IsWinning() const;
00078 
00079     /** Returns true if bounds are losing (delta is 0). */
00080     bool IsLosing() const;
00081     
00082     /** Returns true if IsWinning() or IsLosing() is true. */
00083     bool IsSolved() const;
00084 
00085     void CheckConsistency() const;
00086 
00087     /** Print bounds in human readable format. */
00088     std::string Print() const;
00089 
00090     /** Sets the bounds to (0, INFTY). */
00091     static void SetToWinning(DfpnBounds& bounds);
00092 
00093     /** Sets the bounds to (INFTY, 0). */
00094     static void SetToLosing(DfpnBounds& bounds);
00095 };
00096 
00097 inline DfpnBounds::DfpnBounds()
00098     : phi(INFTY), 
00099       delta(INFTY)
00100 {
00101 }
00102 
00103 inline DfpnBounds::DfpnBounds(DfpnBoundType p, DfpnBoundType d)
00104     : phi(p), 
00105       delta(d)
00106 {
00107 }
00108 
00109 inline std::string DfpnBounds::Print() const
00110 {
00111     std::ostringstream os;
00112     os << "[" << phi << ", " << delta << "]";
00113     return os.str();
00114 }
00115 
00116 inline bool DfpnBounds::GreaterThan(const DfpnBounds& other) const
00117 {
00118     return (phi > other.phi) && (delta > other.delta);
00119 }
00120 
00121 inline bool DfpnBounds::IsWinning() const
00122 {
00123     return phi == 0;
00124 }
00125 
00126 inline bool DfpnBounds::IsLosing() const
00127 {
00128     return delta == 0;
00129 }
00130 
00131 inline bool DfpnBounds::IsSolved() const
00132 {
00133     return IsWinning() || IsLosing();
00134 }
00135 
00136 inline void DfpnBounds::SetToWinning(DfpnBounds& bounds)
00137 {
00138     bounds.phi = 0;
00139     bounds.delta = INFTY;
00140 }
00141 
00142 inline void DfpnBounds::SetToLosing(DfpnBounds& bounds)
00143 {
00144     bounds.phi = INFTY;
00145     bounds.delta = 0;
00146 }
00147 
00148 /** Extends global output operator for DfpnBounds. */
00149 inline std::ostream& operator<<(std::ostream& os, const DfpnBounds& bounds)
00150 {
00151     os << bounds.Print();
00152     return os;
00153 }
00154 
00155 //----------------------------------------------------------------------------
00156 
00157 /** Children of a dfpn state. 
00158     @ingroup dfpn
00159 */
00160 class DfpnChildren
00161 {
00162 public:
00163     DfpnChildren();
00164 
00165     void SetChildren(const std::vector<HexPoint>& children);
00166     
00167     std::size_t Size() const;
00168 
00169     HexPoint FirstMove(int index) const;
00170 
00171     void PlayMove(int index, HexState& state) const;
00172 
00173     void UndoMove(int index, HexState& state) const;
00174 
00175 private:
00176     friend class DfpnData;
00177     friend class DfpnSolver;
00178 
00179     std::vector<HexPoint> m_children;
00180 };
00181 
00182 inline std::size_t DfpnChildren::Size() const
00183 {
00184     return m_children.size();
00185 }
00186 
00187 inline HexPoint DfpnChildren::FirstMove(int index) const
00188 {
00189     return m_children[index];
00190 }
00191 
00192 //----------------------------------------------------------------------------
00193 
00194 /** State in hashtable.
00195     Do not forget to update DFPN_DB_VERSION if this class changes in a
00196     way that invalidiates old databases.  
00197     @ingroup dfpn
00198  */
00199 class DfpnData
00200 {
00201 public:
00202 
00203     DfpnBounds m_bounds;
00204 
00205     DfpnChildren m_children;
00206 
00207     HexPoint m_bestMove;
00208     
00209     size_t m_work;
00210 
00211     bitset_t m_maxProofSet;
00212 
00213     float m_evaluationScore;
00214 
00215     DfpnData();
00216 
00217     DfpnData(const DfpnBounds& bounds, const DfpnChildren& children, 
00218              HexPoint bestMove, size_t work, bitset_t maxProofSet,
00219              float evaluationScore);
00220 
00221     ~DfpnData();
00222 
00223     std::string Print() const; 
00224     
00225     /** @name TransTableStateConcept */
00226     // @{
00227 
00228     bool Initialized() const;
00229     
00230     bool ReplaceWith(const DfpnData& data) const;
00231 
00232     // @}
00233 
00234     /** @name PositionDBStateConcept */
00235     // @{
00236 
00237     int PackedSize() const;
00238 
00239     byte* Pack() const;
00240 
00241     void Unpack(const byte* data);
00242 
00243     void Rotate(const ConstBoard& brd);
00244 
00245     // @}
00246 
00247 private:
00248 
00249     bool m_initialized;
00250 };
00251 
00252 
00253 inline DfpnData::DfpnData()
00254     : m_initialized(false)
00255 { 
00256 }
00257 
00258 inline DfpnData::DfpnData(const DfpnBounds& bounds, 
00259                           const DfpnChildren& children, 
00260                           HexPoint bestMove, size_t work,
00261                           bitset_t maxProofSet, float evaluationScore)
00262     : m_bounds(bounds),
00263       m_children(children),
00264       m_bestMove(bestMove),
00265       m_work(work),
00266       m_maxProofSet(maxProofSet),
00267       m_evaluationScore(evaluationScore),
00268       m_initialized(true)
00269 { 
00270 }
00271 
00272 inline DfpnData::~DfpnData()
00273 {
00274 }
00275 
00276 inline std::string DfpnData::Print() const
00277 {
00278     std::ostringstream os;
00279     os << '[' 
00280        << "bounds=" << m_bounds << ' '
00281        << "children=" << m_children.Size() << ' '
00282        << "bestmove=" << m_bestMove << ' '
00283        << "work=" << m_work << ' '
00284        << "maxpfset=not printing"
00285        << "evaluation=" << m_evaluationScore
00286        << ']';
00287     return os.str();
00288 }
00289 
00290 inline bool DfpnData::ReplaceWith(const DfpnData& data) const
00291 {
00292     SG_UNUSED(data);
00293     return true;
00294 }
00295 
00296 inline bool DfpnData::Initialized() const
00297 {
00298     return m_initialized;
00299 }
00300 
00301 /** Extends global output operator for DfpnData. */
00302 inline std::ostream& operator<<(std::ostream& os, const DfpnData& data)
00303 {
00304     os << data.Print();
00305     return os;
00306 }
00307 
00308 //----------------------------------------------------------------------------
00309 
00310 /** History of moves played from root state to current state. 
00311     @ingroup dfpn
00312 */
00313 class DfpnHistory
00314 {
00315 public:
00316     DfpnHistory();
00317 
00318     /** Adds a new state to the history. */
00319     void Push(HexPoint m_move, hash_t hash);
00320 
00321     /** Removes last stated added from history. */
00322     void Pop();
00323 
00324     /** Returns number of moves played so far. */
00325     int Depth() const;
00326 
00327     /** Hash of last state. */
00328     hash_t LastHash() const;
00329 
00330     /** Move played from parent state to bring us to this state. */
00331     HexPoint LastMove() const;
00332 
00333 private:
00334 
00335     /** Move played from state. */
00336     std::vector<HexPoint> m_move;
00337 
00338     /** Hash of state. */
00339     std::vector<hash_t> m_hash;
00340 };
00341 
00342 inline DfpnHistory::DfpnHistory()
00343 {
00344     m_move.push_back(INVALID_POINT);
00345     m_hash.push_back(0);
00346 }
00347 
00348 inline void DfpnHistory::Push(HexPoint move, hash_t hash)
00349 {
00350     m_move.push_back(move);
00351     m_hash.push_back(hash);
00352 }
00353 
00354 inline void DfpnHistory::Pop()
00355 {
00356     m_move.pop_back();
00357     m_hash.pop_back();
00358 }
00359 
00360 inline int DfpnHistory::Depth() const
00361 {
00362     HexAssert(!m_move.empty());
00363     return static_cast<int>(m_move.size() - 1);
00364 }
00365 
00366 inline hash_t DfpnHistory::LastHash() const
00367 {
00368     return m_hash.back();
00369 }
00370 
00371 inline HexPoint DfpnHistory::LastMove() const
00372 {
00373     return m_move.back();
00374 }
00375 
00376 //----------------------------------------------------------------------------
00377 
00378 /** Interface for listeners of DfpnSolver. 
00379     @ingroup dfpn
00380  */
00381 class DfpnListener
00382 {
00383 public:
00384     virtual ~DfpnListener();
00385 
00386     /** Called when a state is solved. */
00387     virtual void StateSolved(const DfpnHistory& history, const DfpnData& data) = 0;
00388 };
00389 
00390 //----------------------------------------------------------------------------
00391 
00392 /** Hashtable used in dfpn search.  
00393     @ingroup dfpn
00394 */
00395 typedef TransTable<DfpnData> DfpnHashTable;
00396 
00397 /** Database of solved positions. 
00398     @ingroup dfpn
00399 */
00400 class DfpnDB : public StateDB<DfpnData>
00401 {
00402 public:
00403     static const std::string DFPN_DB_VERSION;
00404 
00405     DfpnDB(const std::string& filename)
00406         : StateDB<DfpnData>(filename, DFPN_DB_VERSION)
00407     { }
00408 };
00409 
00410 /** Combines a hashtable with a position db.
00411     @ingroup dfpn
00412 */
00413 typedef SolverDB<DfpnHashTable, DfpnDB, DfpnData> DfpnStates;
00414 
00415 //----------------------------------------------------------------------------
00416 
00417 /** Hex solver using DFPN search. 
00418     @ingroup dfpn
00419 */
00420 class DfpnSolver 
00421 {
00422 public:
00423 
00424     DfpnSolver();
00425 
00426     ~DfpnSolver();
00427 
00428     /** Solves the given state using the given hashtable. 
00429         Returns the color of the winning player (EMPTY if it could
00430         not determine a winner in time). */
00431     HexColor StartSearch(const HexState& state, HexBoard& brd, 
00432                          DfpnStates& positions, PointSequence& pv);
00433 
00434     HexColor StartSearch(const HexState& state, HexBoard& brd, 
00435                          DfpnStates& positions, PointSequence& pv,
00436                          const DfpnBounds& maxBounds);
00437 
00438     void AddListener(DfpnListener& listener);
00439 
00440     /** Returns various histograms pertaining to the evaluation
00441         function from the last search. */
00442     std::string EvaluationInfo() const;
00443 
00444     /** Updates bounds from this state to start of game or a state
00445         is not in position set. */
00446     void PropagateBackwards(const Game& game, DfpnStates& pos);
00447 
00448     //------------------------------------------------------------------------
00449 
00450     /** @name Parameters */
00451     // @{
00452 
00453     /** Dumps output about root state what gui can display. */
00454     bool UseGuiFx() const;
00455 
00456     /** See UseGuiFx() */
00457     void SetUseGuiFx(bool enable);
00458 
00459     /** Maximum time search is allowed to run before aborting. 
00460         Set to 0 for no timelimit. */
00461     double Timelimit() const;
00462 
00463     /** See Timelimit() */
00464     void SetTimelimit(double timelimit);
00465 
00466     /** Widening base affects what number of the moves to consider
00467         are always looked at by the dfpn search (omitting losing moves),
00468         regardless of branching factor. This amount is added to the
00469         proportion computed by the WideningFactor (see below).
00470         The base must be set to at least 1. */
00471     int WideningBase() const;
00472 
00473     /** See WideningBase() */
00474     void SetWideningBase(int wideningBase);
00475 
00476     /** Widening factor affects what fraction of the moves to consider
00477         are looked at by the dfpn search (omitting losing moves).
00478         Must be in the range (0, 1], where 1 ensures no pruning. */
00479     float WideningFactor() const;
00480 
00481     /** See WideningFactor() */
00482     void SetWideningFactor(float wideningFactor);
00483 
00484     // @}
00485 
00486 private:
00487 
00488     /** Handles guifx output. */
00489     class GuiFx
00490     {
00491     public:
00492 
00493         GuiFx();
00494 
00495         void SetChildren(const DfpnChildren& children,
00496                          const std::vector<DfpnData>& bounds);
00497 
00498         void PlayMove(HexColor color, int index);
00499 
00500         void UndoMove();
00501 
00502         void UpdateCurrentBounds(const DfpnBounds& bounds);
00503 
00504         void Write();
00505 
00506         void WriteForced();
00507 
00508     private:
00509         
00510         DfpnChildren m_children;
00511 
00512         std::vector<DfpnData> m_data;
00513 
00514         HexColor m_color;
00515 
00516         int m_index;
00517 
00518         double m_timeOfLastWrite;
00519 
00520         int m_indexAtLastWrite;
00521 
00522         double m_delay;
00523 
00524         void DoWrite();
00525     };
00526 
00527     boost::scoped_ptr<HexState> m_state;
00528 
00529     HexBoard* m_workBoard;
00530 
00531     DfpnStates* m_positions;
00532 
00533     std::vector<DfpnListener*> m_listener;
00534 
00535     SgTimer m_timer;
00536 
00537     /** See UseGuiFx() */
00538     bool m_useGuiFx;
00539 
00540     /** See TimeLimit() */
00541     double m_timelimit;
00542 
00543     /** See WideningBase() */
00544     int m_wideningBase;
00545 
00546     /** See WideningFactor() */
00547     float m_wideningFactor;
00548 
00549     /** Number of calls to CheckAbort() before we check the timer.
00550         This is to avoid expensive calls to SgTime::Get(). Try to scale
00551         this so that it is checked twice a second. */
00552     size_t m_checkTimerAbortCalls;
00553 
00554     bool m_aborted;
00555 
00556     GuiFx m_guiFx;
00557 
00558     size_t m_numTerminal;
00559 
00560     size_t m_numMIDcalls;
00561 
00562     size_t m_numVCbuilds;
00563 
00564     SgStatisticsExt<float, std::size_t> m_prunedSiblingStats;
00565 
00566     SgStatisticsExt<float, std::size_t> m_moveOrderingPercent;
00567 
00568     SgStatisticsExt<float, std::size_t> m_moveOrderingIndex;
00569 
00570     SgStatisticsExt<float, std::size_t> m_considerSetSize;
00571 
00572     SgStatisticsExt<float, std::size_t> m_deltaIncrease;
00573 
00574     size_t m_totalWastedWork;
00575 
00576     SgHistogram<float, std::size_t> m_allEvaluation;
00577 
00578     SgHistogram<float, std::size_t> m_allSolvedEvaluation;
00579 
00580     SgHistogram<float, std::size_t> m_winningEvaluation;
00581 
00582     SgHistogram<float, std::size_t> m_losingEvaluation;
00583 
00584     size_t MID(const DfpnBounds& n, DfpnHistory& history);
00585 
00586     void SelectChild(int& bestMove, DfpnBoundType& delta2, 
00587                      const std::vector<DfpnData>& childrenDfpnBounds,
00588                      size_t maxChildIndex) const;
00589 
00590     void UpdateBounds(DfpnBounds& bounds, 
00591                       const std::vector<DfpnData>& childBounds,
00592                       size_t maxChildIndex) const;
00593 
00594     bool CheckAbort();
00595 
00596     void LookupData(DfpnData& data, const DfpnChildren& children, 
00597                     int childIndex, HexState& state);
00598 
00599     bool TTRead(const HexState& state, DfpnData& data);
00600 
00601     void TTWrite(const HexState& state, const DfpnData& data);
00602 
00603     void DumpGuiFx(const std::vector<HexPoint>& children,
00604                    const std::vector<DfpnBounds>& childBounds) const;
00605 
00606     void PrintStatistics(HexColor winner, const PointSequence& p) const;
00607 
00608     size_t ComputeMaxChildIndex(const std::vector<DfpnData>&
00609                                 childrenData) const;
00610 
00611     void DeleteChildren(DfpnChildren& children,
00612                         std::vector<DfpnData>& childrenData,
00613                         bitset_t deleteChildren) const;
00614 
00615     void NotifyListeners(const DfpnHistory& history, const DfpnData& data);
00616 };
00617 
00618 inline void DfpnSolver::AddListener(DfpnListener& listener)
00619 {
00620     if (std::find(m_listener.begin(), m_listener.end(), &listener)
00621         != m_listener.end())
00622         m_listener.push_back(&listener);
00623 }
00624 
00625 inline bool DfpnSolver::UseGuiFx() const
00626 {
00627     return m_useGuiFx;
00628 }
00629 
00630 inline void DfpnSolver::SetUseGuiFx(bool enable)
00631 {
00632     m_useGuiFx = enable;
00633 }
00634 
00635 inline double DfpnSolver::Timelimit() const
00636 {
00637     return m_timelimit;
00638 }
00639 
00640 inline void DfpnSolver::SetTimelimit(double timelimit)
00641 {
00642     m_timelimit = timelimit;
00643 }
00644 
00645 inline int DfpnSolver::WideningBase() const
00646 {
00647     return m_wideningBase;
00648 }
00649 
00650 inline void DfpnSolver::SetWideningBase(int wideningBase)
00651 {
00652     m_wideningBase = wideningBase;
00653 }
00654 
00655 inline float DfpnSolver::WideningFactor() const
00656 {
00657     return m_wideningFactor;
00658 }
00659 
00660 inline void DfpnSolver::SetWideningFactor(float wideningFactor)
00661 {
00662     m_wideningFactor = wideningFactor;
00663 }
00664 
00665 //----------------------------------------------------------------------------
00666 
00667 _END_BENZENE_NAMESPACE_
00668 
00669 #endif // SOLVERDFPN_HPP


6 Jan 2011 Doxygen 1.6.3