00001
00002
00003 #ifndef OSL_SEARCHSTATE2_H
00004 #define OSL_SEARCHSTATE2_H
00005
00006 #include "osl/search/killerMoveTable.h"
00007 #include "osl/search/bigramKillerMove.h"
00008 #include "osl/search/historyTable.h"
00009 #include "osl/search/firstMoveThreatmate.h"
00010 #include "osl/search/simpleHashRecord.h"
00011 #include "osl/checkmate/dualDfpn.h"
00012 #include "osl/checkmate/fixedDepthSearcher.h"
00013 #include "osl/effect_util/neighboring8Direct.h"
00014 #include "osl/state/numEffectState.h"
00015 #include "osl/hash/hashKey.h"
00016 #include "osl/repetitionCounter.h"
00017 #include "osl/container/moveStack.h"
00018 #include "osl/misc/fixedCapacityVector.h"
00019 #include "osl/misc/align16New.h"
00020
00021 #include <boost/shared_ptr.hpp>
00022 #include <boost/utility.hpp>
00023
00024 namespace osl
00025 {
00026 namespace search
00027 {
00032 class RecordStack2
00033 {
00034 static const int SEARCH_DEPTH_MAX = 64;
00035 FixedCapacityVector<SimpleHashRecord*, SEARCH_DEPTH_MAX> data;
00036 public:
00037 RecordStack2();
00038 void clear();
00039 void push(SimpleHashRecord *r) { data.push_back(r); }
00040 void pop() { assert(size() > 1); data.pop_back(); }
00041
00042 SimpleHashRecord* lastRecord(unsigned int n=0) const
00043 {
00044 assert(size() > n);
00045 return data[size()-1-n];
00046 }
00047 SimpleHashRecord* rootRecord() const
00048 {
00049 assert(! empty());
00050 return data[0];
00051 }
00052 void setRootRecord(SimpleHashRecord *root) { data[0] = root; }
00053 void setLastRecord(SimpleHashRecord *r) {
00054 assert(size() > 0);
00055 data[size()-1] = r;
00056 }
00057
00058 size_t size() const { return data.size(); }
00059 bool empty() const { return data.empty(); }
00060 bool hasLastRecord(unsigned int n=0) const
00061 {
00062 return size() > n;
00063 }
00064
00065 void dump() const;
00066 };
00067
00068 class Worker;
00072 struct SearchState2Shared : boost::noncopyable
00073 {
00074 BigramKillerMove bigram_killers;
00075 KillerMoveTable killer_moves;
00076 HistoryTable history_table;
00077
00078 SearchState2Shared();
00079 ~SearchState2Shared();
00080 };
00081 #define search_assert(x) assert(((x) || (SearchState2Core::abort())))
00082 #define search_assert2(x, m) assert(((x) || (SearchState2Core::abort(m))))
00083 struct AlphaBeta2ParallelCommon;
00086 class SearchState2Core
00087 #if OSL_WORDSIZE == 32
00088 : public misc::Align16New
00089 #endif
00090 {
00091 friend struct AlphaBeta2ParallelCommon;
00092 public:
00093 enum { MaxDepth = 64 };
00094 typedef DualDfpn checkmate_t;
00095 protected:
00096 NumEffectState current_state, root_state;
00097 checkmate_t* checkmate_searcher;
00098 #ifdef OSL_SMP
00099 public:
00100 void setCheckmateSearcher(checkmate_t *new_checkmate) {
00101 checkmate_searcher = new_checkmate;
00102 }
00103 private:
00104 #endif
00105 PathEncoding current_path;
00106 MoveStack move_history;
00107 int root_depth;
00108 protected:
00109 RecordStack2 record_stack;
00110 RepetitionCounter repetition_counter;
00111 boost::shared_ptr<SearchState2Shared> shared;
00112 public:
00113 typedef FixedCapacityVector<Move,MaxDepth> PVVector;
00114 protected:
00115 CArray<PVVector,MaxDepth> pv;
00116 enum NodeType { PvNode = 0, AllNode = 1, CutNode = -1, };
00117 CArray<NodeType,MaxDepth> node_type;
00118 public:
00120 volatile bool stop_tree;
00121 #ifndef MINIMAL
00122 static CArray<int, MaxDepth> depth_node_count_quiesce;
00123 #endif
00124 SearchState2Core(const NumEffectState& s, checkmate_t& checker);
00125 virtual ~SearchState2Core();
00126 int curDepth() const { return history().size() - root_depth; }
00127
00134 virtual void setState(const NumEffectState& s);
00135 void setHistory(const MoveStack& h);
00136 bool hasLastRecord(unsigned int n=0) const
00137 {
00138 return record_stack.hasLastRecord(n);
00139 }
00140 SimpleHashRecord* lastRecord(unsigned int n=0)
00141 {
00142 return record_stack.lastRecord(n);
00143 }
00144 const SimpleHashRecord* lastRecord(unsigned int n=0) const
00145 {
00146 return record_stack.lastRecord(n);
00147 }
00148 SimpleHashRecord *rootRecord()
00149 {
00150 return record_stack.rootRecord();
00151 }
00152 void setCurrentRecord(SimpleHashRecord *r)
00153 {
00154 search_assert((int)record_stack.size() == curDepth()+1);
00155 record_stack.setLastRecord(r);
00156 }
00157 void setRootRecord(SimpleHashRecord *root)
00158 {
00159 search_assert(record_stack.size() == 1);
00160 search_assert(curDepth() == 0);
00161 record_stack.setRootRecord(root);
00162 }
00163
00164
00165 void setKillerMove(Move best_move)
00166 {
00167 if (best_move.isPass())
00168 return;
00169 shared->killer_moves.setMove(curDepth(), best_move);
00170 const Move last_move = lastMove();
00171 if (! last_move.isInvalid()) {
00172 search_assert(best_move.player() != last_move.player());
00173 shared->bigram_killers.setMove(last_move, best_move);
00174 }
00175 }
00176 void getBigramKillerMoves(MoveVector& moves) const
00177 {
00178 shared->bigram_killers.getMove(state(), lastMove(), moves);
00179 #ifdef TRI_PLY_BIGRAM_KILLERS
00180 if (move_history.hasLastMove(3))
00181 shared->bigram_killers.getMove(state(), lastMove(3), moves);
00182 #endif
00183 }
00184 void getKillerMoves(MoveVector& moves) const
00185 {
00186 getBigramKillerMoves(moves);
00187 shared->killer_moves.getMove(state(), curDepth(), moves);
00188 }
00189 const BigramKillerMove& bigramKillerMove() const {
00190 return shared->bigram_killers;
00191 }
00192 void setBigramKillerMove(const BigramKillerMove& killers);
00193 HistoryTable& historyTable() { return shared->history_table; }
00194 const HistoryTable& historyTable() const { return shared->history_table; }
00195
00196 void pushPass()
00197 {
00198 const Move pass = Move::PASS(current_state.turn());
00199 current_state.changeTurn();
00200 current_path.pushMove(pass);
00201 move_history.push(pass);
00202 }
00203 void popPass()
00204 {
00205 const Move pass = Move::PASS(alt(current_state.turn()));
00206 current_state.changeTurn();
00207 current_path.popMove(pass);
00208 move_history.pop();
00209 }
00210 private:
00214 void pushBeforeApply(Move move)
00215 {
00216 move_history.push(move);
00217 record_stack.push(0);
00218 assert(curDepth() > 0);
00219 node_type[curDepth()] = static_cast<NodeType>(-node_type[curDepth()-1]);
00220 }
00221 struct Updator
00222 {
00223 const HashKey& new_hash;
00224 SearchState2Core *state;
00225 Updator(const HashKey& h, SearchState2Core *s)
00226 : new_hash(h), state(s)
00227 {
00228 }
00229 void update()
00230 {
00231 state->updateRepetitionCounterAfterMove(new_hash);
00232 }
00233 };
00234 template <class Function>
00235 struct UpdateWrapper : public Updator
00236 {
00237 Function& function;
00238 UpdateWrapper(const HashKey& h, SearchState2Core *s, Function& f)
00239 : Updator(h, s), function(f)
00240 {
00241 }
00242 void operator()(Square to)
00243 {
00244 update();
00245 function(to);
00246 }
00247 };
00248 friend struct Updator;
00252 void updateRepetitionCounterAfterMove(const HashKey& new_hash)
00253 {
00254 repetition_counter.push(new_hash, current_state);
00255 }
00259 void popAfterApply()
00260 {
00261 record_stack.pop();
00262 repetition_counter.pop();
00263 move_history.pop();
00264 }
00265 #ifndef NDEBUG
00266 void makeMoveHook(Move);
00267 #endif
00268 public:
00272 template <Player P, class Function>
00273 void doUndoMoveOrPass(const HashKey& new_hash,
00274 Move move, Function& f)
00275 {
00276 pushBeforeApply(move);
00277 UpdateWrapper<Function> wrapper(new_hash, this, f);
00278 current_path.pushMove(move);
00279 current_state.makeUnmakeMove(Player2Type<P>(), move, wrapper);
00280 current_path.popMove(move);
00281 popAfterApply();
00282 }
00283 void makeMove(Move move)
00284 {
00285 HashKey new_hash = currentHash().newHashWithMove(move);
00286 pushBeforeApply(move);
00287 current_state.makeMove(move);
00288 updateRepetitionCounterAfterMove(new_hash);
00289 }
00290
00291 const Move lastMove(int i=1) const { return move_history.lastMove(i); }
00292 const MoveStack& history() const { return move_history; }
00293 const RecordStack2& recordHistory() const { return record_stack; }
00294 const PathEncoding& path() const { return current_path; }
00295 const NumEffectState& state() const { return current_state; }
00296 const NumEffectState& rootState() const { return root_state; }
00297 void restoreRootState();
00298 const checkmate_t& checkmateSearcher() const { return *checkmate_searcher; }
00299 const RepetitionCounter& repetitionCounter() const {
00300 return repetition_counter;
00301 }
00302 const HashKey& currentHash() const
00303 {
00304 return repetition_counter.history().top();
00305 }
00306
00310 template <Player P, class Function>
00311 void doUndoMoveLight(Move move, Function& f)
00312 {
00313 current_path.pushMove(move);
00314 current_state.makeUnmakeMove(Player2Type<P>(), move, f);
00315 current_path.popMove(move);
00316 }
00317
00318 template <Player P>
00319 bool isLosingState(int node_limit)
00320 {
00321 search_assert(P == path().turn());
00322 const bool lose =
00323 checkmate_searcher->isLosingState<P>
00324 (node_limit, current_state, currentHash(), path(), lastMove());
00325 return lose;
00326 }
00327 public:
00328 template <Player P>
00329 static bool isWinningState(checkmate_t& search, NumEffectState& state,
00330 const HashKey& key, PathEncoding path,
00331 int node_limit, Move& checkmate_move,
00332 Move last_move, bool
00333 #ifdef OSL_DFPN_SMP_SEARCH
00334 parallel
00335 #endif
00336 =false
00337 )
00338 {
00339 assert(P == path.turn());
00340 #ifdef OSL_DFPN_SMP_SEARCH
00341 if (parallel)
00342 return search.isWinningStateParallel<P>
00343 (node_limit, state, key, path, checkmate_move, last_move);
00344 #endif
00345 const bool win = search.isWinningState<P>
00346 (node_limit, state, key, path, checkmate_move, last_move);
00347 return win;
00348 }
00349 static bool isWinningState(checkmate_t& search, NumEffectState& state,
00350 const HashKey& key, PathEncoding path,
00351 int node_limit, Move& checkmate_move,
00352 Move last_move, bool parallel=false)
00353 {
00354 if (state.turn() == BLACK)
00355 return isWinningState<BLACK>
00356 (search, state, key, path, node_limit, checkmate_move, last_move, parallel);
00357 else
00358 return isWinningState<WHITE>
00359 (search, state, key, path, node_limit, checkmate_move, last_move, parallel);
00360 }
00361 template <Player P>
00362 bool isWinningState(int node_limit, Move& checkmate_move, bool parallel=false)
00363 {
00364 search_assert(P == path().turn());
00365 return isWinningState<P>(*checkmate_searcher, current_state, currentHash(),
00366 path(), node_limit, checkmate_move, lastMove(), parallel);
00367 }
00369 template <Player P>
00370 bool isWinningStateShort(int depth, Move& checkmate_move)
00371 {
00372 checkmate::FixedDepthSearcher searcher(current_state);
00373 const ProofDisproof pdp=searcher.hasCheckmateMove<P>(depth, checkmate_move);
00374 return pdp.isCheckmateSuccess();
00375 }
00379 template <Player P>
00380 bool isThreatmateState(int node_limit, Move& threatmate_move, bool
00381 #ifdef OSL_DFPN_SMP_SEARCH
00382 parallel
00383 #endif
00384 =false)
00385 {
00386 search_assert(P == path().turn());
00387 current_state.changeTurn();
00388 HashKey threatmate_hash = currentHash();
00389 threatmate_hash.changeTurn();
00390 const PathEncoding threatmate_path(current_state.turn());
00391 const Player Opponent = PlayerTraits<P>::opponent;
00392 bool threatmate;
00393 #ifdef OSL_DFPN_SMP_SEARCH
00394 if (parallel)
00395 threatmate = checkmate_searcher->template isWinningStateParallel<Opponent>
00396 (node_limit, current_state,
00397 threatmate_hash, threatmate_path, threatmate_move, Move::PASS(P));
00398 else
00399 #endif
00400 threatmate
00401 = checkmate_searcher->template isWinningState<Opponent>
00402 (node_limit, current_state,
00403 threatmate_hash, threatmate_path, threatmate_move, Move::PASS(P));
00404 current_state.changeTurn();
00405 return threatmate;
00406 }
00407 template <Player P>
00408 bool isThreatmateStateShort(int depth, Move& threatmate_move)
00409 {
00410 search_assert(P == path().turn());
00411 current_state.changeTurn();
00412
00413 const Player Opponent = PlayerTraits<P>::opponent;
00414
00415 checkmate::FixedDepthSearcher searcher(current_state);
00416 const ProofDisproof pdp
00417 = searcher.hasCheckmateMove<Opponent>(depth, threatmate_move);
00418
00419 current_state.changeTurn();
00420 return pdp.isCheckmateSuccess();
00421 }
00422 bool abort() const;
00423 virtual bool abort(Move) const;
00424
00425 bool tryThreatmate() const
00426 {
00427 const Move last_move = lastMove();
00428 if (! last_move.isNormal())
00429 return false;
00430 const Square king = state().kingSquare(state().turn());
00431 if (curDepth() == 1)
00432 return FirstMoveThreatmate::isMember(last_move, king);
00433 return Neighboring8Direct::hasEffect
00434 (state(), last_move.ptypeO(), last_move.to(), king);
00435
00436 }
00437
00438 void makePV(Move m)
00439 {
00440 const int depth = curDepth();
00441 makePV(pv[depth], m, pv[depth+1]);
00442 }
00443 void initPV()
00444 {
00445 const int depth = curDepth();
00446 pv[depth].clear();
00447 }
00448 void makePV(PVVector& parent, Move m, PVVector& pv) const;
00450 int
00451 #ifdef __GNUC__
00452 __attribute__ ((pure))
00453 #endif
00454 countCheckAfterThreatmate(Player turn, int depth=1) const
00455 {
00456 assert(((depth % 2) && state().turn() == turn)
00457 || ((depth % 2) == 0 && state().turn() != turn));
00458 int result = 0;
00459 for (int i=depth;; i+=2) {
00460 if (! hasLastRecord(i) || ! lastRecord(i))
00461 break;
00462 if (lastRecord(i)->qrecord.threatmate.status(turn).status()
00463 != ThreatmateState::CHECK_AFTER_THREATMATE)
00464 break;
00465 ++result;
00466 }
00467 return result;
00468 }
00469 int countCheckAfterThreatmateSacrifice(Player turn, int depth=1) const
00470 {
00471 assert(((depth % 2) && state().turn() == turn)
00472 || ((depth % 2) == 0 && state().turn() != turn));
00473 assert(depth > 0);
00474 int result = 0;
00475 for (int i=depth;; i+=2) {
00476 if (! hasLastRecord(i) || ! lastRecord(i))
00477 break;
00478 if (lastRecord(i)->qrecord.threatmate.status(turn).status()
00479 != ThreatmateState::CHECK_AFTER_THREATMATE)
00480 break;
00481 if (lastMove(i-1).isCapture())
00482 ++result;
00483 }
00484 return result;
00485 }
00486 };
00487
00488 #undef search_assert
00489 #undef search_assert2
00490 #define search_assert(x) assert((x) || SearchState2Core::abort())
00491 #define search_assert2(x, m) assert((x) || SearchState2Core::abort(m))
00492
00495 class SearchState2 : public SearchState2Core
00496 {
00497 public:
00499 static const int ReSearchLimitMargin = 80;
00500 protected:
00501 int root_limit;
00502 int cur_limit;
00503 public:
00504 SearchState2(const NumEffectState& s, checkmate_t& checker);
00505 virtual ~SearchState2();
00506
00507 void setState(const NumEffectState& s);
00508 void setKillerMove(Move best_move)
00509 {
00510 if (best_move.isPass())
00511 return;
00512 SearchState2Core::setKillerMove(best_move);
00513 }
00514
00515 int curLimit() const { return cur_limit; }
00516
00517 bool abort(Move) const;
00518
00519 protected:
00523 void setRoot(int limit)
00524 {
00525 root_limit = limit;
00526 cur_limit = limit;
00527 }
00528 void addLimit(int limit) { cur_limit += limit; search_assert(cur_limit >= 0); }
00529 void subLimit(int limit) { cur_limit -= limit; search_assert(cur_limit >= 0); }
00530
00534 int countSacrificeCheck2(int history_max) const;
00536 void checkPointSearchAllMoves();
00537 };
00538 }
00539 }
00540
00541 #undef search_assert
00542 #undef search_assert2
00543
00544 #endif
00545
00546
00547
00548