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