00001
00002
00003 #include "osl/search/alphaBeta2.h"
00004 #ifdef OSL_SMP
00005 # include "osl/search/alphaBeta2Parallel.h"
00006 #endif
00007 #include "osl/search/simpleHashRecord.h"
00008 #include "osl/search/simpleHashTable.h"
00009 #include "osl/search/dominanceCheck.h"
00010 #include "osl/search/moveGenerator.h"
00011 #include "osl/search/realizationProbability.h"
00012 #include "osl/search/quiescenceSearch2.h"
00013 #include "osl/search/realizationProbability.h"
00014 #include "osl/search/moveWithComment.h"
00015 #include "osl/search/moveStackRejections.h"
00016 #include "osl/search/searchMonitor.h"
00017 #include "osl/search/usiReporter.h"
00018 #include "osl/checkmate/limitToCheckCount.h"
00019 #include "osl/eval/see.h"
00020 #include "osl/eval/pieceEval.h"
00021 #include "osl/checkmate/immediateCheckmate.h"
00022 #include "osl/record/csa.h"
00023 #include "osl/record/ki2.h"
00024 #include "osl/record/kanjiCode.h"
00025 #include "osl/move_classifier/pawnDropCheckmate.h"
00026 #include "osl/move_classifier/check_.h"
00027 #include "osl/move_classifier/moveAdaptor.h"
00028 #include "osl/move_generator/legalMoves.h"
00029 #include "osl/effect_util/additionalEffect.h"
00030 #include "osl/misc/nonBlockDelete.h"
00031 #include "osl/misc/ctime.h"
00032 #include "osl/misc/iconvConvert.h"
00033 #include "osl/stat/ratio.h"
00034 #include "osl/enter_king/enterKing.h"
00035 #include <boost/foreach.hpp>
00036 #include <stdexcept>
00037 #include <iostream>
00038 #include <iomanip>
00039
00040 #define search_assert(x, m) assert((x) || SearchState2::abort(m))
00041
00042 typedef osl::search::RealizationProbability Probabilities_t;
00043
00044 #ifdef CHECKMATE_COUNT
00045 static size_t root_checkmate = 0, checkmate_before = 0, checkmate_after = 0,
00046 count_threatmate = 0, quiesce_checkmate=0;
00047 #endif
00048
00049
00050
00051
00052 void osl::search::AlphaBeta2SharedRoot::
00053 showLastPv(int limit) const
00054 {
00055 for (int i=last_pv.size()-1; i>=0 && last_pv[i].depth == limit; --i) {
00056 std::cerr << last_pv[i].eval << ' ';
00057 for (size_t j=0; j<std::min((size_t)2, last_pv[i].pv.size()); ++j)
00058 std::cerr << record::csa::show(last_pv[i].pv[j]);
00059 std::cerr << " ";
00060 }
00061 std::cerr << "\n";
00062 }
00063
00064
00065
00066
00067
00068 #ifndef MINIMAL
00069 template <class EvalT>
00070 osl::CArray<int, osl::search::SearchState2Core::MaxDepth> osl::search::AlphaBeta2Tree<EvalT>::depth_node_count;
00071 #endif
00072
00073 template <class EvalT>
00074 osl::search::AlphaBeta2Tree<EvalT>::
00075 AlphaBeta2Tree(const NumEffectState& s, checkmate_t& c,
00076 SimpleHashTable *t, CountRecorder& r)
00077 : SearchBase<EvalT,SimpleHashTable,CountRecorder,RealizationProbability>(r, t),
00078 SearchState2(s, c), AlphaBeta2Common<EvalT>(s), node_count(0), shared_root(new AlphaBeta2SharedRoot)
00079 {
00080 #ifdef OSL_SMP
00081 for (int i=0; i<4; ++i) {
00082 try
00083 {
00084 shared.reset(new AlphaBeta2Parallel<EvalT>(this));
00085 break;
00086 }
00087 catch (std::bad_alloc&)
00088 {
00089 std::cerr << "panic " << i << " allocation of AlphaBeta2Parallel failed\n";
00090 #ifdef _WIN32
00091 boost::this_thread::sleep(boost::posix_time::seconds(1));
00092 #else
00093 sleep(1);
00094 #endif
00095 NonBlockDelete::deleteAll();
00096 }
00097 }
00098 #endif
00099 }
00100
00101 template <class EvalT>
00102 osl::search::AlphaBeta2Tree<EvalT>::
00103 AlphaBeta2Tree(const AlphaBeta2Tree<EvalT>& src, AlphaBeta2Parallel<EvalT> *)
00104 : SearchBase<EvalT,SimpleHashTable,CountRecorder,RealizationProbability>(src),
00105 SearchState2(src), SearchTimer(src), AlphaBeta2Common<EvalT>(src),
00106 node_count(0), shared(src.shared), shared_root(src.shared_root)
00107 {
00108 BOOST_FOREACH(PVVector& p, pv)
00109 p.clear();
00110 }
00111
00112 template <class EvalT>
00113 osl::search::AlphaBeta2Tree<EvalT>::
00114 ~AlphaBeta2Tree()
00115 {
00116 BOOST_FOREACH(MoveGenerator *p, generators)
00117 dealloc(p);
00118 #ifdef OSL_SMP
00119 if (shared && shared.use_count() == 1)
00120 NonBlockDelete::reset(shared);
00121 #endif
00122 }
00123
00124 template <class EvalT>
00125 osl::search::MoveGenerator *
00126 osl::search::AlphaBeta2Tree<EvalT>::alloc()
00127 {
00128 try
00129 {
00130 return new MoveGenerator;
00131 }
00132 catch (std::bad_alloc&)
00133 {
00134 std::cerr << "panic. allocation of MoveGenerator failed\n";
00135 throw TableFull();
00136 }
00137 return 0;
00138 }
00139
00140 template <class EvalT>
00141 void osl::search::AlphaBeta2Tree<EvalT>::dealloc(MoveGenerator *p)
00142 {
00143 delete p;
00144 }
00145
00146 template <class EvalT>
00147 osl::search::MoveGenerator& osl::search::AlphaBeta2Tree<EvalT>::makeGenerator()
00148 {
00149 const size_t cur_depth = this->curDepth();
00150 while (generators.size() <= cur_depth)
00151 generators.push_back(0);
00152 if (generators[cur_depth] == 0)
00153 generators[cur_depth] = alloc();
00154 return *generators[cur_depth];
00155 }
00156
00157
00158
00159
00160
00161 template <class EvalT>
00162 template <osl::Player P>
00163 int osl::search::AlphaBeta2Tree<EvalT>::
00164 alphaBetaSearchAfterMove(const MoveLogProb& moved, Window w,
00165 bool in_pv)
00166 {
00167 assert(w.alpha(P) % 2);
00168 assert(w.beta(P) % 2);
00169 assert(alt(P) == state().turn());
00170 assert(P == moved.player());
00171 assert(eval::notLessThan(P, w.beta(P), w.alpha(P)));
00172
00173
00174 if (state().inCheck(P)) {
00175 return this->minusInfty(P);
00176 }
00177 this->eval.update(state(), moved.move());
00178 const Player Turn = PlayerTraits<P>::opponent;
00179 const size_t previous_node_count = nodeCount();
00180
00181 pv[this->curDepth()].clear();
00182
00183 int result;
00184
00185
00186 boost::scoped_ptr<SimpleHashRecord> record_if_unavailable;
00187 int alloc_limit = curLimit(), memory1000 = lastMemoryUseRatio1000();
00188 const SimpleHashRecord *parent = hasLastRecord(1) ? lastRecord(1) : 0;
00189 const uint64_t table_use = this->table->memoryUse();
00190 if (table_use*8 > OslConfig::memoryUseLimit()
00191 && memory1000 > 300 && (root_limit >= 1600 || memory1000 > 500)
00192 && ! in_pv && ! state().inCheck() && (!parent||! parent->inCheck()))
00193 {
00194 if (table_use*6 > OslConfig::memoryUseLimit())
00195 alloc_limit -= std::max(root_limit - 1400, 200);
00196 else
00197 alloc_limit -= std::max((root_limit - 1400)*3/4, 0);
00198 if (memory1000 > 900)
00199 alloc_limit -= 400;
00200 else if (root_limit >= 1600 && memory1000 > 800)
00201 alloc_limit -= 200;
00202 else if (root_limit >= 1600 && memory1000 > 700)
00203 alloc_limit -= 100;
00204 alloc_limit = std::max(0, alloc_limit);
00205 }
00206 SimpleHashRecord *record
00207 = this->table->allocate(currentHash(), alloc_limit);
00208 const bool has_table_record = record;
00209 if (! record) {
00210 record_if_unavailable.reset(new SimpleHashRecord());
00211 record = record_if_unavailable.get();
00212 }
00213 setCurrentRecord(record);
00214 record->setInCheck(state().inCheck());
00215 #if 0
00216
00217 if (pass_count.loopByBothPass()) {
00218 return quiesce<Turn>(w);
00219 }
00220 #endif
00221 ++node_count;
00222 int consumption = moved.logProb();
00223
00224
00225 if (in_pv
00226 && (! record->bestMove().isNormal())) {
00227 assert(node_type[this->curDepth()] == PvNode);
00228 for (int limit = curLimit() - 200+1; limit > consumption+200;
00229 limit -= 200) {
00230 searchAllMoves<Turn>(moved.move(), limit,
00231 record, w);
00232 if (! record->bestMove().validMove()) {
00233 Move quiesce_best = record->qrecord.bestMove();
00234 if (quiesce_best.isNormal())
00235 record->setBestMove(quiesce_best, 200);
00236 }
00237 }
00238 }
00239 if (! in_pv) {
00240
00241 if (node_type[this->curDepth()] == PvNode)
00242 node_type[this->curDepth()] = AllNode;
00243 result = searchAllMoves<Turn>
00244 (moved.move(), consumption, record,
00245 Window(w.alpha(P), w.alpha(P)));
00246 } else {
00247
00248 assert(node_type[this->curDepth()] == PvNode);
00249 result = searchAllMoves<Turn>(moved.move(), consumption,
00250 record, w);
00251 }
00252 bool extended = false;
00253
00254 if (eval::betterThan(P, result, w.alpha(P))) {
00255 const SimpleHashRecord *parent = lastRecord(1);
00256 int consumption_here = consumption+1;
00257 const int re_search = 100;
00258 if (! w.null() && (! in_pv || consumption > re_search))
00259 consumption_here = std::min(consumption, re_search);
00260 else if (consumption > re_search
00261 && (record->threatmate().status(P).status() == ThreatmateState::CHECK_AFTER_THREATMATE
00262 || record->threatmate().mayHaveCheckmate(P)))
00263 consumption_here = re_search;
00264 else if (consumption > 150
00265 && ((parent && parent->inCheck())
00266 || state().hasEffectAt(P, state().kingSquare(alt(P)))))
00267 consumption_here = 150;
00268 if (consumption_here <= consumption) {
00269 node_type[this->curDepth()] = PvNode;
00270 extended = true;
00271 ext_limit.add(consumption - consumption_here);
00272 result = searchAllMoves<Turn>(moved.move(), consumption_here, record, w);
00273 }
00274 }
00275 ext.add(extended);
00276
00277 if (has_table_record)
00278 record->addNodeCount(nodeCount() - previous_node_count);
00279 return result;
00280 }
00281
00282 template <class EvalT>
00283 template <osl::Player Turn>
00284 int osl::search::AlphaBeta2Tree<EvalT>::
00285 searchAllMoves(Move m, int limit_consumption, SimpleHashRecord *record,
00286 Window w)
00287 {
00288 if (! w.null())
00289 assert(node_type[this->curDepth()] == PvNode);
00290 const Player P = PlayerTraits<Turn>::opponent;
00291 this->recorder.tryMove(MoveLogProb(m, limit_consumption),
00292 w.alpha(P), curLimit());
00293 subLimit(limit_consumption);
00294
00295 const int result = searchAllMoves<Turn>(record, w);
00296
00297 addLimit(limit_consumption);
00298 this->recorder.recordValue(MoveLogProb(m, limit_consumption),
00299 result,eval::betterThan(P, result, w.alpha(P)),
00300 curLimit());
00301 return result;
00302 }
00303
00304 template <class EvalT>
00305 template <osl::Player P>
00306 void osl::search::AlphaBeta2Tree<EvalT>::
00307 testThreatmate(SimpleHashRecord *record, bool in_pv)
00308 {
00309 if ((! record->inCheck())
00310 && (! (record && record->threatmate().isThreatmate(P)))
00311 && (in_pv || (this->curDepth() > 0
00312 && this->node_type[this->curDepth()-1] != CutNode))
00313 && tryThreatmate())
00314 {
00315 int threatmate_limit = 0;
00316 const SimpleHashRecord *parent = lastRecord(1);
00317 size_t node_count = record->nodeCount();
00318 if (parent)
00319 node_count = std::max(node_count, parent->nodeCount()/32);
00320 threatmate_limit = 4500-this->curDepth()*1000;
00321 int threatmate_max = 0;
00322 if ((node_count >= 1000 && this->recorder.checkmateRatio() < 0.5)
00323 || (node_count >= 200
00324 && (state().king8Info(P).libertyCount() == 0
00325 || state().king8Info(P).dropCandidate()
00326 || state().king8Info(P).template hasMoveCandidate<PlayerTraits<P>::opponent>(state()))))
00327 threatmate_max = 100;
00328 threatmate_limit = std::max(threatmate_limit, threatmate_max);
00329 if (! in_pv)
00330 threatmate_limit /= 2;
00331 if (root_limit < 800)
00332 threatmate_limit /= 2;
00333 #ifdef EXPERIMENTAL_QUIESCE
00334 if (curLimit() <= 400)
00335 threatmate_limit = 1;
00336 else if (curLimit() <= 500)
00337 threatmate_limit /= 16;
00338 else if (curLimit() <= 600)
00339 threatmate_limit /= 4;
00340 #endif
00341
00342 if (curLimit() >= this->table->minimumRecordLimit())
00343 {
00344 threatmate_limit = record->qrecord.threatmateNodesLeft(threatmate_limit);
00345 }
00346 else
00347 {
00348 threatmate_limit /= 2;
00349 }
00350
00351 Move threatmate_move;
00352 this->recorder.gotoCheckmateSearch(state(), threatmate_limit);
00353 #ifdef CHECKMATE_COUNT
00354 size_t count = checkmateSearcher().totalNodeCount();
00355 #endif
00356 bool threatmate
00357 = isThreatmateState<P>(threatmate_limit, threatmate_move);
00358 #ifdef CHECKMATE_COUNT
00359 count_threatmate += checkmateSearcher().totalNodeCount() - count;
00360 #endif
00361 if (threatmate_limit > 100) {
00362 updateCheckmateCount();
00363 testStop();
00364 }
00365 this->recorder.backFromCheckmateSearch();
00366 if (!threatmate && threatmate_limit == 0
00367 && record->qrecord.threatmateNodesLeft(2)) {
00368 threatmate = isThreatmateStateShort<P>(2, threatmate_move);
00369 }
00370 if (threatmate)
00371 {
00372 record->threatmate().setThreatmate(P, threatmate_move);
00373 }
00374 }
00375 }
00376
00377 template <class EvalT>
00378 template <osl::Player P>
00379 bool osl::search::AlphaBeta2Tree<EvalT>::
00380 tryCheckmate(SimpleHashRecord *record, bool in_pv, Move& checkmate_move)
00381 {
00382 int checkmate_limit = 1;
00383 if (! in_pv) {
00384 const SimpleHashRecord *parent = lastRecord(1);
00385 if (! (record->threatmate().mayHaveCheckmate(alt(P))
00386 || (parent && parent->threatmate().maybeThreatmate(alt(P)))))
00387 return false;
00388
00389 }
00390 if (in_pv && root_limit >= 500+this->rootLimitBias()) {
00391 int depth = this->curDepth();
00392 if (root_limit >= 700+this->rootLimitBias() && depth <= 3) {
00393 if ( (depth <= 1))
00394 checkmate_limit = checkmate::limitToCheckCount(3500);
00395 else if ( (depth == 2))
00396 checkmate_limit = 1000;
00397 else if ( (depth == 3))
00398 checkmate_limit = 200;
00399 }
00400 else if (((root_limit - curLimit()) <= 500) || (depth <= 5))
00401 {
00402 assert(static_cast<unsigned int>(curLimit()) < 4096);
00403 checkmate_limit = checkmate::limitToCheckCount(std::max(0,curLimit()-200-this->rootLimitBias()));
00404 }
00405 const SimpleHashRecord *parent = lastRecord(1);
00406 if (record->threatmate().mayHaveCheckmate(alt(P))
00407 || (parent && parent->threatmate().maybeThreatmate(alt(P))))
00408 checkmate_limit += std::max(100, checkmate_limit);
00409 else
00410 checkmate_limit = std::min((long)record->nodeCount()/2, (long)checkmate_limit);
00411 if (root_limit < 800)
00412 checkmate_limit /= 2;
00413 }
00414 if (curLimit() >= this->table->minimumRecordLimit())
00415 {
00416
00417 checkmate_limit = record->qrecord.checkmateNodesLeft(checkmate_limit);
00418 if (checkmate_limit <= 0)
00419 return false;
00420 }
00421 else
00422 {
00423 checkmate_limit /= 2;
00424 }
00425
00426
00427 #ifdef CHECKMATE_COUNT
00428 size_t count = checkmateSearcher().totalNodeCount();
00429 #endif
00430 this->recorder.gotoCheckmateSearch(state(), checkmate_limit);
00431 const bool win = isWinningState<P>
00432 (checkmate_limit, checkmate_move);
00433 if (checkmate_limit > 100)
00434 updateCheckmateCount();
00435 this->recorder.backFromCheckmateSearch();
00436 #ifdef CHECKMATE_COUNT
00437 checkmate_before += checkmateSearcher().totalNodeCount() - count;
00438 #endif
00439 if (this->root_limit >= 1600 && checkmate_limit >= 100)
00440 this->checkmate_searcher->runGC(this->table->isVerbose(),
00441 lastMemoryUseRatio1000());
00442 return win;
00443 }
00444
00445 template <class EvalT>
00446 template <osl::Player P>
00447 bool osl::search::AlphaBeta2Tree<EvalT>::
00448 tryCheckmateAgain(SimpleHashRecord *record, Move& checkmate_move,
00449 int node_count, int best_value)
00450 {
00451 int checkmate_limit = 1;
00452 if (record->threatmate().maybeThreatmate(P)) {
00453 if (EvalTraits<P>::betterThan(this->eval.captureValue(newPtypeO(P,KING)), best_value))
00454 checkmate_limit = node_count / 2;
00455 else
00456 checkmate_limit = node_count / 8;
00457 checkmate_limit += 100;
00458 if (this->recorder.checkmateRatio() < 0.5) {
00459 int checkmate_importance_wrt_root = this->recorder.searchNodeCount()/2
00460 + this->recorder.checkmateCount()/8;
00461 for (int i=0; i<this->curDepth(); ++i) {
00462 if (this->in_pv[i])
00463 checkmate_importance_wrt_root = checkmate_importance_wrt_root*7/8;
00464 else
00465 checkmate_importance_wrt_root /= 7;
00466 }
00467 checkmate_limit = std::max(checkmate_limit, checkmate_importance_wrt_root);
00468 }
00469 } else if (record->threatmate().mayHaveCheckmate(alt(P))) {
00470 checkmate_limit = countCheckAfterThreatmate(alt(P),2)*320 + 100;
00471 #ifdef MORE_CHECKMATE_IF_CAPTURE_MAJOR
00472 if (lastMove().isNormal() && isMajorNonPieceOK(lastMove().capturePtype())
00473 && ! state().hasEffectAt(P, lastMove().to()))
00474 checkmate_limit += 20000;
00475 #endif
00476 }
00477 if (curDepth() == 1 && hasLastRecord(1)) {
00478 const SimpleHashRecord *parent = lastRecord(1);
00479 if (parent->inCheck() || parent->threatmate().maybeThreatmate(alt(P)))
00480 checkmate_limit = std::max(checkmate_limit, (int)parent->nodeCount()/2+parent->checkmateNodes());
00481 }
00482
00483
00484 int checkmate_afford = this->nodeAffordable();
00485 #ifdef OSL_SMP
00486 checkmate_afford *= 1.5 / shared->max_threads;
00487 #endif
00488 if (checkmate_limit > 100 && checkmate_limit > checkmate_afford) {
00489 #ifndef NDEBUG
00490 if (checkmate_afford > 0 && this->timeAssigned().standard.toSeconds() >= 10.0)
00491 std::cerr << "adjust checkmate near timeover " << checkmate_limit << " => " << checkmate_afford << "\n";
00492 #endif
00493 checkmate_limit = checkmate_afford;
00494 }
00495
00496 if (true )
00497 checkmate_limit = record->qrecord.checkmateNodesLeft(checkmate_limit);
00498
00499 #ifdef CHECKMATE_COUNT
00500 size_t count = checkmateSearcher().totalNodeCount();
00501 #endif
00502 this->recorder.gotoCheckmateSearch(state(), checkmate_limit);
00503 const bool win = isWinningState<P>
00504 (checkmate_limit, checkmate_move);
00505 if (checkmate_limit > 100)
00506 updateCheckmateCount();
00507 this->recorder.backFromCheckmateSearch();
00508 #ifdef CHECKMATE_COUNT
00509 checkmate_after += checkmateSearcher().totalNodeCount() - count;
00510 #endif
00511 if (this->root_limit >= 1600 && checkmate_limit >= 100)
00512 this->checkmate_searcher->runGC(this->table->isVerbose(),
00513 lastMemoryUseRatio1000());
00514 return win;
00515 }
00516
00517 template <class EvalT>
00518 bool osl::search::
00519 AlphaBeta2Tree<EvalT>::tryPass(SimpleHashRecord *record, Player P) const
00520 {
00521 return ! record->inCheck()
00522 && ! record->threatmate().maybeThreatmate(P);
00523 }
00524
00525 template <class EvalT>
00526 template <osl::Player P>
00527 const osl::MoveLogProb osl::search::
00528 AlphaBeta2Tree<EvalT>::nextMove()
00529 {
00530 MoveGenerator& generator = makeGenerator();
00531 SimpleHashRecord *record = lastRecord();
00532 switch (this->move_type[this->curDepth()]) {
00533 case common_t::HASH:
00534 {
00535 if (curLimit() < this->leafLimit()) {
00536 this->move_type[this->curDepth()] = common_t::FINISH;
00537 break;
00538 }
00539 this->move_type[this->curDepth()] = common_t::TACTICAL;
00540 MoveLogProb best_move_in_table = record->bestMove();
00541 assert(curLimit() > 0);
00542 generator.init<eval_t>(curLimit(), record, this->eval, state(),
00543 node_type[this->curDepth()] != CutNode,
00544 best_move_in_table.move());
00545 if (best_move_in_table.validMove() &&
00546 this->validTableMove(state(), best_move_in_table, curLimit())) {
00547 if (this->in_pv[this->curDepth()]
00548 || best_move_in_table.move().capturePtype())
00549 best_move_in_table.setLogProbAtMost(RealizationProbability::TableMove);
00550 else
00551 best_move_in_table.setLogProbAtMost(state().inCheck() ? 120 : 150);
00552 return best_move_in_table;
00553 }
00554 }
00555
00556
00557 case common_t::TACTICAL:
00558 {
00559 MoveLogProb m = generator.nextTacticalMove<P>(*this);
00560 if (m.validMove())
00561 return m;
00562
00563 this->move_type[this->curDepth()] = common_t::KILLER;
00564 this->killers[this->curDepth()].clear();
00565 if ((! record->inCheck())
00566 && ! record->threatmate().maybeThreatmate(P)
00567 && (curLimit() >= 300)) {
00568 MoveVector killer_moves;
00569 getKillerMoves(killer_moves);
00570 BOOST_FOREACH(Move move, killer_moves) {
00571 assert(this->killers[this->curDepth()].size() < this->killers[this->curDepth()].capacity());
00572 this->killers[this->curDepth()].push_back(move);
00573 }
00574 std::reverse(this->killers[this->curDepth()].begin(), this->killers[this->curDepth()].end());
00575 }
00576 }
00577 case common_t::KILLER:
00578 {
00579 typename common_t::killer_t& killers = this->killers[this->curDepth()];
00580 if (! killers.empty()) {
00581 Move m = killers[killers.size()-1];
00582 killers.pop_back();
00583 return MoveLogProb(m, 300);
00584 }
00585
00586 this->move_type[this->curDepth()] = common_t::PASS;
00587 }
00588 case common_t::PASS:
00589 assert(record->inCheck() == state().inCheck());
00590 this->move_type[this->curDepth()] = common_t::ALL;
00591 if (tryPass(record, P)) {
00592 const int pass_consumption = (curLimit() >= 800) ? 300 : 200;
00593 return MoveLogProb(Move::PASS(P), pass_consumption);
00594 }
00595
00596
00597 case common_t::ALL:
00598 {
00599 MoveLogProb m = generator.nextMove<P>(*this);
00600 if (m.validMove())
00601 return m;
00602 this->move_type[this->curDepth()] = common_t::FINISH;
00603 }
00604 default:
00605 assert(this->move_type[this->curDepth()] == common_t::FINISH);
00606 }
00607 return MoveLogProb();
00608 }
00609
00610 template <class EvalT>
00611 template <osl::Player P>
00612 int osl::search::AlphaBeta2Tree<EvalT>::
00613 searchAllMoves(SimpleHashRecord *record, Window w)
00614 {
00615 #ifndef NDEBUG
00616 checkPointSearchAllMoves();
00617 #endif
00618 assert(P == state().turn());
00619 search_assert(w.isConsistent(), lastMove());
00620 assert(curLimit() >= 0);
00621
00622 assert(hasLastRecord(1));
00623 const SimpleHashRecord *parent = lastRecord(1);
00624 #ifndef DONT_USE_CHECKMATE
00625 const int node_count_at_beginning = nodeCount();
00626 #endif
00627 #if (! defined OSL_USE_RACE_DETECTOR) && (! defined MINIMAL)
00628 depth_node_count[this->curDepth()]++;
00629 #endif
00630 this->move_type[this->curDepth()] = common_t::INITIAL;
00631 const bool in_pv = this->in_pv[this->curDepth()] = ! w.null();
00632
00633
00634 if (record) {
00635 if (in_pv) {
00636 if (record->hasLowerBound(SearchTable::HistorySpecialDepth)) {
00637 int lower_bound = record->lowerBound();
00638 if (this->isWinValue(P, lower_bound)
00639 || (record->hasUpperBound(SearchTable::HistorySpecialDepth)
00640 && record->upperBound() == lower_bound))
00641 return lower_bound;
00642 }
00643 if (record->hasUpperBound(SearchTable::HistorySpecialDepth)) {
00644 int upper_bound = record->upperBound();
00645 if (this->isWinValue(alt(P), upper_bound))
00646 return upper_bound;
00647 }
00648 }
00649 else {
00650 int table_value = 0;
00651 if (record->hasGreaterLowerBound<P>(curLimit(), w.alpha(P),
00652 table_value)) {
00653 assert(eval::isConsistentValue(table_value));
00654 w.alpha(P) = table_value + EvalTraits<P>::delta;
00655 if (EvalTraits<P>::betterThan(table_value, w.beta(P))) {
00656 this->recorder.tableHitLowerBound(P, table_value, w.beta(P), curLimit());
00657 return table_value;
00658 }
00659 }
00660 if (record->hasLesserUpperBound<P>(curLimit(), w.beta(P), table_value)) {
00661 assert(eval::isConsistentValue(table_value));
00662 w.beta(P) = table_value - EvalTraits<P>::delta;
00663 if (EvalTraits<P>::betterThan(w.alpha(P), table_value))
00664 {
00665 this->recorder.tableHitUpperBound(P, table_value, w.alpha(P), curLimit());
00666 return table_value;
00667 }
00668 }
00669 }
00670
00671 Move checkmate_move=Move::INVALID();
00672 if ((!record->inCheck())
00673 && record->qrecord.checkmateNodesLeft(1)
00674 && isWinningStateShort<P>(2, checkmate_move))
00675 {
00676 this->recordWinByCheckmate(P, record, checkmate_move);
00677 return this->winByCheckmate(P);
00678 }
00679 #ifndef DONT_USE_CHECKMATE
00680 assert(record);
00681
00682 int additional_limit = 0;
00683 if (parent && parent->threatmate().maybeThreatmate(alt(P)))
00684 {
00685 additional_limit = std::max(100, parent->qrecord.threatmateNodes()/8);
00686 additional_limit = record->qrecord.checkmateNodesLeft(additional_limit);
00687 }
00688 this->recorder.gotoCheckmateSearch(state(), additional_limit);
00689 const bool win = isWinningState<P>(additional_limit, checkmate_move);
00690 updateCheckmateCount();
00691 this->recorder.backFromCheckmateSearch();
00692 if (win) {
00693 assert(checkmate_move.isValid());
00694 this->recordWinByCheckmate(P, record, checkmate_move);
00695 return this->winByCheckmate(P);
00696 }
00697 #endif
00698 }
00699
00700 search_assert(w.isConsistent(), lastMove());
00701 const int initial_alpha = w.alpha(P);
00702
00703 #ifndef DONT_USE_CHECKMATE
00704
00705 testThreatmate<P>(record, in_pv);
00706 #endif
00707
00708 record->qrecord.updateThreatmate(P, (parent ? &(parent->threatmate()) : 0),
00709 state().inCheck());
00710
00711 MoveLogProb best_move;
00712 int best_value = this->minusInfty(P);
00713 int tried_moves = 0;
00714 int alpha_update = 0;
00715 int last_alpha_update = 0;
00716 #if (defined OSL_SMP)
00717 int last_smp_idle = 0;
00718 # if (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00719 # if (! defined NDEBUG)
00720 bool already_split = false;
00721 # endif
00722 # endif
00723 #endif
00724
00725
00726 MoveLogProb m = nextMove<P>();
00727 ++tried_moves;
00728 if (! m.validMove() || m.logProb() > curLimit()) {
00729 goto move_generation_failure;
00730 }
00731 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00732 int first_move_node;
00733 #endif
00734 {
00735 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00736 const int previous_node_count = nodeCount();
00737 #endif
00738 assert(eval::betterThan(P, w.alpha(P), best_value));
00739 const int result = alphaBetaSearch<P>(m, w, in_pv);
00740 if (eval::betterThan(P, result, best_value)) {
00741 best_value = result;
00742 best_move = m;
00743 if (eval::betterThan(P, best_value, w.alpha(P))) {
00744 w.alpha(P) = result + EvalTraits<P>::delta;;
00745 assert(best_move.validMove());
00746 ++alpha_update;
00747 last_alpha_update = 1;
00748 if (eval::betterThan(P, result, w.beta(P))) {
00749 mpn_cut.add(tried_moves);
00750 if (this->move_type[this->curDepth()] >= common_t::ALL) {
00751 setKillerMove(best_move.move());
00752 if (best_move.isNormal()
00753 && ! best_move.move().isCapture())
00754 {
00755 const int d = (curLimit()+200)/100;
00756 this->historyTable().add(best_move.move(), d*d);
00757 }
00758 }
00759 assert(best_move.validMove());
00760 assert(! this->isWinValue(alt(P), best_value));
00761 goto register_table;
00762 } else {
00763 if (in_pv)
00764 makePV(m.move());
00765 }
00766 }
00767 }
00768 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00769 first_move_node = nodeCount() - previous_node_count;
00770 #endif
00771 }
00772 testStop();
00773
00774 #ifndef DONT_USE_CHECKMATE
00775 if (in_pv)
00776 {
00777 Move checkmate_move;
00778 if (tryCheckmate<P>(record, in_pv, checkmate_move)) {
00779 assert(checkmate_move.isValid());
00780 best_value= this->winByCheckmate(P);
00781 this->recordWinByCheckmate(P, record, checkmate_move);
00782 return best_value;
00783 }
00784 }
00785 #endif
00786 search_assert(w.isConsistent(), lastMove());
00787 if (curLimit() < this->leafLimit())
00788 goto move_generation_failure;
00789 while (true) {
00790 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00791 const bool prefer_split = shared && curLimit() >= shared->split_min_limit
00792 && (curLimit() >= 600+this->rootLimitBias()
00793
00794 || (first_move_node >= 30000));
00795 try {
00796 if (prefer_split) {
00797 int cur_smp_idle;
00798 {
00799 # ifdef OSL_USE_RACE_DETECTOR
00800 boost::mutex::scoped_lock lk(shared->lock_smp);
00801 #endif
00802 cur_smp_idle = shared->smp_idle;
00803 }
00804 if (cur_smp_idle > last_smp_idle) {
00805 last_smp_idle = cur_smp_idle;
00806 assert(! already_split);
00807 # if (! defined NDEBUG)
00808 already_split = true;
00809 # endif
00810 if (examineMovesOther<P>(w, best_move, best_value, tried_moves,
00811 alpha_update, last_alpha_update)) {
00812 assert(best_move.validMove());
00813 assert(best_move.player() == P);
00814 if (this->move_type[this->curDepth()] >= common_t::ALL) {
00815 setKillerMove(best_move.move());
00816 if (best_move.isNormal()
00817 && ! best_move.move().isCapture())
00818 {
00819 const int d = (curLimit()+200)/100;
00820 this->historyTable().add(best_move.move(), d*d);
00821 }
00822 }
00823 mpn_cut.add(tried_moves);
00824 goto register_table;
00825 }
00826 goto all_moves_done;
00827 }
00828 }
00829 }
00830 catch(AlphaBeta2ParallelCommon::SplitFailed&) {
00831 # if (! defined NDEBUG)
00832 already_split = false;
00833 # endif
00834
00835 }
00836 #endif
00837 MoveLogProb m = nextMove<P>();
00838 if (! m.validMove())
00839 break;
00840 ++tried_moves;
00841
00842 assert(eval::betterThan(P, w.alpha(P), best_value));
00843 const int result = alphaBetaSearch<P>(m, w, in_pv && ! best_move.validMove());
00844 if (eval::betterThan(P, result, best_value)) {
00845 best_value = result;
00846 best_move = m;
00847 if (eval::betterThan(P, best_value, w.alpha(P))) {
00848 w.alpha(P) = result + EvalTraits<P>::delta;;
00849 assert(best_move.validMove());
00850 ++alpha_update;
00851 last_alpha_update = tried_moves;
00852 if (eval::betterThan(P, result, w.beta(P))) {
00853 assert(best_move.validMove());
00854 if (this->move_type[this->curDepth()] >= common_t::ALL) {
00855 setKillerMove(best_move.move());
00856 if (best_move.isNormal()
00857 && ! best_move.move().isCapture())
00858 {
00859 const int d = (curLimit()+200)/100;
00860 this->historyTable().add(best_move.move(), d*d);
00861 }
00862 }
00863 mpn_cut.add(tried_moves);
00864 goto register_table;
00865 } else {
00866 if (in_pv)
00867 makePV(m.move());
00868 }
00869 }
00870 }
00871 }
00872 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00873 all_moves_done:
00874 #endif
00875 if (tried_moves == 1 && tryPass(record, P)) {
00876
00877 goto move_generation_failure;
00878 }
00879 mpn.add(tried_moves);
00880 if (alpha_update) {
00881 this->alpha_update.add(alpha_update);
00882 this->last_alpha_update.add(last_alpha_update);
00883 }
00884
00885 if (((this->curDepth() % 2) == 0 || OslConfig::usiMode())
00886 && EnterKing::canDeclareWin<P>(state())) {
00887 best_value = this->brinkmatePenalty(alt(P), std::max(1,16-this->curDepth())*256)
00888 + this->eval.value();
00889 record->setAbsoluteValue(Move::DeclareWin(), best_value,
00890 SearchTable::CheckmateSpecialDepth);
00891 return best_value;
00892 }
00893 if (record) {
00894
00895 #ifndef DONT_USE_CHECKMATE
00896 Move checkmate_move=Move::INVALID();
00897 if (tryCheckmateAgain<P>(record, checkmate_move,
00898 nodeCount() - node_count_at_beginning,
00899 best_value)) {
00900 assert(checkmate_move.isValid());
00901 best_value= this->winByCheckmate(P);
00902 this->recordWinByCheckmate(P, record, checkmate_move);
00903 return best_value;
00904 }
00905 #endif
00906 }
00907 register_table:
00908 assert(best_value == this->minusInfty(P) || best_move.validMove());
00909 assert(eval::isConsistentValue(best_value));
00910 if (this->isWinValue(alt(P), best_value))
00911 {
00912
00913
00914 best_value = this->brinkmatePenalty(P, std::max(1,16-this->curDepth())*256) + this->eval.value();
00915
00916
00917 record->setAbsoluteValue(best_move, best_value, curLimit());
00918 return best_value;
00919 }
00920 else if (EvalTraits<P>::betterThan(w.alpha(P), initial_alpha)) {
00921 if (best_move.validMove()) {
00922 assert(best_value % 2 == 0);
00923 record->setLowerBound(P, curLimit(), best_move, best_value);
00924 }
00925 }
00926 if (EvalTraits<P>::betterThan(w.beta(P), best_value)) {
00927 if (best_move.validMove())
00928 record->setUpperBound(P, curLimit(), best_move, best_value);
00929 }
00930 return best_value;
00931 move_generation_failure:
00932 pv[this->curDepth()].clear();
00933
00934
00935 best_value = quiesce<P>(w);
00936 if (record)
00937 {
00938 if (EvalTraits<P>::betterThan(best_value, initial_alpha)) {
00939 if (EvalTraits<P>::betterThan(w.beta(P), best_value)) {
00940 record->setAbsoluteValue(MoveLogProb(), best_value, curLimit());
00941 } else {
00942 record->setLowerBound(P, curLimit(), MoveLogProb(), best_value);
00943 }
00944 }
00945 else
00946 {
00947 assert(EvalTraits<P>::betterThan(w.beta(P), best_value));
00948 record->setUpperBound(P, curLimit(), MoveLogProb(), best_value);
00949 }
00950 }
00951 assert(eval::isConsistentValue(best_value));
00952 return best_value;
00953 }
00954
00955 template <class EvalT>
00956 template <osl::Player P>
00957 int osl::search::AlphaBeta2Tree<EvalT>::
00958 quiesce(Window w)
00959 {
00960 #ifdef EXPERIMENTAL_QUIESCE
00961 return quiesceExp<P>(w);
00962 #else
00963 return quiesceStable<P>(w);
00964 #endif
00965 }
00966
00967 template <class EvalT>
00968 template <osl::Player P>
00969 int osl::search::AlphaBeta2Tree<EvalT>::
00970 quiesceStable(Window w)
00971 {
00972 testStop();
00973 initPV();
00974
00975 typedef QuiescenceSearch2<eval_t> qsearcher_t;
00976 qsearcher_t qs(*this, *this->table);
00977 Move last_move = lastMove();
00978 if (last_move.isInvalid())
00979 last_move = Move::PASS(alt(P));
00980 assert(w.alpha(P) % 2);
00981 assert(w.beta(P) % 2);
00982 #ifdef CHECKMATE_COUNT
00983 size_t count = checkmateSearcher().totalNodeCount();
00984 #endif
00985 const int result = qs.template search<P>(w.alpha(P), w.beta(P), this->eval, last_move, 4);
00986 node_count += qs.nodeCount();
00987 this->recorder.addQuiescenceCount(qs.nodeCount());
00988 #ifdef CHECKMATE_COUNT
00989 quiesce_checkmate += checkmateSearcher().totalNodeCount() - count;
00990 #endif
00991
00992 assert(result % 2 == 0);
00993 return result;
00994 }
00995
00996 template <class EvalT>
00997 template <osl::Player P>
00998 int osl::search::AlphaBeta2Tree<EvalT>::
00999 quiesceExp(Window w)
01000 {
01001 testStop();
01002
01003 SimpleHashRecord *record = lastRecord();
01004 assert(record);
01005 Move best_move;
01006 const int qdepth = 4;
01007 const int previous_node_count = nodeCount();
01008
01009 int result =
01010 quiesceRoot<P>(w, qdepth, best_move, record->threatmate());
01011
01012 const size_t qnode = nodeCount() - previous_node_count;
01013 this->recorder.addQuiescenceCount(qnode);
01014 record->qrecord.setLowerBound(qdepth, result, best_move);
01015 return result;
01016 }
01017
01018 template <class EvalT>
01019 template <osl::Player P>
01020 struct osl::search::AlphaBeta2Tree<EvalT>::NextQMove
01021 {
01022 AlphaBeta2Tree *searcher;
01023 Window window;
01024 const int depth;
01025 int *result;
01026 DualThreatmateState threatmate;
01027 NextQMove(AlphaBeta2Tree *s, Window w, int d, int *r,
01028 DualThreatmateState t)
01029 : searcher(s), window(w), depth(d), result(r), threatmate(t) {
01030 }
01031 void operator()(Square ) {
01032 searcher->eval.update(searcher->state(), searcher->lastMove());
01033 *result =
01034 searcher->quiesce<P>(window, depth, threatmate);
01035 }
01036 };
01037
01038 template <class EvalT>
01039 template <osl::Player P>
01040 bool osl::search::AlphaBeta2Tree<EvalT>::
01041 quiesceWithMove(Move move, Window& w, int depth_left, Move& best_move, int& best_value,
01042 const DualThreatmateState& threatmate)
01043 {
01044
01045 const bool in_pv = ! w.null();
01046 int result;
01047 typedef NextQMove<PlayerTraits<P>::opponent> next_t;
01048 next_t helper(this, w, depth_left, &result, threatmate);
01049
01050 const HashKey new_hash = currentHash().newHashWithMove(move);
01051 const eval_t old_eval = this->eval;
01052 doUndoMoveOrPass<P,next_t>(new_hash, move, helper);
01053 this->eval = old_eval;
01054
01055 if (eval::betterThan(P, result, best_value)) {
01056 best_value = result;
01057 best_move = move;
01058 if (eval::betterThan(P, best_value, w.alpha(P))) {
01059 w.alpha(P) = result + EvalTraits<P>::delta;
01060 if (in_pv)
01061 makePV(best_move);
01062 if (eval::betterThan(P, result, w.beta(P))) {
01063 return true;
01064 }
01065 }
01066 }
01067 return false;
01068 }
01069
01070 template <class EvalT>
01071 template <osl::Player P>
01072 int osl::search::AlphaBeta2Tree<EvalT>::
01073 quiesceRoot(Window w, int depth_left, Move& best_move, DualThreatmateState threatmate)
01074 {
01075 assert(! state().inCheck(alt(P)));
01076
01077 initPV();
01078
01079
01080
01081 SimpleHashRecord& record = *lastRecord();
01082 assert(record.inCheck() == state().inCheck());
01083 assert(depth_left > 0);
01084
01085 int best_value = this->minusInfty(P);
01086
01087 if (! record.inCheck()) {
01088 if (! threatmate.maybeThreatmate(P)) {
01089 best_value = this->eval.value();
01090 } else {
01091 const int value = this->eval.value() + this->threatmatePenalty(P);
01092 best_value = EvalTraits<P>::max(best_value, value);
01093 }
01094 best_move = Move::PASS(P);
01095 if (EvalTraits<P>::betterThan(best_value, w.alpha(P))) {
01096 if (EvalTraits<P>::betterThan(best_value, w.beta(P)))
01097 return best_value;
01098 w.alpha(P) = best_value + EvalTraits<P>::delta;
01099 }
01100 }
01101
01102 Move prev_best = record.qrecord.bestMove();
01103 MoveGenerator& generator = makeGenerator();
01104 generator.init(200, &record, this->eval, state(),
01105 w.alpha(P) == w.beta(P),
01106 prev_best, true);
01107 int tried_moves = 0;
01108
01109 if (prev_best.isNormal()) {
01110 ++tried_moves;
01111 if (quiesceWithMove<P>(prev_best, w, depth_left-1, best_move, best_value,
01112 threatmate))
01113 goto finish;
01114 }
01115
01116
01117
01118 for (MoveLogProb m = generator.nextTacticalMove<P>(*this);
01119 m.validMove(); m = generator.nextTacticalMove<P>(*this)) {
01120 ++tried_moves;
01121 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01122 threatmate))
01123 goto finish;
01124 }
01125 for (MoveLogProb m = generator.nextMove<P>(*this);
01126 m.validMove(); m = generator.nextMove<P>(*this)) {
01127 ++tried_moves;
01128 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01129 threatmate))
01130 goto finish;
01131 }
01132
01133
01134 if (record.inCheck()) {
01135 if (tried_moves == 0) {
01136 if (lastMove().isNormal() && lastMove().ptype() == PAWN && lastMove().isDrop())
01137 return this->winByFoul(P);
01138 return this->winByCheckmate(alt(P));
01139 }
01140 goto finish;
01141 }
01142 finish:
01143 return best_value;
01144 }
01145
01146 template <class EvalT>
01147 template <osl::Player P>
01148 int osl::search::AlphaBeta2Tree<EvalT>::
01149 quiesce(Window w, int depth_left, DualThreatmateState parent_threatmate)
01150 {
01151 if (state().inCheck(alt(P))) {
01152 return this->minusInfty(alt(P));
01153 }
01154
01155 initPV();
01156 #ifndef MINIMAL
01157 depth_node_count_quiesce[this->curDepth()]++;
01158 #endif
01159 ++node_count;
01160
01161 SimpleHashRecord record;
01162 record.setInCheck(state().inCheck());
01163
01164 DualThreatmateState threatmate;
01165 threatmate.updateInLock(P, &parent_threatmate, record.inCheck());
01166
01167 int best_value = this->minusInfty(P);
01168
01169 if (depth_left <= 0) {
01170 if (record.inCheck()) {
01171 if (lastMove().isCapture())
01172 depth_left +=2;
01173 else
01174 depth_left = 0;
01175 }
01176 else if (threatmate.maybeThreatmate(P)) {
01177 if (threatmate.mayHaveCheckmate(alt(P))) {
01178 Move checkmate_move;
01179 bool win = isWinningState<P>(10, checkmate_move);
01180 if (win)
01181 return this->winByCheckmate(P);
01182 }
01183 return this->eval.value() + this->threatmatePenalty(P);
01184 }
01185 else {
01186 if (threatmate.mayHaveCheckmate(alt(P)))
01187 return this->eval.value() + this->threatmatePenalty(alt(P));
01188 if (ImmediateCheckmate::hasCheckmateMove<P>(state()))
01189 return this->winByCheckmate(P);
01190 if (ImmediateCheckmate::hasCheckmateMove<PlayerTraits<P>::opponent>(state()))
01191 return this->eval.value() + this->threatmatePenalty(P);
01192 return this->eval.value();
01193 }
01194 }
01195
01196 if (! record.inCheck()) {
01197 if (ImmediateCheckmate::hasCheckmateMove<P>(state())) {
01198 return this->winByCheckmate(P);
01199 }
01200 }
01201 if (threatmate.mayHaveCheckmate(alt(P))) {
01202 Move checkmate_move;
01203 bool win = isWinningState<P>(10, checkmate_move);
01204 if (win)
01205 return this->winByCheckmate(P);
01206 }
01207 MoveGenerator& generator = makeGenerator();
01208 generator.init(200, &record, this->eval, state(),
01209 w.alpha(P) == w.beta(P),
01210 Move(), true);
01211 int tried_moves = 0;
01212 Move best_move;
01213
01214 if (! record.inCheck()) {
01215 if (! threatmate.maybeThreatmate(P)) {
01216 best_value = this->eval.value();
01217 } else {
01218 const int value = this->eval.value() + this->threatmatePenalty(P);
01219 best_value = EvalTraits<P>::max(best_value, value);
01220 }
01221 best_move = Move::PASS(P);
01222 if (EvalTraits<P>::betterThan(best_value, w.alpha(P))) {
01223 if (EvalTraits<P>::betterThan(best_value, w.beta(P)))
01224 return best_value;
01225 w.alpha(P) = best_value + EvalTraits<P>::delta;
01226 }
01227 }
01228
01229
01230 for (MoveLogProb m = generator.nextTacticalMove<P>(*this);
01231 m.validMove(); m = generator.nextTacticalMove<P>(*this)) {
01232 ++tried_moves;
01233 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01234 threatmate))
01235 goto finish;
01236 }
01237 for (MoveLogProb m = generator.nextMove<P>(*this);
01238 m.validMove(); m = generator.nextMove<P>(*this)) {
01239 ++tried_moves;
01240 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01241 threatmate))
01242 goto finish;
01243 }
01244
01245
01246 if (record.inCheck()) {
01247 if (tried_moves == 0) {
01248 if (lastMove().ptype() == PAWN && lastMove().isDrop())
01249 return this->winByFoul(P);
01250 return this->winByCheckmate(alt(P));
01251 }
01252 goto finish;
01253 }
01254 finish:
01255 return best_value;
01256 }
01257
01258 template <class EvalT>
01259 void osl::search::AlphaBeta2Tree<EvalT>::updateCheckmateCount()
01260 {
01261 #ifdef OSL_SMP
01262 if (shared) {
01263 const size_t new_count = shared->checkmateCount();
01264 this->recorder.setCheckmateCount(new_count);
01265 return;
01266 }
01267 #endif
01268 this->recorder.setCheckmateCount
01269 (checkmateSearcher().totalNodeCount());
01270 }
01271
01272 template <class EvalT>
01273 int
01274 osl::search::AlphaBeta2Tree<EvalT>::
01275 rootAlpha(Player P, int last_value, Progress16 progress)
01276 {
01277 int pawns = 3;
01278 if (eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(alt(P),KING))))
01279 {
01280 pawns = 10;
01281 }
01282 else if (progress.value() <= 1)
01283 {
01284 pawns = 3;
01285 }
01286 else if (progress.value() <= 7)
01287 {
01288 pawns = 4;
01289 }
01290 else if (progress.value() <= 9)
01291 {
01292 pawns = 5;
01293 }
01294 else if (progress.value() <= 10)
01295 {
01296 pawns = 6;
01297 }
01298 else
01299 {
01300 pawns = 7;
01301 }
01302 const int width = eval_t::captureValue(newPtypeO(alt(P),PAWN))*pawns/2;
01303 return last_value - width - eval::delta(P);
01304 }
01305
01306 template <class EvalT>
01307 int
01308 osl::search::AlphaBeta2Tree<EvalT>::
01309 stableThreshold(Player P, int last_value)
01310 {
01311 int pawns = 3;
01312 if (eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(alt(P),KING))))
01313 pawns = 10;
01314 else if (eval::betterThan(P, eval_t::captureValue(newPtypeO(alt(P),PAWN))*2, last_value)
01315 && eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(P,PAWN))*2))
01316 pawns = 2;
01317 const int width = eval_t::captureValue(newPtypeO(alt(P),PAWN))*pawns/2;
01318 return last_value - width - eval::delta(P);
01319 }
01320
01321 namespace osl
01322 {
01323 namespace
01324 {
01325 void find_threatmate(const SimpleHashTable& table, HashKey key,
01326 const search::SearchState2::PVVector& pv,
01327 CArray<bool, search::SearchState2::MaxDepth>& threatmate)
01328 {
01329 for (size_t i=0; i<pv.size(); ++i) {
01330 key = key.newMakeMove(pv[i]);
01331 const SimpleHashRecord *record = table.find(key);
01332 threatmate[i] = record
01333 && record->threatmate().isThreatmate(key.turn());
01334 }
01335 }
01336 }
01337 }
01338
01339 template <class EvalT>
01340 void osl::search::AlphaBeta2Tree<EvalT>::
01341 updateRootPV(Player P, std::ostream& os, int result, Move m)
01342 {
01343 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01344 this->makePV(m);
01345 const int last_root_value = shared_root->root_values_for_iteration.size() ? shared_root->root_values_for_iteration.back() : 0;
01346 const int threshold = stableThreshold(P, last_root_value);
01347 bool new_stable = eval::betterThan(P, result, threshold);
01348 shared_root->last_root_value_update = result;
01349
01350 if (new_stable && m != shared_root->last_root_move
01351 && (See::see(state(), m) < -eval::Ptype_Eval_Table.value(KNIGHT)*2
01352 || eval::betterThan(P, result, eval_t::captureValue(newPtypeO(alt(P),KING))))) {
01353 new_stable = false;
01354 }
01355 if (new_stable && shared_root->root_values_for_iteration.size() > 1) {
01356 const int last_root_value2 = shared_root->root_values_for_iteration[shared_root->root_values_for_iteration.size()-2];
01357 const int threshold2 = stableThreshold(P, last_root_value2);
01358 if (eval::betterThan(P, threshold2, result)
01359 && eval::betterThan(P, last_root_value2, last_root_value))
01360 new_stable = false;
01361 }
01362 this->shared_root->last_pv.push_back(RootPV(root_limit, pv[0], result));
01363 this->setStable(new_stable);
01364 #ifndef GPSONE
01365 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
01366 const double scale = OslConfig::usiOutputPawnValue()*2.0
01367 / eval_t::captureValue(newPtypeO(alt(P),PAWN));
01368 CArray<bool, MaxDepth> threatmate = {{ 0 }};
01369 find_threatmate(*this->table, currentHash(), pv[0], threatmate);
01370 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01371 this->monitors())
01372 monitor->showPV(root_limit/200, this->recorder.allNodeCount(),
01373 this->elapsed(), static_cast<int>(result*scale),
01374 m, &*pv[0].begin(), &*pv[0].end(),
01375 &threatmate[0], &threatmate[0]+pv[0].size());
01376 }
01377 #endif
01378 if (this->table->isVerbose()) {
01379 showPV(os, result, m, new_stable ? ' ' : '*');
01380 }
01381 }
01382
01383 template <class EvalT>
01384 void osl::search::AlphaBeta2Tree<EvalT>::
01385 addMultiPV(Player P, int result, Move m)
01386 {
01387 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01388 this->makePV(m);
01389 this->shared_root->last_pv.push_back(RootPV(root_limit, pv[0], result));
01390 std::swap(*this->shared_root->last_pv.rbegin(), *(this->shared_root->last_pv.rbegin()+1));
01391
01392 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
01393 const double scale = OslConfig::usiOutputPawnValue()*2.0
01394 / eval_t::captureValue(newPtypeO(alt(P),PAWN));
01395 CArray<bool, MaxDepth> threatmate = {{ 0 }};
01396 find_threatmate(*this->table, currentHash(), pv[0], threatmate);
01397 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01398 this->monitors())
01399 monitor->showPV(root_limit/200, this->recorder.allNodeCount(),
01400 this->elapsed(), static_cast<int>(result*scale),
01401 m, &*pv[0].begin(), &*pv[0].end(),
01402 &threatmate[0], &threatmate[0]+pv[0].size());
01403 }
01404
01405 if (this->table->isVerbose()) {
01406 showPV(std::cerr, result, m, '&');
01407 }
01408 }
01409
01410 template <class EvalT>
01411 void osl::search::AlphaBeta2Tree<EvalT>::
01412 showFailLow(int result, Move m) const
01413 {
01414 if (this->root_ignore_moves)
01415 std::cerr << "[" << this->root_ignore_moves->size() << "] ";
01416 std::cerr << " <" << std::setfill(' ') << std::setw(5)
01417 << static_cast<int>(result*200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN)))
01418 << " " << record::csa::show(m) << "\n";
01419 }
01420
01421 template <class EvalT>
01422 void osl::search::AlphaBeta2Tree<EvalT>::
01423 showPV(std::ostream& os, int result, Move m, char stable_char) const
01424 {
01425 assert(m.isNormal());
01426 if (this->root_ignore_moves)
01427 os << "[" << this->root_ignore_moves->size() << "] ";
01428 os << stable_char;
01429 os << " " << std::setfill(' ') << std::setw(5)
01430 << static_cast<int>(result*200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN))) << " ";
01431 BOOST_FOREACH(Move m, pv[0]) {
01432 os << record::csa::show(m);
01433 }
01434 const double elapsed = this->elapsed();
01435 if (elapsed > 1.0)
01436 os << " (" << elapsed << "s, " << OslConfig::memoryUseRatio()*100.0 << "%)";
01437 os << std::endl;
01438 #ifndef MINIMAL
01439 #ifndef _WIN32
01440 if (! OslConfig::usiMode())
01441 {
01442 NumEffectState state = this->state();
01443 std::string str; str.reserve(200); str = " ";
01444 for (size_t i=0; i<pv[0].size(); ++i) {
01445 str += record::ki2::show(pv[0][i], state, i ? pv[0][i-1] : Move());
01446 state.makeMove(pv[0][i]);
01447
01448
01449 const SimpleHashRecord *record
01450 = this->table->find(HashKey(state));
01451 if (record &&
01452 record->threatmate().isThreatmate(state.turn()))
01453 str += "(" K_TSUMERO ")";
01454 }
01455 std::string converted = IconvConvert::eucToLang(str);
01456 if (! converted.empty())
01457 os << converted << std::endl;
01458 }
01459 #endif
01460 #endif
01461
01462 #ifdef DEBUG_PV
01463 NumEffectState s = state();
01464 for (size_t i=0; i<pv[0].size(); ++i) {
01465 if (! pv[0][i].isPass() && ! s.isValidMove(pv[0][i])) {
01466 std::cerr << "root pv error " << pv[0][i] << " " << i << "\n";
01467 break;
01468 }
01469 ApplyMoveOfTurn::doMove(s, pv[0][i]);
01470 }
01471 #endif
01472 }
01473
01474 template <class EvalT>
01475 template <osl::Player P>
01476 struct osl::search::AlphaBeta2Tree<EvalT>::NextMove
01477 {
01478 AlphaBeta2Tree *searcher;
01479 const MoveLogProb& moved;
01480 Window window;
01481 int *result;
01482 bool in_pv;
01483 NextMove(AlphaBeta2Tree *s, const MoveLogProb& md, Window w, int *r,
01484 bool p)
01485 : searcher(s), moved(md), window(w), result(r), in_pv(p) {
01486 assert(P == md.player());
01487 }
01488 void operator()(Square ) {
01489 #ifndef NDEBUG
01490 const int cur_limit = searcher->curLimit();
01491 #endif
01492 *result =
01493 searcher->alphaBetaSearchAfterMove<P>(moved, window, in_pv);
01494 assert(cur_limit == searcher->curLimit() || searcher->SearchState2Core::abort());
01495 }
01496 };
01497
01498 template <class EvalT>
01499 template <osl::Player P>
01500 int osl::search::AlphaBeta2Tree<EvalT>::
01501 alphaBetaSearch(const MoveLogProb& search_move, Window w, bool in_pv)
01502 {
01503 assert(w.alpha(P) % 2);
01504 assert(w.beta(P) % 2);
01505 const Move move = search_move.move();
01506 assert(P == move.player());
01507 assert(P == state().turn());
01508 assert(eval::notLessThan(P, w.beta(P), w.alpha(P)));
01509
01510 testStop();
01511 pv[curDepth()+1].clear();
01512
01513 if (! move.isPass() ){
01514 if(MoveStackRejections::probe<P>(state(),history(),curDepth(),move,w.alpha(P),repetitionCounter().checkCount(alt(P)))){
01515 return this->winByLoop(alt(P));
01516 }
01517 if (move_classifier::MoveAdaptor<move_classifier::PawnDropCheckmate<P> >
01518 ::isMember(state(), move))
01519 return this->winByFoul(alt(P));
01520 }
01521
01522 const HashKey new_hash = currentHash().newHashWithMove(move);
01523 assert(P == move.player());
01524
01525 if (move.isPass())
01526 this->pass_count.inc(P);
01527
01528
01529 if (! this->pass_count.loopByBothPass()) {
01530 const Sennichite next_sennichite
01531 = repetition_counter.isAlmostSennichite(new_hash);
01532 if (next_sennichite.isDraw())
01533 return this->drawValue();
01534 if (next_sennichite.hasWinner())
01535 return this->winByFoul(next_sennichite.winner());
01536 assert(next_sennichite.isNormal());
01537 }
01538
01539 if (! move.isPass()) {
01540
01541 const DominanceCheck::Result has_dominance
01542 = DominanceCheck::detect(repetition_counter.history(), new_hash);
01543 if (has_dominance == DominanceCheck::LOSE)
01544 return this->winByLoop(alt(P));
01545 if (has_dominance == DominanceCheck::WIN)
01546 return this->winByLoop(P);
01547
01548 if (! move.isCapture()) {
01549 const int sacrifice_count = countSacrificeCheck2(this->curDepth());
01550 if (sacrifice_count == 2) {
01551
01552 const Square to = move.to();
01553 int offence = state().countEffect(P, to) + (move.isDrop() ? 1 : 0);
01554 const int deffense = state().hasEffectAt(alt(P), to);
01555 if (offence <= deffense)
01556 offence += AdditionalEffect::count2(state(), to, P);
01557 if (offence <= deffense) {
01558 return this->winByLoop(alt(P));
01559 }
01560 }
01561 }
01562 }
01563
01564 int result;
01565 NextMove<P> helper(this, search_move, w, &result, in_pv);
01566
01567 this->recorder.addNodeCount();
01568 const eval_t old_eval = this->eval;
01569 doUndoMoveOrPass<P,NextMove<P> >(new_hash, move, helper);
01570 this->eval = old_eval;
01571 if (move.isPass())
01572 this->pass_count.dec(P);
01573
01574 return result;
01575 }
01576
01577 template <class EvalT>
01578 template <osl::Player P>
01579 void osl::search::AlphaBeta2Tree<EvalT>::
01580 examineMovesRoot(const MoveLogProbVector& moves, size_t i, Window window,
01581 MoveLogProb& best_move, int& best_value)
01582 {
01583 for (;i<moves.size(); ++i) {
01584 testStop();
01585
01586 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_ROOT)
01587 if (shared && i > 8
01588 && moves.size() > i+1) {
01589 int smp_idle;
01590 {
01591 # ifdef OSL_USE_RACE_DETECTOR
01592 boost::mutex::scoped_lock lk(shared->lock_smp);
01593 # endif
01594 smp_idle = shared->smp_idle;
01595 }
01596 if (smp_idle) {
01597 try {
01598 examineMovesRootPar<P>(moves, i, window, best_move, best_value);
01599 break;
01600 } catch (AlphaBeta2ParallelCommon::SplitFailed&) {
01601 }
01602 }
01603 }
01604 #endif
01605
01606 const MoveLogProb& m = moves[i];
01607 #ifndef GPSONE
01608 if (this->elapsed() > 1.0)
01609 {
01610 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01611 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01612 this->monitors())
01613 monitor->rootMove(m.move());
01614 }
01615 if (this->multi_pv) {
01616 int width = this->multi_pv*this->eval.captureValue(newPtypeO(P, PAWN))/200;
01617 if (width % 2 == 0)
01618 width -= EvalTraits<P>::delta;
01619 window.alpha(P) = best_value + width;
01620 }
01621 #endif
01622 const int result = alphaBetaSearch<P>(m, window, false);
01623 if (eval::betterThan(P, result, best_value))
01624 {
01625 window.alpha(P) = result + EvalTraits<P>::delta;
01626 best_move = m;
01627 best_value = result;
01628 updateRootPV(P, std::cerr, result, m.move());
01629 if (eval::betterThan(P, result, window.beta(P))) {
01630 assert(! this->isWinValue(alt(P), result));
01631 break;
01632 }
01633 }
01634 #ifndef GPSONE
01635 else if (this->multi_pv && eval::betterThan(P, result, window.alpha(P)))
01636 {
01637 addMultiPV(P, result, m.move());
01638 }
01639 #endif
01640 if (this->root_limit >= 1600)
01641 this->checkmate_searcher->runGC(this->table->isVerbose(),
01642 lastMemoryUseRatio1000());
01643 }
01644 }
01645
01646
01647
01648 template <class EvalT>
01649 osl::search::AlphaBeta2<EvalT>::
01650 AlphaBeta2(const NumEffectState& s, checkmate_t& c,
01651 SimpleHashTable *t, CountRecorder& r)
01652 : AlphaBeta2Tree<EvalT>(s, c, t, r)
01653 {
01654 MoveGenerator::initOnce();
01655 }
01656
01657 template <class EvalT>
01658 osl::search::AlphaBeta2<EvalT>::
01659 ~AlphaBeta2()
01660 {
01661 }
01662
01663 template <class EvalT>
01664 typename osl::search::AlphaBeta2<EvalT>::PVCheckmateStatus osl::search::AlphaBeta2<EvalT>::
01665 findCheckmateInPV(int verify_node, CArray<bool,2>& king_in_threat)
01666 {
01667 king_in_threat.fill(false);
01668 if (this->shared_root->last_pv.empty())
01669 return PVStable;
01670 const SearchState2::PVVector& pv = this->shared_root->last_pv.back().pv;
01671 NumEffectState state = this->state();
01672 PathEncoding path = this->path();
01673 PVCheckmateStatus found = PVStable;
01674 SearchState2::checkmate_t *checkmate_searcher = this->checkmate_searcher;
01675 if (this->node_count < verify_node*pv.size())
01676 verify_node = this->node_count/(pv.size()+1)/4;
01677 for (size_t i=0; i<pv.size(); ++i)
01678 {
01679 this->checkmate_searcher->runGC(this->table->isVerbose(),
01680 this->lastMemoryUseRatio1000());
01681 assert(pv[i].isPass() || state.isValidMove(pv[i]));
01682 if (! pv[i].isPass() && ! state.isValidMove(pv[i]))
01683 {
01684 std::cerr << "pv error " << pv[i] << "\n" << state;
01685 return PVStable;
01686 }
01687 state.makeMove(pv[i]);
01688 path.pushMove(pv[i]);
01689 if (state.inCheck())
01690 continue;
01691 const HashKey key(state);
01692 SimpleHashRecord *record = this->table->allocate(key, 2000);
01693 if (! record)
01694 break;
01695 Move checkmate_move, threatmate_move;
01696 const bool old_win = this->isWinningState
01697 (*checkmate_searcher, state, key, path,
01698 0, checkmate_move, pv[i]);
01699 if (! old_win)
01700 {
01701 const bool new_win = this->isWinningState
01702 (*checkmate_searcher, state, key, path,
01703 verify_node, checkmate_move, pv[i], true);
01704 if (new_win)
01705 {
01706 found = PVCheckmate;
01707 this->recordWinByCheckmate(state.turn(), record, checkmate_move);
01708 king_in_threat[alt(state.turn())] = true;
01709 if (this->table->isVerbose())
01710 std::cerr << " pv checkmate " << record::csa::show(pv[i])
01711 << "(" << i << ")\n";
01712 }
01713 }
01714 state.changeTurn();
01715 const Player T = state.turn();
01716 const bool old_threatmate_in_record = record->threatmate().isThreatmate(alt(T));
01717 const bool old_threatmate = this->isWinningState
01718 (*checkmate_searcher, state, HashKey(state), PathEncoding(T),
01719 1, threatmate_move, Move::PASS(alt(T)));
01720 if (! old_threatmate)
01721 {
01722 const bool new_threatmate = this->isWinningState
01723 (*checkmate_searcher, state, HashKey(state), PathEncoding(T),
01724 verify_node, threatmate_move, Move::PASS(alt(T)), this->root_limit >= 1000 + this->rootLimitBias());
01725 if (new_threatmate)
01726 {
01727 record->threatmate().setThreatmate(alt(T), threatmate_move);
01728 king_in_threat[alt(T)] = true;
01729 if (! old_threatmate_in_record)
01730 found = PVThreatmate;
01731 else if (found == PVStable)
01732 found = PVThreatmateNotRecord;
01733 if (this->table->isVerbose())
01734 std::cerr << " pv threatmate " << record::csa::show(pv[i])
01735 << "(" << i << ")\n";
01736 }
01737 }
01738 state.changeTurn();
01739 }
01740 this->checkmate_searcher->runGC(this->table->isVerbose(),
01741 this->lastMemoryUseRatio1000());
01742 this->updateCheckmateCount();
01743 return found;
01744 }
01745
01746 template <class EvalT>
01747 int osl::search::AlphaBeta2<EvalT>::
01748 alphaBetaSearchRoot(MoveLogProb& best_move, int limit)
01749 {
01750 const Player Turn = this->state().turn();
01751 Window root_window = this->fullWindow(Turn);
01752 return alphaBetaSearchRoot(root_window, best_move, limit);
01753 }
01754
01755 template <class EvalT>
01756 osl::Move osl::search::AlphaBeta2<EvalT>::
01757 computeBestMoveIteratively(int limit, const int step,
01758 int initial_limit, size_t node_limit,
01759 const TimeAssigned& assign,
01760 MoveWithComment *additional_info)
01761 {
01762 this->setStartTime(MilliSeconds::now());
01763 this->setTimeAssign(assign);
01764 if (this->table->verboseLevel() > 2)
01765 {
01766 const time_t now = time(0);
01767 char ctime_buf[64];
01768 std::cerr << "AlphaBeta2 " << ctime_r(&now, ctime_buf);
01769 }
01770 if (this->table->isVerbose()) {
01771 std::cerr << " time assign/max " << this->timeAssigned().standard.toSeconds()
01772 << "/" << this->timeAssigned().max.toSeconds()
01773 << " multipv " << this->multi_pv
01774 << " iteration " << this->nextIterationCoefficient()
01775 << " mem " << std::fixed << std::setprecision(2)
01776 << OslConfig::memoryUseRatio()*100.0 << "%";
01777 std::cerr << "\n";
01778 }
01779 initial_limit = std::min(initial_limit, limit);
01780
01781 this->recorder.resetNodeCount();
01782
01783 double last_iteration_consumed = 0;
01784 double total_consumed = 0;
01785 int limit_iterative = initial_limit;
01786 Move last_best_move = Move::INVALID();
01787 this->shared_root->last_pv.clear();
01788
01789 #ifdef OSL_SMP
01790 # ifdef SPLIT_STAT
01791 if (this->shared) {
01792 this->shared->parallel_splits = 0;
01793 this->shared->cancelled_splits.setValue(0);
01794 this->shared->parallel_abort.setValue(0);
01795 }
01796 # endif
01797 #endif
01798 try
01799 {
01800 if (this->table->verboseLevel() > 1)
01801 {
01802 MoveVector moves;
01803 move_generator::LegalMoves::generate(this->state(), moves);
01804 BOOST_FOREACH(Move move, moves) {
01805 HashKey key = this->currentHash().newHashWithMove(move);
01806 const SimpleHashRecord *record = this->table->find(key);
01807 if (! record || record->lowerLimit() < SearchTable::HistorySpecialDepth)
01808 continue;
01809 std::cerr << "prebound value " << record::csa::show(move)
01810 << " " << record->lowerBound() << " " << record->upperBound() << "\n";
01811 }
01812 }
01813
01814 MoveLogProb search_move;
01815 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, 0));
01816 this->shared_root->last_root_move = search_move.move();
01817 this->shared_root->best_move_for_iteration.push_back(search_move.move());
01818 if (this->table->verboseLevel() > 1)
01819 std::cerr << "=> quiesce "
01820 << record::csa::show(search_move.move()) << "\n";
01821 while (limit_iterative < limit && ! this->stopping())
01822 {
01823 if (this->table->verboseLevel() > 1)
01824 std::cerr << "=> iteration " << limit_iterative
01825 << " (" << last_iteration_consumed << ", " << total_consumed << " sec)"
01826 << " mem " << OslConfig::memoryUseRatio()*100.0 << "%\n";
01827 this->recorder.startSearch(limit_iterative);
01828 const int previous_node_count = this->nodeCount();
01829 try {
01830 for (int i=0; i<8; ++i)
01831 {
01832 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, limit_iterative+this->rootLimitBias()));
01833 this->shared_root->last_root_move = search_move.move();
01834 last_best_move = search_move.move();
01835 if (this->stopping())
01836 break;
01837 PVCheckmateStatus need_more_verify = PVStable;
01838 CArray<bool, 2> king_in_threat;
01839 int verify_node_limit = limit <= (1200 + this->rootLimitBias()) ? 10000 : 40000;
01840 if (this->timeAssigned().standard.toSeconds() < 20)
01841 verify_node_limit /= 4;
01842 #ifdef DONT_USE_CHECKMATE
01843 break;
01844 #endif
01845 need_more_verify = findCheckmateInPV(verify_node_limit, king_in_threat);
01846 if (need_more_verify == PVStable
01847 || (i > 0 && need_more_verify == PVThreatmateNotRecord))
01848 break;
01849 if (this->isStableNow())
01850 this->setStable(i > 0 && king_in_threat[this->state().turn()] == false);
01851 }
01852 } catch (...) {
01853 last_iteration_consumed = this->elapsed() - total_consumed;
01854 total_consumed += last_iteration_consumed;
01855 this->updateCheckmateCount();
01856 this->recorder.finishSearch(search_move.move(), total_consumed,
01857 this->table->verboseLevel());
01858 throw;
01859 }
01860
01861 last_iteration_consumed = this->elapsed() - total_consumed;
01862 total_consumed += last_iteration_consumed;
01863 this->shared_root->best_move_for_iteration.push_back(last_best_move);
01864 this->shared_root->root_values_for_iteration.push_back
01865 (this->shared_root->root_values.back());
01866
01867 this->updateCheckmateCount();
01868 if (this->table->verboseLevel() > 2) {
01869 std::cerr << "<= "
01870 << record::csa::show(search_move.move());
01871 std::cerr << std::setprecision(4) << " mpn " << this->mpn.getAverage()
01872 << " cut " << this->mpn_cut.getAverage()
01873 << " alpha " << this->alpha_update.getAverage()
01874 << " last " << this->last_alpha_update.getAverage()
01875 << " ext " << 100.0*this->ext.getAverage() << "%"
01876 << " ext_limit " << this->ext_limit.getAverage()
01877 << " mem " << OslConfig::memoryUseRatio()*100.0;
01878 #ifdef OSL_SMP
01879 # ifdef SPLIT_STAT
01880 if (this->shared) {
01881 std::cerr << " split " << this->shared->parallel_splits << " cancel " << this->shared->cancelled_splits.value()
01882 << " abort " << this->shared->parallel_abort.value();
01883 }
01884 # endif
01885 #endif
01886 std::cerr << "\n";
01887 }
01888 bool time_over = false;
01889 if (this->hasSchedule()) {
01890 const double elapsed = this->elapsed();
01891 const double current_time_left = this->timeAssigned().standard.toSeconds() - elapsed;
01892 double coef = this->nextIterationCoefficient();
01893 if (! this->isStableNow())
01894 coef = std::min(0.5, coef);
01895 else {
01896 const int same_best_moves = this->shared_root->sameBestMoves();
01897 if (same_best_moves == 0) {
01898 if (this->table->verboseLevel() > 2 && coef > 0.75)
01899 std::cerr << "info: " << coef << " -> 0.75 by bestmove update\n";
01900 coef = std::min(0.75, coef);
01901 }
01902 else if (same_best_moves >= 3) {
01903 const Move last_move = this->lastMove();
01904 if (last_move.isNormal() && last_best_move.isNormal()
01905 && last_move.to() == last_best_move.to()
01906 && isMajor(last_best_move.capturePtype())
01907 && isMajorNonPieceOK(last_move.capturePtype())) {
01908 if (coef < 5.0 && this->table->verboseLevel() > 2)
01909 std::cerr << "info: " << coef << " -> 5.0 by takeback major piece\n";
01910 coef = std::max(5.0, coef);
01911 }
01912 }
01913 }
01914 if (current_time_left
01915 < last_iteration_consumed * coef)
01916 time_over = true;
01917 if (! time_over) {
01918 SimpleHashRecord *record
01919 = this->table->find(this->currentHash());
01920 if (record) {
01921 record->addNodeCount(this->nodeCount() - previous_node_count);
01922 }
01923 }
01924 }
01925 bool node_limit_over = (this->recorder.nodeCount() *4 > node_limit);
01926 this->recorder.finishSearch(search_move.move(),
01927 total_consumed,
01928 (time_over || node_limit_over) && this->table->verboseLevel());
01929 if (time_over || node_limit_over || this->stopping()) {
01930 if (this->table->isVerbose()) {
01931 const char *reason = "other reason";
01932 if (this->stopReason() == SearchTimerCommon::NoMoreMemory)
01933 reason = "memory full";
01934 else if (time_over || this->stopReason() == SearchTimerCommon::NoMoreTime)
01935 reason = "time";
01936 else if (node_limit_over)
01937 reason = "node count";
01938 else if (this->stopReason() == SearchTimerCommon::StopByOutside)
01939 reason = "outside";
01940 std::cerr << "iteration stop at " << limit_iterative << " by "
01941 << reason << "\n";
01942 }
01943 goto finish;
01944 }
01945 this->testStop();
01946 limit_iterative += step;
01947 }
01948 if (this->table->verboseLevel() > 1)
01949 std::cerr << "=> final iteration " << limit_iterative
01950 << " (" << last_iteration_consumed << ", " << total_consumed << " sec)"
01951 << " mem " << OslConfig::memoryUseRatio()*100.0 << "%\n";
01952 while (true) {
01953 this->recorder.startSearch(limit);
01954 try {
01955 for (int i=0; i<8; ++i)
01956 {
01957 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, limit+this->rootLimitBias()));
01958 this->shared_root->last_root_move = search_move.move();
01959 last_best_move = search_move.move();
01960 if (this->stopping())
01961 break;
01962 PVCheckmateStatus need_more_verify = PVStable;
01963 CArray<bool, 2> king_in_threat;
01964 int verify_node_limit = limit <= (1200 + this->rootLimitBias()) ? 10000 : 40000;
01965 if (this->timeAssigned().standard.toSeconds() < 20)
01966 verify_node_limit /= 4;
01967 #ifdef DONT_USE_CHECKMATE
01968 break;
01969 #endif
01970 need_more_verify = findCheckmateInPV(verify_node_limit, king_in_threat);
01971 if (need_more_verify == PVStable
01972 || (i > 0 && need_more_verify == PVThreatmateNotRecord))
01973 break;
01974 if (this->isStableNow())
01975 this->setStable(i > 0 && king_in_threat[this->state().turn()] == false);
01976 }
01977 } catch (...) {
01978 last_iteration_consumed = this->elapsed() - total_consumed;
01979 total_consumed += last_iteration_consumed;
01980 this->updateCheckmateCount();
01981 this->recorder.finishSearch(search_move.move(), total_consumed,
01982 this->table->verboseLevel());
01983 throw;
01984 }
01985 last_iteration_consumed = this->elapsed() - total_consumed;
01986 total_consumed += last_iteration_consumed;
01987 this->updateCheckmateCount();
01988 this->recorder.finishSearch(search_move.move(), total_consumed,
01989 this->table->verboseLevel());
01990 this->shared_root->best_move_for_iteration.push_back(last_best_move);
01991 this->shared_root->root_values_for_iteration.push_back
01992 (this->shared_root->root_values.back());
01993
01994 if (last_best_move.isNormal())
01995 break;
01996 this->testStop();
01997
01998
01999 if (limit >= 2000 || this->root_ignore_moves)
02000 break;
02001
02002 limit += 200;
02003 if (this->table->isVerbose())
02004 std::cerr << " extend limit to " << limit << " before resign\n";
02005 }
02006 }
02007 catch (std::exception& e)
02008 {
02009 if (! OslConfig::usiMode())
02010 std::cerr << "std exception " << e.what() << "\n";
02011 }
02012 catch (...)
02013 {
02014 std::cerr << "unknown exception\n";
02015 #ifndef NDEBUG
02016 throw;
02017 #endif
02018 }
02019 finish:
02020 if (this->table->verboseLevel() > 1) {
02021 std::cerr << "<= " << record::csa::show(last_best_move);
02022 std::cerr << std::setprecision(4) << " mpn " << this->mpn.getAverage()
02023 << " cut " << this->mpn_cut.getAverage()
02024 << " alpha " << this->alpha_update.getAverage()
02025 << " last " << this->last_alpha_update.getAverage()
02026 << " ext " << this->ext.getAverage()
02027 << " ext_limit " << this->ext_limit.getAverage()
02028 << " mem " << OslConfig::memoryUseRatio()*100.0;
02029 #ifdef OSL_SMP
02030 # ifdef SPLIT_STAT
02031 if (this->shared) {
02032 std::cerr << " split " << this->shared->parallel_splits << " cancel " << this->shared->cancelled_splits.value()
02033 << " abort " << this->shared->parallel_abort.value();
02034 }
02035 # endif
02036 #endif
02037 std::cerr << "\n";
02038 }
02039
02040 if (additional_info) {
02041 additional_info->node_count = this->nodeCount();
02042 additional_info->elapsed = this->elapsed();
02043 additional_info->moves.clear();
02044 additional_info->root_limit = this->root_limit;
02045 }
02046 if (additional_info && this->shared_root->root_values.size() > 1) {
02047 assert(last_best_move == this->shared_root->last_root_move);
02048 additional_info->move = last_best_move;
02049 const double scale = 200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN));
02050 additional_info->value = static_cast<int>(this->shared_root->last_root_value_update * scale);
02051 if (!this->shared_root->last_pv.empty()) {
02052 for (size_t i=1; i<this->shared_root->last_pv.back().pv.size(); ++i) {
02053 additional_info->moves.push_back(this->shared_root->last_pv.back().pv[i]);
02054 }
02055 }
02056 }
02057 #ifndef GPSONE
02058 {
02059 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02060 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02061 this->monitors())
02062 monitor->searchFinished();
02063 }
02064 #endif
02065 return last_best_move;
02066 }
02067
02068 template <class EvalT>
02069 template <osl::Player P>
02070 int osl::search::AlphaBeta2<EvalT>::
02071 alphaBetaSearchRoot(Window window, MoveLogProb& best_move, int limit)
02072 {
02073 #ifndef GPSONE
02074 {
02075 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02076 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02077 this->monitors())
02078 monitor->newDepth(limit/200);
02079 }
02080 #endif
02081 assert(P == this->state().turn());
02082 assert(window.alpha(P) % 2);
02083 assert(window.beta(P) % 2);
02084 setRoot(limit);
02085 assert(this->curDepth() == 0);
02086 this->node_type[this->curDepth()] = base_t::PvNode;
02087 this->checkmate_searcher->setRootPlayer(P);
02088 #ifdef OSL_SMP
02089 if (this->shared)
02090 this->shared->threadStart();
02091 #endif
02092
02093 SimpleHashRecord *record_in_table
02094 = this->table->allocate(this->currentHash(), limit);
02095 SimpleHashRecord *record = record_in_table;
02096 boost::scoped_ptr<SimpleHashRecord> record_if_not_allocated;
02097 if (! record)
02098 {
02099 record_if_not_allocated.reset(new SimpleHashRecord());
02100 record = record_if_not_allocated.get();
02101 }
02102 assert(record);
02103 this->setRootRecord(record);
02104 assert(this->rootRecord() == record);
02105 assert(this->hasLastRecord() && this->lastRecord() == record);
02106 record->setInCheck(this->state().inCheck());
02107
02108 if (limit == 0) {
02109 int result = this->template quiesce<P>(fullWindow(P));
02110 best_move = MoveLogProb(record->qrecord.bestMove(), 100);
02111 if (this->root_ignore_moves
02112 && this->root_ignore_moves->isMember(best_move.move()))
02113 best_move = MoveLogProb();
02114 #ifndef GPSONE
02115 else if (this->hasMonitor() && !this->prediction_for_speculative_search)
02116 {
02117 const double scale = OslConfig::usiOutputPawnValue()*2.0
02118 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02119 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02120 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02121 this->monitors())
02122 monitor->showPV(1, this->recorder.allNodeCount(),
02123 this->elapsed(), static_cast<int>(result*scale),
02124 best_move.move(), 0, 0, 0, 0);
02125 }
02126 #endif
02127 return result;
02128 }
02129 if (record_in_table) {
02130 int table_value = 0;
02131 const MoveLogProb m = record_in_table->bestMove();
02132 if (! m.isNormal())
02133 record_in_table->resetValue();
02134 else if (record->hasGreaterLowerBound<P>(this->curLimit(), window.beta(P),
02135 table_value)) {
02136 if (! this->root_ignore_moves
02137 || ! this->root_ignore_moves->isMember(m.move())) {
02138 best_move = m;
02139 return table_value;
02140 }
02141 }
02142 }
02143
02144
02145 MoveLogProbVector moves;
02146 MoveGenerator& generator = this->makeGenerator();
02147 const MoveLogProb last_best_move = record->bestMove();
02148 {
02149 MoveLogProbVector raw_moves;
02150 assert(this->curLimit() > 0);
02151 const Move hash_move = last_best_move.isNormal()
02152 ? last_best_move.move() : record->qrecord.bestMove();
02153 generator.init(this->curLimit()+200, record, this->eval, this->state(), true, hash_move);
02154 if (last_best_move.isNormal())
02155 raw_moves.push_back(last_best_move);
02156 else if (record->qrecord.bestMove().isNormal())
02157 raw_moves.push_back(MoveLogProb(record->qrecord.bestMove(), 100));
02158 generator.generateAll<P>(*this, raw_moves);
02159
02160
02161 for (size_t i=0; i<raw_moves.size(); ++i) {
02162 const Move m = raw_moves[i].move();
02163 if (i > 0 && m == hash_move)
02164 continue;
02165 const HashKey key = this->currentHash().newHashWithMove(m);
02166 const SimpleHashRecord *record = this->table->find(key);
02167 assert(this->state().isValidMove(m));
02168 if (record) {
02169 if (record->hasUpperBound(SearchTable::HistorySpecialDepth)
02170 && this->isWinValue(alt(P), record->upperBound()))
02171 continue;
02172 }
02173 if (this->root_ignore_moves && this->root_ignore_moves->isMember(m))
02174 continue;
02175 if (! m.isDrop() && m.ptype() != KING
02176 && move_classifier::KingOpenMove<P>::isMember(this->state(), m.ptype(), m.from(), m.to()))
02177 continue;
02178 if (move_classifier::MoveAdaptor<move_classifier::PawnDropCheckmate<P> >
02179 ::isMember(this->state(), m))
02180 continue;
02181 raw_moves[i].setLogProbAtMost(limit);
02182 moves.push_back(raw_moves[i]);
02183 }
02184 }
02185
02186 if (! OslConfig::searchExactValueInOneReply()) {
02187 if (moves.size() == 1
02188 || (moves.size() == 2 && moves[0].move() == moves[1].move()))
02189 {
02190 best_move = moves[0];
02191 #ifndef GPSONE
02192 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
02193 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02194 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02195 this->monitors())
02196 monitor->rootForcedMove(best_move.move());
02197 }
02198 #endif
02199 return 0;
02200 }
02201 }
02202
02203 #ifndef DONT_USE_CHECKMATE
02204
02205 int checkmate_node = 0;
02206 if (! this->prediction_for_speculative_search) {
02207 int checkmate_max = 30000*std::max(limit - 300 - this->rootLimitBias(), 0)/100;
02208 if (limit >= 1000 + this->rootLimitBias())
02209 checkmate_max = std::min(400000, 60000*(limit - 800 - this->rootLimitBias())/100);
02210 if (this->timeAssigned().standard.toSeconds() < 20) {
02211 checkmate_node /= 4;
02212 if (this->timeAssigned().standard.toSeconds() < 10)
02213 checkmate_node /= 2;
02214 }
02215 checkmate_node = record->qrecord.checkmateNodesLeft(checkmate_max);
02216 #ifdef CHECKMATE_COUNT
02217 std::cerr << "limit " << limit << " checkmate " << checkmate_node << "\n";
02218 #endif
02219 }
02220 if (checkmate_node > 0)
02221 {
02222 const bool my_king_in_check
02223 = this->state().hasEffectAt(alt(P),this->state().kingSquare(P));
02224 if (my_king_in_check)
02225 {
02226
02227 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node/8);
02228 const bool lose = this->template isLosingState<P>(checkmate_node/8);
02229 this->recorder.backFromCheckmateSearch();
02230 this->updateCheckmateCount();
02231 if (lose)
02232 {
02233 best_move = MoveLogProb(Move::INVALID(),100);
02234 this->recordLoseByCheckmate(P, record);
02235 this->shared_root->last_pv.clear();
02236 this->shared_root->last_root_move = Move();
02237 this->shared_root->last_root_value_update = this->winByCheckmate(alt(P));
02238 #ifndef GPSONE
02239 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02240 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02241 this->monitors())
02242 monitor->rootLossByCheckmate();
02243 #endif
02244 return this->winByCheckmate(alt(P));
02245 }
02246 }
02247
02248 {
02249 Move checkmate_move;
02250 #ifdef CHECKMATE_COUNT
02251 size_t count = this->checkmateSearcher().totalNodeCount();
02252 #endif
02253 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node);
02254 const bool win = this->template isWinningState<P>
02255 (checkmate_node, checkmate_move, limit >= 1000 + this->rootLimitBias());
02256 this->recorder.backFromCheckmateSearch();
02257 this->updateCheckmateCount();
02258 #ifdef CHECKMATE_COUNT
02259 root_checkmate += this->checkmateSearcher().totalNodeCount() - count;
02260 #endif
02261 if (win)
02262 {
02263 best_move = MoveLogProb(checkmate_move,100);
02264 this->recordWinByCheckmate(P, record, checkmate_move);
02265 this->shared_root->last_pv.clear();
02266 this->shared_root->last_root_move = checkmate_move;
02267 this->shared_root->last_root_value_update = this->winByCheckmate(P);
02268 this->pv[1].clear();
02269 this->updateRootPV(P, std::cerr, this->winByCheckmate(P), checkmate_move);
02270 return this->winByCheckmate(P);
02271 }
02272 }
02273
02274 if ((! my_king_in_check)
02275 && (! (record->threatmate().isThreatmate(P))))
02276 {
02277 Move threatmate_move;
02278 #ifdef CHECKMATE_COUNT
02279 size_t count = this->checkmateSearcher().totalNodeCount();
02280 #endif
02281 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node);
02282 const bool threatmate
02283 = this->template isThreatmateState<P>
02284 (checkmate_node, threatmate_move, limit >= 1000 + this->rootLimitBias());
02285 #ifdef CHECKMATE_COUNT
02286 root_checkmate += this->checkmateSearcher().totalNodeCount() - count;
02287 #endif
02288 this->recorder.backFromCheckmateSearch();
02289 this->updateCheckmateCount();
02290 if (threatmate)
02291 {
02292 if (record)
02293 record->threatmate().setThreatmate(P, threatmate_move);
02294 if (this->table->verboseLevel() > 1)
02295 std::cerr << " root threatmate " << threatmate_move << "\n";
02296 }
02297 BOOST_FOREACH(Ptype ptype, PieceStand::order)
02298 {
02299 this->testStop();
02300 if (! this->state().hasPieceOnStand(P, ptype))
02301 continue;
02302 NumEffectState state(this->state().emulateHandPiece(P, alt(P), ptype));
02303 state.setTurn(alt(P));
02304 Move hand_move;
02305 this->template isWinningState<PlayerTraits<P>::opponent>
02306 (*this->checkmate_searcher, state, HashKey(state), PathEncoding(alt(P)),
02307 checkmate_node, hand_move, Move::PASS(P), limit >= 1000 + this->rootLimitBias());
02308 }
02309 }
02310 this->testStop();
02311 }
02312 this->checkmate_searcher->runGC(this->table->isVerbose(),
02313 this->lastMemoryUseRatio1000());
02314 #endif
02315 const int ValueNone = window.alpha(P) - EvalTraits<P>::delta;
02316 int best_value = ValueNone;
02317 try {
02318
02319 size_t i=0;
02320 if (limit >= 1000 && ! moves.empty() && window == fullWindow(P))
02321 {
02322
02323 const int root_alpha =
02324 this->rootAlpha(P, this->shared_root->root_values.size() ? this->shared_root->root_values.back() : 0,
02325 this->eval.progress16());
02326 if (EvalTraits<P>::betterThan(root_alpha, window.alpha(P))) {
02327 const Window window_copy = window;
02328 window.alpha(P) = root_alpha;
02329 #ifndef GPSONE
02330 {
02331 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02332 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02333 this->monitors())
02334 monitor->rootFirstMove(moves[0].move());
02335 }
02336 #endif
02337 const int result = this->template alphaBetaSearch<P>(moves[0], window, true);
02338 if (EvalTraits<P>::betterThan(result, root_alpha))
02339 {
02340 window.alpha(P) = result + EvalTraits<P>::delta;
02341 best_move = moves[0];
02342 best_value = result;
02343 this->updateRootPV(P, std::cerr, result, moves[0].move());
02344 ++i;
02345 }
02346 else
02347 {
02348 if (this->table->isVerbose())
02349 this->showFailLow(result, moves[0].move());
02350 #ifndef GPSONE
02351 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
02352 const double scale = OslConfig::usiOutputPawnValue()*2.0
02353 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02354 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02355 this->monitors())
02356 monitor->showFailLow(this->root_limit/200, this->recorder.allNodeCount(),
02357 this->elapsed(),static_cast<int>(result*scale),
02358 moves[0].move());
02359 }
02360 #endif
02361 this->setStable(false);
02362 window = window_copy;
02363 }
02364 this->checkmate_searcher->runGC(this->table->isVerbose(),
02365 this->lastMemoryUseRatio1000());
02366 }
02367 }
02368 for (; i<moves.size() && best_value == ValueNone
02369 && window == fullWindow(P); ++i) {
02370 const MoveLogProb& m = moves[i];
02371 #ifndef GPSONE
02372 {
02373 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02374 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02375 this->monitors())
02376 monitor->rootMove(m.move());
02377 }
02378 #endif
02379 const int result = this->template alphaBetaSearch<P>(m, window, true);
02380 if (eval::betterThan(P, result, best_value)) {
02381 window.alpha(P) = result + EvalTraits<P>::delta;
02382 best_move = m;
02383 best_value = result;
02384 this->updateRootPV(P, std::cerr, result, m.move());
02385 if (eval::betterThan(P, result, window.beta(P))) {
02386 assert(! this->isWinValue(alt(P), result));
02387 }
02388 }
02389 else if (result == ValueNone)
02390 this->setStable(false);
02391 this->checkmate_searcher->runGC(this->table->isVerbose(),
02392 this->lastMemoryUseRatio1000());
02393 }
02394
02395 if (! eval::betterThan(P, window.alpha(P), window.beta(P))) {
02396 this->template examineMovesRoot<P>(moves, i, window, best_move, best_value);
02397 }
02398 if (best_move.isNormal()) {
02399 if (best_value != ValueNone) {
02400 assert(! this->shared_root->last_pv.empty());
02401 assert(best_move.move() == this->shared_root->last_pv.back().pv[0]);
02402 }
02403 }
02404 #ifndef GPSONE
02405 {
02406 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02407 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02408 this->monitors())
02409 monitor->depthFinishedNormally(limit/200);
02410 }
02411 #endif
02412 } catch (std::runtime_error& e) {
02413 if (this->table->isVerbose())
02414 std::cerr << e.what() << "\n";
02415 assert(best_value % 2 == 0);
02416 this->stopNow();
02417 this->restoreRootState();
02418 if (best_value != ValueNone)
02419 record->setLowerBound(P, this->curLimit(), best_move, best_value);
02420 if (best_move.validMove()
02421 && best_move.move() != last_best_move.move()) {
02422 if (this->table->verboseLevel() > 1) {
02423 std::cerr << "! use better move than the last best move\n";
02424 if (best_value != ValueNone) {
02425 assert(! this->shared_root->last_pv.empty() &&
02426 ! this->shared_root->last_pv.back().pv.empty());
02427 assert(best_move.move() == this->shared_root->last_pv.back().pv[0]);
02428 }
02429 }
02430 }
02431 else {
02432 #ifdef OSL_SMP
02433 if (this->shared)
02434 this->shared->waitAll();
02435 #endif
02436 throw;
02437 }
02438 }
02439
02440 assert(best_value % 2 == 0);
02441 if (best_value != ValueNone)
02442 record->setLowerBound(P, this->curLimit(), best_move, best_value);
02443 #ifdef OSL_SMP
02444 if (this->shared)
02445 this->shared->waitAll();
02446 #endif
02447 #ifndef GPSONE
02448 if (best_value == ValueNone
02449 && this->hasMonitor() && !this->prediction_for_speculative_search)
02450 {
02451 const double scale = OslConfig::usiOutputPawnValue()*2.0
02452 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02453 const int value = this->winByCheckmate(alt(P));
02454 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02455 this->monitors())
02456 monitor->showPV(limit/200, this->recorder.allNodeCount(),
02457 this->elapsed(), static_cast<int>(value*scale),
02458 Move::INVALID(), 0, 0, 0, 0);
02459 }
02460 #endif
02461 return best_value;
02462 }
02463
02464 template <class EvalT>
02465 void osl::search::AlphaBeta2<EvalT>::setRoot(int limit)
02466 {
02467 SearchState2::setRoot(limit);
02468 SimpleHashRecord *record = this->table->allocate(this->currentHash(), std::max(1000,limit));
02469 assert(record);
02470 this->setRootRecord(record);
02471 this->move_type[this->curDepth()] = base_t::INITIAL;
02472 }
02473
02474 template <class EvalT>
02475 void osl::search::AlphaBeta2<EvalT>::makeMove(Move move)
02476 {
02477 assert(this->state().isValidMove(move));
02478 SearchState2::makeMove(move);
02479 this->eval.update(this->state(), move);
02480
02481 SimpleHashRecord *record
02482 = this->table->allocate(this->currentHash(), this->curLimit());
02483 assert(record);
02484 this->move_type[this->curDepth()] = base_t::INITIAL;
02485 record->setInCheck(this->state().inCheck());
02486 this->setCurrentRecord(record);
02487 }
02488
02489 template <class EvalT>
02490 bool osl::search::AlphaBeta2<EvalT>::
02491 isReasonableMove(Move , int )
02492 {
02493 return true;
02494 }
02495
02496 template <class EvalT>
02497 void osl::search::AlphaBeta2<EvalT>::
02498 showNodeDepth(std::ostream& os)
02499 {
02500 #ifndef MINIMAL
02501 int max_depth=0;
02502 for (int i=base_t::MaxDepth-1; i>=0; --i) {
02503 if (base_t::depth_node_count[i] || base_t::depth_node_count_quiesce[i]) {
02504 max_depth = i;
02505 break;
02506 }
02507 }
02508 int max_count=0;
02509 for (int i=0; i<=max_depth; i+=2) {
02510 max_count = std::max(max_count,
02511 base_t::depth_node_count[i]+base_t::depth_node_count_quiesce[i]);
02512 }
02513
02514 int unit = std::max(max_count/79, 100);
02515 for (int i=0; i<=max_depth; i+=2) {
02516 os << std::setw(3) << i << " "
02517 << std::string(base_t::depth_node_count[i]/unit, '*')
02518 << std::string(base_t::depth_node_count_quiesce[i]/unit, '+')
02519 << std::endl;
02520 }
02521 # ifdef CHECKMATE_COUNT
02522 std::cerr << "checkmate root " << root_checkmate << " quiesce " << quiesce_checkmate
02523 << "\nnormal before " << checkmate_before
02524 << " after " << checkmate_after << " threatmate " << count_threatmate
02525 << "\n";
02526 # endif
02527 #endif
02528 }
02529
02530 template <class EvalT>
02531 void osl::search::AlphaBeta2<EvalT>::
02532 clearNodeDepth()
02533 {
02534 #ifndef MINIMAL
02535 base_t::depth_node_count.fill(0);
02536 base_t::depth_node_count_quiesce.fill(0);
02537 #endif
02538 }
02539
02540 namespace osl
02541 {
02542 namespace search
02543 {
02544 #ifndef MINIMAL
02545 template class AlphaBeta2<eval::ProgressEval>;
02546 template class AlphaBeta2Tree<eval::ProgressEval>;
02547 template
02548 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRoot<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02549 template
02550 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRoot<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02551 #endif
02552 template class AlphaBeta2<eval::ml::OpenMidEndingEval>;
02553 template class AlphaBeta2Tree<eval::ml::OpenMidEndingEval>;
02554 template
02555 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRoot<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02556 template
02557 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRoot<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02558 }
02559 }
02560
02561
02562
02563
02564
02565