00001 //---------------------------------------------------------------------------- 00002 /** @file DfsSolver.hpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #ifndef DFSSOLVER_H 00007 #define DFSSOLVER_H 00008 00009 #include <boost/scoped_ptr.hpp> 00010 00011 #include "Hex.hpp" 00012 #include "HexBoard.hpp" 00013 #include "VC.hpp" 00014 #include "TransTable.hpp" 00015 #include "DfsData.hpp" 00016 #include "SolverDB.hpp" 00017 #include "StateDB.hpp" 00018 #include "HexEval.hpp" 00019 00020 _BEGIN_BENZENE_NAMESPACE_ 00021 00022 //---------------------------------------------------------------------------- 00023 00024 /** Transposition table for use in DfsSolver. */ 00025 typedef TransTable<DfsData> DfsHashTable; 00026 00027 /** Database for use in DfsSolver. */ 00028 class DfsDB : public StateDB<DfsData> 00029 { 00030 public: 00031 static const std::string DFS_DB_VERSION; 00032 00033 DfsDB(const std::string& filename) 00034 : StateDB<DfsData>(filename, DFS_DB_VERSION) 00035 { } 00036 }; 00037 00038 /** Solver database combining both of the above. */ 00039 typedef SolverDB<DfsHashTable, DfsDB, DfsData> DfsStates; 00040 00041 //---------------------------------------------------------------------------- 00042 00043 /** Stats for a branch of the search tree. */ 00044 struct DfsBranchStatistics 00045 { 00046 /** Total states in tree if no DB and no TT. */ 00047 unsigned total_states; 00048 00049 /** States actually visited; includes leafs, tt and db hits. */ 00050 unsigned explored_states; 00051 00052 /** Expanded nodes; non leaf, non tt and db hit states. */ 00053 unsigned expanded_states; 00054 00055 /** Number of expanded nodes assuming perfect move ordering 00056 (assuming the same set of winning moves). */ 00057 unsigned minimal_explored; 00058 00059 /** Decompositions found; if black is to move, it must be a 00060 decomposition for white. */ 00061 unsigned decompositions; 00062 00063 /** Decompositions where the player to move won. */ 00064 unsigned decompositions_won; 00065 00066 /** Total number of moves to consider in expanded states. 00067 Includes moves that are later pruned (by mustplay or 00068 from skipping due to finding a win). */ 00069 unsigned moves_to_consider; 00070 00071 /** Number of expanded states that had winning moves. */ 00072 unsigned winning_expanded; 00073 00074 /** Number of branches tried before win was found. */ 00075 unsigned branches_to_win; 00076 00077 /** States pruned by mustplay pruning. */ 00078 unsigned pruned; 00079 00080 /** Number of proofs that were successfully shrunk. */ 00081 unsigned shrunk; 00082 00083 /** Total number of cells removed in all successful proof 00084 shrinkings. */ 00085 unsigned cells_removed; 00086 00087 DfsBranchStatistics(); 00088 00089 void operator+=(const DfsBranchStatistics& o); 00090 }; 00091 00092 inline DfsBranchStatistics::DfsBranchStatistics() 00093 : total_states(0), 00094 explored_states(0), 00095 expanded_states(0), 00096 minimal_explored(0), 00097 decompositions(0), 00098 decompositions_won(0), 00099 moves_to_consider(0), 00100 winning_expanded(0), 00101 branches_to_win(0), 00102 pruned(0), 00103 shrunk(0), 00104 cells_removed(0) 00105 { 00106 } 00107 00108 inline void DfsBranchStatistics::operator+=(const DfsBranchStatistics& o) 00109 { 00110 total_states += o.total_states; 00111 explored_states += o.explored_states; 00112 expanded_states += o.expanded_states; 00113 minimal_explored += o.minimal_explored; 00114 decompositions += o.decompositions; 00115 decompositions_won += o.decompositions_won; 00116 moves_to_consider += o.moves_to_consider; 00117 winning_expanded += o.winning_expanded; 00118 branches_to_win += o.branches_to_win; 00119 pruned += o.pruned; 00120 shrunk += o.shrunk; 00121 cells_removed += o.cells_removed; 00122 } 00123 00124 //---------------------------------------------------------------------------- 00125 00126 /** Stats for the entire search tree broken down by level. */ 00127 struct DfsHistogram 00128 { 00129 /** Map of # of stones to a counter. */ 00130 typedef std::map<int, std::size_t> StatsMap; 00131 00132 /** Terminal states encountered at each depth. */ 00133 StatsMap terminal; 00134 00135 /** Internal states encountered at each depth. */ 00136 StatsMap states; 00137 00138 /** Winning states encountered at each depth. */ 00139 StatsMap winning; 00140 00141 StatsMap size_of_winning_states; 00142 00143 StatsMap size_of_losing_states; 00144 00145 /** Branches taken to find winning move at each depth. */ 00146 StatsMap branches; 00147 00148 /** Size of original mustplay in winning states. */ 00149 StatsMap mustplay; 00150 00151 /** States under losing moves before winning move. */ 00152 StatsMap states_under_losing; 00153 00154 /** DB/TT hits at each depth. */ 00155 StatsMap tthits; 00156 00157 /** Writes histogram in human-readable format to a string. */ 00158 std::string Write(); 00159 }; 00160 00161 //---------------------------------------------------------------------------- 00162 00163 /** Contains all relevant data for a solution to a state. */ 00164 struct DfsSolutionSet 00165 { 00166 bitset_t proof; 00167 00168 int m_numMoves; 00169 00170 PointSequence pv; 00171 00172 DfsBranchStatistics stats; 00173 00174 DfsSolutionSet(); 00175 00176 void SetPV(HexPoint cell); 00177 00178 void SetPV(HexPoint cell, const PointSequence& pv); 00179 }; 00180 00181 inline DfsSolutionSet::DfsSolutionSet() 00182 : m_numMoves(0) 00183 { 00184 } 00185 00186 inline void DfsSolutionSet::SetPV(HexPoint cell) 00187 { 00188 pv.clear(); 00189 pv.push_back(cell); 00190 } 00191 00192 inline void DfsSolutionSet::SetPV(HexPoint cell, const PointSequence& old) 00193 { 00194 pv.clear(); 00195 pv.push_back(cell); 00196 pv.insert(pv.end(), old.begin(), old.end()); 00197 } 00198 00199 //---------------------------------------------------------------------------- 00200 00201 namespace DfsMoveOrderFlags 00202 { 00203 /** Each move is played and the size of the resulting mustplay is 00204 stored. Moves are ordered in increasing order of mustplay. 00205 This is a very expensive move ordering, since the vcs 00206 and inferior cells must be updated for every possible move in 00207 every possible state. However, the move ordering is usually 00208 very good. */ 00209 static const int WITH_MUSTPLAY = 1; 00210 00211 /** Resistance score is used to break ties instead of distance 00212 from the center of the board. */ 00213 static const int WITH_RESIST = 2; 00214 00215 /** Moves near center of board get higher priority than moves near 00216 the edge of the board. */ 00217 static const int FROM_CENTER = 4; 00218 } 00219 00220 //---------------------------------------------------------------------------- 00221 00222 /** Determines the winner of a gamestate. 00223 DfsSolver uses a mustplay driven depth-first search to determine the 00224 winner in the given state. 00225 */ 00226 class DfsSolver 00227 { 00228 public: 00229 DfsSolver(); 00230 00231 ~DfsSolver(); 00232 00233 //------------------------------------------------------------------------ 00234 00235 /** Solves state using the given previously solved positions. 00236 Returns color of winner or EMPTY if aborted before state was 00237 solved. 00238 */ 00239 HexColor Solve(const HexState& state, HexBoard& board, 00240 DfsSolutionSet& solution, 00241 DfsStates& positions, int depthLimit = -1, 00242 double timeLimit = -1.0); 00243 00244 //------------------------------------------------------------------------ 00245 00246 /** @name Parameters */ 00247 // @{ 00248 00249 /** Controls whether gamestates decomposible into separate 00250 components have each side solved separately and the proofs 00251 combined as necessary. */ 00252 bool UseDecompositions() const; 00253 00254 /** See UseDecompositions() */ 00255 void SetUseDecompositions(bool enable); 00256 00257 /** Depth from root in which the current variation is printed. */ 00258 int ProgressDepth() const; 00259 00260 /** See ProgressDepth() */ 00261 void SetProgressDepth(int depth); 00262 00263 /** Depth at which the current state is dumped to the log. */ 00264 int UpdateDepth() const; 00265 00266 /** See UpdateDepth() */ 00267 void SetUpdateDepth(int depth); 00268 00269 /** Whether ICE is used to provably shrink proofs. */ 00270 bool ShrinkProofs() const; 00271 00272 /** See ShrinkProofs() */ 00273 void SetShrinkProofs(bool enable); 00274 00275 /** Use newly acquired ICE-info after the move ordering stage to 00276 prune the moves to consider. */ 00277 bool BackupIceInfo() const; 00278 00279 /** See BackupIceInfo() */ 00280 void SetBackupIceInfo(bool enable); 00281 00282 bool UseGuiFx() const; 00283 00284 /** See UseGuiFx() */ 00285 void SetUseGuiFx(bool enable); 00286 00287 /** Returns the move order flags. */ 00288 int MoveOrdering() const; 00289 00290 /** See MoveOrdering() */ 00291 void SetMoveOrdering(int flags); 00292 00293 // @} 00294 00295 //------------------------------------------------------------------------ 00296 00297 /** Dumps the stats on # of states, branching factors, etc, for 00298 the last run. */ 00299 void DumpStats(const DfsSolutionSet& solution) const; 00300 00301 /** Returns histogram of last search. */ 00302 DfsHistogram Histogram() const; 00303 00304 private: 00305 00306 //------------------------------------------------------------------------ 00307 00308 /** Globabl statistics for the current solver run. */ 00309 struct GlobalStatistics 00310 { 00311 /** Times HexBoard::PlayMove() was called. */ 00312 unsigned played; 00313 00314 GlobalStatistics() 00315 : played(0) 00316 { } 00317 }; 00318 00319 //------------------------------------------------------------------------ 00320 00321 DfsStates* m_positions; 00322 00323 HexBoard* m_workBrd; 00324 00325 double m_start_time; 00326 00327 double m_end_time; 00328 00329 std::vector<std::pair<int, int> > m_completed; 00330 00331 bool m_aborted; 00332 00333 mutable DfsHistogram m_histogram; 00334 00335 mutable GlobalStatistics m_statistics; 00336 00337 boost::scoped_ptr<HexState> m_state; 00338 00339 /** See UseDecompositions() */ 00340 bool m_use_decompositions; 00341 00342 /** See UpdateDepth() */ 00343 int m_update_depth; 00344 00345 /** See ShrinkProofs() */ 00346 bool m_shrink_proofs; 00347 00348 /** See BackupIceInfo() */ 00349 bool m_backup_ice_info; 00350 00351 /** See UseGuiFx() */ 00352 bool m_use_guifx; 00353 00354 /** See MoveOrdering() */ 00355 int m_move_ordering; 00356 00357 unsigned m_last_histogram_dump; 00358 00359 int m_depthLimit; 00360 00361 double m_timeLimit; 00362 00363 //------------------------------------------------------------------------ 00364 00365 void PlayMove(HexPoint cell); 00366 00367 void UndoMove(HexPoint cell); 00368 00369 bool CheckTransposition(DfsData& state) const; 00370 00371 void StoreState(const DfsData& state, const bitset_t& proof); 00372 00373 bool CheckAbort(); 00374 00375 bool HandleLeafNode(DfsData& state, bitset_t& proof) const; 00376 00377 bool HandleTerminalNode(DfsData& state, bitset_t& proof) const; 00378 00379 bool OrderMoves(bitset_t& mustplay, DfsSolutionSet& solution, 00380 std::vector<HexMoveValue>& moves); 00381 00382 bool SolveState(PointSequence& variation, DfsSolutionSet& solution); 00383 00384 bool SolveDecomposition(PointSequence& variation, 00385 DfsSolutionSet& solution, HexPoint group); 00386 00387 bool SolveInteriorState(PointSequence& variation, 00388 DfsSolutionSet& solution); 00389 00390 void HandleProof(const PointSequence& variation, 00391 bool winning_state, DfsSolutionSet& solution); 00392 }; 00393 00394 //---------------------------------------------------------------------------- 00395 00396 inline bool DfsSolver::UseDecompositions() const 00397 { 00398 return m_use_decompositions; 00399 } 00400 00401 inline void DfsSolver::SetUseDecompositions(bool enable) 00402 { 00403 m_use_decompositions = enable; 00404 } 00405 00406 inline int DfsSolver::UpdateDepth() const 00407 { 00408 return m_update_depth; 00409 } 00410 00411 inline void DfsSolver::SetUpdateDepth(int depth) 00412 { 00413 m_update_depth = depth; 00414 } 00415 00416 inline bool DfsSolver::ShrinkProofs() const 00417 { 00418 return m_shrink_proofs; 00419 } 00420 00421 inline void DfsSolver::SetShrinkProofs(bool enable) 00422 { 00423 m_shrink_proofs = enable; 00424 } 00425 00426 inline bool DfsSolver::BackupIceInfo() const 00427 { 00428 return m_backup_ice_info; 00429 } 00430 00431 inline void DfsSolver::SetBackupIceInfo(bool enable) 00432 { 00433 m_backup_ice_info = enable; 00434 } 00435 00436 inline bool DfsSolver::UseGuiFx() const 00437 { 00438 return m_use_guifx; 00439 } 00440 00441 inline void DfsSolver::SetUseGuiFx(bool enable) 00442 { 00443 m_use_guifx = enable; 00444 } 00445 00446 inline int DfsSolver::MoveOrdering() const 00447 { 00448 return m_move_ordering; 00449 } 00450 00451 inline void DfsSolver::SetMoveOrdering(int flags) 00452 { 00453 m_move_ordering = flags; 00454 } 00455 00456 inline DfsHistogram DfsSolver::Histogram() const 00457 { 00458 return m_histogram; 00459 } 00460 00461 //---------------------------------------------------------------------------- 00462 00463 /** Methods in DfsSolver that do not need DfsSolver's private data. */ 00464 namespace DfsSolverUtil 00465 { 00466 /** Computes distance from the center of the board. */ 00467 int DistanceFromCenter(const ConstBoard& brd, HexPoint p); 00468 } 00469 00470 //---------------------------------------------------------------------------- 00471 00472 _END_BENZENE_NAMESPACE_ 00473 00474 #endif // DFSSOLVER_H