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 #ifndef NDEBUG
00278 makeMoveHook(move);
00279 #endif
00280 UpdateWrapper<Function> wrapper(new_hash, this, f);
00281 current_path.pushMove(move);
00282 current_state.makeUnmakeMove(Player2Type<P>(), move, wrapper);
00283 current_path.popMove(move);
00284 popAfterApply();
00285 }
00286 void makeMove(Move move)
00287 {
00288 HashKey new_hash = currentHash().newHashWithMove(move);
00289 pushBeforeApply(move);
00290 current_state.makeMove(move);
00291 updateRepetitionCounterAfterMove(new_hash);
00292 }
00293
00294 const Move lastMove(int i=1) const { return move_history.lastMove(i); }
00295 const MoveStack& history() const { return move_history; }
00296 const RecordStack2& recordHistory() const { return record_stack; }
00297 const PathEncoding& path() const { return current_path; }
00298 const NumEffectState& state() const { return current_state; }
00299 const NumEffectState& rootState() const { return root_state; }
00300 void restoreRootState();
00301 const checkmate_t& checkmateSearcher() const { return *checkmate_searcher; }
00302 const RepetitionCounter& repetitionCounter() const {
00303 return repetition_counter;
00304 }
00305 const HashKey& currentHash() const
00306 {
00307 return repetition_counter.history().top();
00308 }
00309
00313 template <Player P, class Function>
00314 void doUndoMoveLight(Move move, Function& f)
00315 {
00316 current_path.pushMove(move);
00317 current_state.makeUnmakeMove(Player2Type<P>(), move, f);
00318 current_path.popMove(move);
00319 }
00320
00321 template <Player P>
00322 bool isLosingState(int node_limit)
00323 {
00324 search_assert(P == path().turn());
00325 const bool lose =
00326 checkmate_searcher->isLosingState<P>
00327 (node_limit, current_state, currentHash(), path(), lastMove());
00328 return lose;
00329 }
00330 public:
00331 template <Player P>
00332 static bool isWinningState(checkmate_t& search, NumEffectState& state,
00333 const HashKey& key, PathEncoding path,
00334 int node_limit, Move& checkmate_move,
00335 Move last_move, bool
00336 #ifdef OSL_DFPN_SMP_SEARCH
00337 parallel
00338 #endif
00339 =false
00340 )
00341 {
00342 assert(P == path.turn());
00343 #ifdef OSL_DFPN_SMP_SEARCH
00344 if (parallel)
00345 return search.isWinningStateParallel<P>
00346 (node_limit, state, key, path, checkmate_move, last_move);
00347 #endif
00348 const bool win = search.isWinningState<P>
00349 (node_limit, state, key, path, checkmate_move, last_move);
00350 return win;
00351 }
00352 static bool isWinningState(checkmate_t& search, NumEffectState& state,
00353 const HashKey& key, PathEncoding path,
00354 int node_limit, Move& checkmate_move,
00355 Move last_move, bool parallel=false)
00356 {
00357 if (state.turn() == BLACK)
00358 return isWinningState<BLACK>
00359 (search, state, key, path, node_limit, checkmate_move, last_move, parallel);
00360 else
00361 return isWinningState<WHITE>
00362 (search, state, key, path, node_limit, checkmate_move, last_move, parallel);
00363 }
00364 template <Player P>
00365 bool isWinningState(int node_limit, Move& checkmate_move, bool parallel=false)
00366 {
00367 search_assert(P == path().turn());
00368 return isWinningState<P>(*checkmate_searcher, current_state, currentHash(),
00369 path(), node_limit, checkmate_move, lastMove(), parallel);
00370 }
00372 template <Player P>
00373 bool isWinningStateShort(int depth, Move& checkmate_move)
00374 {
00375 checkmate::FixedDepthSearcher searcher(current_state);
00376 const ProofDisproof pdp=searcher.hasCheckmateMove<P>(depth, checkmate_move);
00377 return pdp.isCheckmateSuccess();
00378 }
00382 template <Player P>
00383 bool isThreatmateState(int node_limit, Move& threatmate_move, bool
00384 #ifdef OSL_DFPN_SMP_SEARCH
00385 parallel
00386 #endif
00387 =false)
00388 {
00389 search_assert(P == path().turn());
00390 current_state.changeTurn();
00391 HashKey threatmate_hash = currentHash();
00392 threatmate_hash.changeTurn();
00393 const PathEncoding threatmate_path(current_state.turn());
00394 const Player Opponent = PlayerTraits<P>::opponent;
00395 bool threatmate;
00396 #ifdef OSL_DFPN_SMP_SEARCH
00397 if (parallel)
00398 threatmate = checkmate_searcher->template isWinningStateParallel<Opponent>
00399 (node_limit, current_state,
00400 threatmate_hash, threatmate_path, threatmate_move, Move::PASS(P));
00401 else
00402 #endif
00403 threatmate
00404 = checkmate_searcher->template isWinningState<Opponent>
00405 (node_limit, current_state,
00406 threatmate_hash, threatmate_path, threatmate_move, Move::PASS(P));
00407 current_state.changeTurn();
00408 return threatmate;
00409 }
00410 template <Player P>
00411 bool isThreatmateStateShort(int depth, Move& threatmate_move)
00412 {
00413 search_assert(P == path().turn());
00414 current_state.changeTurn();
00415
00416 const Player Opponent = PlayerTraits<P>::opponent;
00417
00418 checkmate::FixedDepthSearcher searcher(current_state);
00419 const ProofDisproof pdp
00420 = searcher.hasCheckmateMove<Opponent>(depth, threatmate_move);
00421
00422 current_state.changeTurn();
00423 return pdp.isCheckmateSuccess();
00424 }
00425 bool abort() const;
00426 virtual bool abort(Move) const;
00427
00428 bool tryThreatmate() const
00429 {
00430 const Move last_move = lastMove();
00431 if (! last_move.isNormal())
00432 return false;
00433 const Square king = state().kingSquare(state().turn());
00434 if (curDepth() == 1)
00435 return FirstMoveThreatmate::isMember(last_move, king);
00436 return Neighboring8Direct::hasEffect
00437 (state(), last_move.ptypeO(), last_move.to(), king);
00438
00439 }
00440
00441 void makePV(Move m)
00442 {
00443 const int depth = curDepth();
00444 makePV(pv[depth], m, pv[depth+1]);
00445 }
00446 void initPV()
00447 {
00448 const int depth = curDepth();
00449 pv[depth].clear();
00450 }
00451 void makePV(PVVector& parent, Move m, PVVector& pv) const;
00453 int
00454 #ifdef __GNUC__
00455 __attribute__ ((pure))
00456 #endif
00457 countCheckAfterThreatmate(Player turn, int depth=1) const
00458 {
00459 assert(((depth % 2) && state().turn() == turn)
00460 || ((depth % 2) == 0 && state().turn() != turn));
00461 int result = 0;
00462 for (int i=depth;; i+=2) {
00463 if (! hasLastRecord(i) || ! lastRecord(i))
00464 break;
00465 if (lastRecord(i)->qrecord.threatmate.status(turn).status()
00466 != ThreatmateState::CHECK_AFTER_THREATMATE)
00467 break;
00468 ++result;
00469 }
00470 return result;
00471 }
00472 int countCheckAfterThreatmateSacrifice(Player turn, int depth=1) const
00473 {
00474 assert(((depth % 2) && state().turn() == turn)
00475 || ((depth % 2) == 0 && state().turn() != turn));
00476 assert(depth > 0);
00477 int result = 0;
00478 for (int i=depth;; i+=2) {
00479 if (! hasLastRecord(i) || ! lastRecord(i))
00480 break;
00481 if (lastRecord(i)->qrecord.threatmate.status(turn).status()
00482 != ThreatmateState::CHECK_AFTER_THREATMATE)
00483 break;
00484 if (lastMove(i-1).isCapture())
00485 ++result;
00486 }
00487 return result;
00488 }
00489 };
00490
00491 #undef search_assert
00492 #undef search_assert2
00493 #define search_assert(x) assert((x) || SearchState2Core::abort())
00494 #define search_assert2(x, m) assert((x) || SearchState2Core::abort(m))
00495
00498 class SearchState2 : public SearchState2Core
00499 {
00500 public:
00502 static const int ReSearchLimitMargin = 80;
00503 protected:
00504 int root_limit;
00505 int cur_limit;
00506 public:
00507 SearchState2(const NumEffectState& s, checkmate_t& checker);
00508 virtual ~SearchState2();
00509
00510 void setState(const NumEffectState& s);
00511 void setKillerMove(Move best_move)
00512 {
00513 if (best_move.isPass())
00514 return;
00515 SearchState2Core::setKillerMove(best_move);
00516 }
00517
00518 int curLimit() const { return cur_limit; }
00519
00520 bool abort(Move) const;
00521
00522 protected:
00526 void setRoot(int limit)
00527 {
00528 root_limit = limit;
00529 cur_limit = limit;
00530 }
00531 void addLimit(int limit) { cur_limit += limit; search_assert(cur_limit >= 0); }
00532 void subLimit(int limit) { cur_limit -= limit; search_assert(cur_limit >= 0); }
00533
00537 int countSacrificeCheck2(int history_max) const;
00539 void checkPointSearchAllMoves();
00540 };
00541 }
00542 }
00543
00544 #undef search_assert
00545 #undef search_assert2
00546
00547 #endif
00548
00549
00550
00551