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 bool already_split = false;
00720 # endif
00721 #endif
00722
00723
00724 MoveLogProb m = nextMove<P>();
00725 ++tried_moves;
00726 if (! m.validMove() || m.logProb() > curLimit()) {
00727 goto move_generation_failure;
00728 }
00729 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00730 int first_move_node;
00731 #endif
00732 {
00733 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00734 const int previous_node_count = nodeCount();
00735 #endif
00736 assert(eval::betterThan(P, w.alpha(P), best_value));
00737 const int result = alphaBetaSearch<P>(m, w, in_pv);
00738 if (eval::betterThan(P, result, best_value)) {
00739 best_value = result;
00740 best_move = m;
00741 if (eval::betterThan(P, best_value, w.alpha(P))) {
00742 w.alpha(P) = result + EvalTraits<P>::delta;;
00743 assert(best_move.validMove());
00744 ++alpha_update;
00745 last_alpha_update = 1;
00746 if (eval::betterThan(P, result, w.beta(P))) {
00747 mpn_cut.add(tried_moves);
00748 if (this->move_type[this->curDepth()] >= common_t::ALL) {
00749 setKillerMove(best_move.move());
00750 if (best_move.isNormal()
00751 && ! best_move.move().isCapture())
00752 {
00753 const int d = (curLimit()+200)/100;
00754 this->historyTable().add(best_move.move(), d*d);
00755 }
00756 }
00757 assert(best_move.validMove());
00758 assert(! this->isWinValue(alt(P), best_value));
00759 goto register_table;
00760 } else {
00761 if (in_pv)
00762 makePV(m.move());
00763 }
00764 }
00765 }
00766 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00767 first_move_node = nodeCount() - previous_node_count;
00768 #endif
00769 }
00770 testStop();
00771
00772 #ifndef DONT_USE_CHECKMATE
00773 if (in_pv)
00774 {
00775 Move checkmate_move;
00776 if (tryCheckmate<P>(record, in_pv, checkmate_move)) {
00777 assert(checkmate_move.isValid());
00778 best_value= this->winByCheckmate(P);
00779 this->recordWinByCheckmate(P, record, checkmate_move);
00780 return best_value;
00781 }
00782 }
00783 #endif
00784 search_assert(w.isConsistent(), lastMove());
00785 if (curLimit() < this->leafLimit())
00786 goto move_generation_failure;
00787 while (true) {
00788 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00789 const bool prefer_split = shared && curLimit() >= shared->split_min_limit
00790 && (curLimit() >= 600+this->rootLimitBias()
00791
00792 || (first_move_node >= 30000));
00793 try {
00794 if (prefer_split) {
00795 int cur_smp_idle;
00796 {
00797 # ifdef OSL_USE_RACE_DETECTOR
00798 boost::mutex::scoped_lock lk(shared->lock_smp);
00799 #endif
00800 cur_smp_idle = shared->smp_idle;
00801 }
00802 if (cur_smp_idle > last_smp_idle) {
00803 last_smp_idle = cur_smp_idle;
00804 assert(! already_split);
00805 already_split = true;
00806 if (examineMovesOther<P>(w, best_move, best_value, tried_moves,
00807 alpha_update, last_alpha_update)) {
00808 assert(best_move.validMove());
00809 assert(best_move.player() == P);
00810 if (this->move_type[this->curDepth()] >= common_t::ALL) {
00811 setKillerMove(best_move.move());
00812 if (best_move.isNormal()
00813 && ! best_move.move().isCapture())
00814 {
00815 const int d = (curLimit()+200)/100;
00816 this->historyTable().add(best_move.move(), d*d);
00817 }
00818 }
00819 mpn_cut.add(tried_moves);
00820 goto register_table;
00821 }
00822 goto all_moves_done;
00823 }
00824 }
00825 }
00826 catch(AlphaBeta2ParallelCommon::SplitFailed&) {
00827 already_split = false;
00828
00829 }
00830 #endif
00831 MoveLogProb m = nextMove<P>();
00832 if (! m.validMove())
00833 break;
00834 ++tried_moves;
00835
00836 assert(eval::betterThan(P, w.alpha(P), best_value));
00837 const int result = alphaBetaSearch<P>(m, w, in_pv && ! best_move.validMove());
00838 if (eval::betterThan(P, result, best_value)) {
00839 best_value = result;
00840 best_move = m;
00841 if (eval::betterThan(P, best_value, w.alpha(P))) {
00842 w.alpha(P) = result + EvalTraits<P>::delta;;
00843 assert(best_move.validMove());
00844 ++alpha_update;
00845 last_alpha_update = tried_moves;
00846 if (eval::betterThan(P, result, w.beta(P))) {
00847 assert(best_move.validMove());
00848 if (this->move_type[this->curDepth()] >= common_t::ALL) {
00849 setKillerMove(best_move.move());
00850 if (best_move.isNormal()
00851 && ! best_move.move().isCapture())
00852 {
00853 const int d = (curLimit()+200)/100;
00854 this->historyTable().add(best_move.move(), d*d);
00855 }
00856 }
00857 mpn_cut.add(tried_moves);
00858 goto register_table;
00859 } else {
00860 if (in_pv)
00861 makePV(m.move());
00862 }
00863 }
00864 }
00865 }
00866 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_INTERNAL)
00867 all_moves_done:
00868 #endif
00869 if (tried_moves == 1 && tryPass(record, P)) {
00870
00871 goto move_generation_failure;
00872 }
00873 mpn.add(tried_moves);
00874 if (alpha_update) {
00875 this->alpha_update.add(alpha_update);
00876 this->last_alpha_update.add(last_alpha_update);
00877 }
00878
00879 if (((this->curDepth() % 2) == 0 || OslConfig::usiMode())
00880 && EnterKing::canDeclareWin<P>(state())) {
00881 best_value = this->brinkmatePenalty(alt(P), std::max(1,16-this->curDepth())*256)
00882 + this->eval.value();
00883 record->setAbsoluteValue(Move::DeclareWin(), best_value,
00884 SearchTable::CheckmateSpecialDepth);
00885 return best_value;
00886 }
00887 if (record) {
00888
00889 #ifndef DONT_USE_CHECKMATE
00890 Move checkmate_move=Move::INVALID();
00891 if (tryCheckmateAgain<P>(record, checkmate_move,
00892 nodeCount() - node_count_at_beginning,
00893 best_value)) {
00894 assert(checkmate_move.isValid());
00895 best_value= this->winByCheckmate(P);
00896 this->recordWinByCheckmate(P, record, checkmate_move);
00897 return best_value;
00898 }
00899 #endif
00900 }
00901 register_table:
00902 assert(best_value == this->minusInfty(P) || best_move.validMove());
00903 assert(eval::isConsistentValue(best_value));
00904 if (this->isWinValue(alt(P), best_value))
00905 {
00906
00907
00908 best_value = this->brinkmatePenalty(P, std::max(1,16-this->curDepth())*256) + this->eval.value();
00909
00910
00911 record->setAbsoluteValue(best_move, best_value, curLimit());
00912 return best_value;
00913 }
00914 else if (EvalTraits<P>::betterThan(w.alpha(P), initial_alpha)) {
00915 if (best_move.validMove()) {
00916 assert(best_value % 2 == 0);
00917 record->setLowerBound(P, curLimit(), best_move, best_value);
00918 }
00919 }
00920 if (EvalTraits<P>::betterThan(w.beta(P), best_value)) {
00921 if (best_move.validMove())
00922 record->setUpperBound(P, curLimit(), best_move, best_value);
00923 }
00924 return best_value;
00925 move_generation_failure:
00926 pv[this->curDepth()].clear();
00927
00928
00929 best_value = quiesce<P>(w);
00930 if (record)
00931 {
00932 if (EvalTraits<P>::betterThan(best_value, initial_alpha)) {
00933 if (EvalTraits<P>::betterThan(w.beta(P), best_value)) {
00934 record->setAbsoluteValue(MoveLogProb(), best_value, curLimit());
00935 } else {
00936 record->setLowerBound(P, curLimit(), MoveLogProb(), best_value);
00937 }
00938 }
00939 else
00940 {
00941 assert(EvalTraits<P>::betterThan(w.beta(P), best_value));
00942 record->setUpperBound(P, curLimit(), MoveLogProb(), best_value);
00943 }
00944 }
00945 assert(eval::isConsistentValue(best_value));
00946 return best_value;
00947 }
00948
00949 template <class EvalT>
00950 template <osl::Player P>
00951 int osl::search::AlphaBeta2Tree<EvalT>::
00952 quiesce(Window w)
00953 {
00954 #ifdef EXPERIMENTAL_QUIESCE
00955 return quiesceExp<P>(w);
00956 #else
00957 return quiesceStable<P>(w);
00958 #endif
00959 }
00960
00961 template <class EvalT>
00962 template <osl::Player P>
00963 int osl::search::AlphaBeta2Tree<EvalT>::
00964 quiesceStable(Window w)
00965 {
00966 testStop();
00967 initPV();
00968
00969 typedef QuiescenceSearch2<eval_t> qsearcher_t;
00970 qsearcher_t qs(*this, *this->table);
00971 Move last_move = lastMove();
00972 if (last_move.isInvalid())
00973 last_move = Move::PASS(alt(P));
00974 assert(w.alpha(P) % 2);
00975 assert(w.beta(P) % 2);
00976 #ifdef CHECKMATE_COUNT
00977 size_t count = checkmateSearcher().totalNodeCount();
00978 #endif
00979 const int result = qs.template search<P>(w.alpha(P), w.beta(P), this->eval, last_move, 4);
00980 node_count += qs.nodeCount();
00981 this->recorder.addQuiescenceCount(qs.nodeCount());
00982 #ifdef CHECKMATE_COUNT
00983 quiesce_checkmate += checkmateSearcher().totalNodeCount() - count;
00984 #endif
00985
00986 assert(result % 2 == 0);
00987 return result;
00988 }
00989
00990 template <class EvalT>
00991 template <osl::Player P>
00992 int osl::search::AlphaBeta2Tree<EvalT>::
00993 quiesceExp(Window w)
00994 {
00995 testStop();
00996
00997 SimpleHashRecord *record = lastRecord();
00998 assert(record);
00999 Move best_move;
01000 const int qdepth = 4;
01001 const int previous_node_count = nodeCount();
01002
01003 int result =
01004 quiesceRoot<P>(w, qdepth, best_move, record->threatmate());
01005
01006 const size_t qnode = nodeCount() - previous_node_count;
01007 this->recorder.addQuiescenceCount(qnode);
01008 record->qrecord.setLowerBound(qdepth, result, best_move);
01009 return result;
01010 }
01011
01012 template <class EvalT>
01013 template <osl::Player P>
01014 struct osl::search::AlphaBeta2Tree<EvalT>::NextQMove
01015 {
01016 AlphaBeta2Tree *searcher;
01017 Window window;
01018 const int depth;
01019 int *result;
01020 DualThreatmateState threatmate;
01021 NextQMove(AlphaBeta2Tree *s, Window w, int d, int *r,
01022 DualThreatmateState t)
01023 : searcher(s), window(w), depth(d), result(r), threatmate(t) {
01024 }
01025 void operator()(Square ) {
01026 searcher->eval.update(searcher->state(), searcher->lastMove());
01027 *result =
01028 searcher->quiesce<P>(window, depth, threatmate);
01029 }
01030 };
01031
01032 template <class EvalT>
01033 template <osl::Player P>
01034 bool osl::search::AlphaBeta2Tree<EvalT>::
01035 quiesceWithMove(Move move, Window& w, int depth_left, Move& best_move, int& best_value,
01036 const DualThreatmateState& threatmate)
01037 {
01038
01039 const bool in_pv = ! w.null();
01040 int result;
01041 typedef NextQMove<PlayerTraits<P>::opponent> next_t;
01042 next_t helper(this, w, depth_left, &result, threatmate);
01043
01044 const HashKey new_hash = currentHash().newHashWithMove(move);
01045 const eval_t old_eval = this->eval;
01046 doUndoMoveOrPass<P,next_t>(new_hash, move, helper);
01047 this->eval = old_eval;
01048
01049 if (eval::betterThan(P, result, best_value)) {
01050 best_value = result;
01051 best_move = move;
01052 if (eval::betterThan(P, best_value, w.alpha(P))) {
01053 w.alpha(P) = result + EvalTraits<P>::delta;
01054 if (in_pv)
01055 makePV(best_move);
01056 if (eval::betterThan(P, result, w.beta(P))) {
01057 return true;
01058 }
01059 }
01060 }
01061 return false;
01062 }
01063
01064 template <class EvalT>
01065 template <osl::Player P>
01066 int osl::search::AlphaBeta2Tree<EvalT>::
01067 quiesceRoot(Window w, int depth_left, Move& best_move, DualThreatmateState threatmate)
01068 {
01069 assert(! state().inCheck(alt(P)));
01070
01071 initPV();
01072
01073
01074
01075 SimpleHashRecord& record = *lastRecord();
01076 assert(record.inCheck() == state().inCheck());
01077 assert(depth_left > 0);
01078
01079 int best_value = this->minusInfty(P);
01080
01081 if (! record.inCheck()) {
01082 if (! threatmate.maybeThreatmate(P)) {
01083 best_value = this->eval.value();
01084 } else {
01085 const int value = this->eval.value() + this->threatmatePenalty(P);
01086 best_value = EvalTraits<P>::max(best_value, value);
01087 }
01088 best_move = Move::PASS(P);
01089 if (EvalTraits<P>::betterThan(best_value, w.alpha(P))) {
01090 if (EvalTraits<P>::betterThan(best_value, w.beta(P)))
01091 return best_value;
01092 w.alpha(P) = best_value + EvalTraits<P>::delta;
01093 }
01094 }
01095
01096 Move prev_best = record.qrecord.bestMove();
01097 MoveGenerator& generator = makeGenerator();
01098 generator.init(200, &record, this->eval, state(),
01099 w.alpha(P) == w.beta(P),
01100 prev_best, true);
01101 int tried_moves = 0;
01102
01103 if (prev_best.isNormal()) {
01104 ++tried_moves;
01105 if (quiesceWithMove<P>(prev_best, w, depth_left-1, best_move, best_value,
01106 threatmate))
01107 goto finish;
01108 }
01109
01110
01111
01112 for (MoveLogProb m = generator.nextTacticalMove<P>(*this);
01113 m.validMove(); m = generator.nextTacticalMove<P>(*this)) {
01114 ++tried_moves;
01115 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01116 threatmate))
01117 goto finish;
01118 }
01119 for (MoveLogProb m = generator.nextMove<P>(*this);
01120 m.validMove(); m = generator.nextMove<P>(*this)) {
01121 ++tried_moves;
01122 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01123 threatmate))
01124 goto finish;
01125 }
01126
01127
01128 if (record.inCheck()) {
01129 if (tried_moves == 0) {
01130 if (lastMove().isNormal() && lastMove().ptype() == PAWN && lastMove().isDrop())
01131 return this->winByFoul(P);
01132 return this->winByCheckmate(alt(P));
01133 }
01134 goto finish;
01135 }
01136 finish:
01137 return best_value;
01138 }
01139
01140 template <class EvalT>
01141 template <osl::Player P>
01142 int osl::search::AlphaBeta2Tree<EvalT>::
01143 quiesce(Window w, int depth_left, DualThreatmateState parent_threatmate)
01144 {
01145 if (state().inCheck(alt(P))) {
01146 return this->minusInfty(alt(P));
01147 }
01148
01149 initPV();
01150 #ifndef MINIMAL
01151 depth_node_count_quiesce[this->curDepth()]++;
01152 #endif
01153 ++node_count;
01154
01155 SimpleHashRecord record;
01156 record.setInCheck(state().inCheck());
01157
01158 DualThreatmateState threatmate;
01159 threatmate.updateInLock(P, &parent_threatmate, record.inCheck());
01160
01161 int best_value = this->minusInfty(P);
01162
01163 if (depth_left <= 0) {
01164 if (record.inCheck()) {
01165 if (lastMove().isCapture())
01166 depth_left +=2;
01167 else
01168 depth_left = 0;
01169 }
01170 else if (threatmate.maybeThreatmate(P)) {
01171 if (threatmate.mayHaveCheckmate(alt(P))) {
01172 Move checkmate_move;
01173 bool win = isWinningState<P>(10, checkmate_move);
01174 if (win)
01175 return this->winByCheckmate(P);
01176 }
01177 return this->eval.value() + this->threatmatePenalty(P);
01178 }
01179 else {
01180 if (threatmate.mayHaveCheckmate(alt(P)))
01181 return this->eval.value() + this->threatmatePenalty(alt(P));
01182 if (ImmediateCheckmate::hasCheckmateMove<P>(state()))
01183 return this->winByCheckmate(P);
01184 if (ImmediateCheckmate::hasCheckmateMove<PlayerTraits<P>::opponent>(state()))
01185 return this->eval.value() + this->threatmatePenalty(P);
01186 return this->eval.value();
01187 }
01188 }
01189
01190 if (! record.inCheck()) {
01191 if (ImmediateCheckmate::hasCheckmateMove<P>(state())) {
01192 return this->winByCheckmate(P);
01193 }
01194 }
01195 if (threatmate.mayHaveCheckmate(alt(P))) {
01196 Move checkmate_move;
01197 bool win = isWinningState<P>(10, checkmate_move);
01198 if (win)
01199 return this->winByCheckmate(P);
01200 }
01201 MoveGenerator& generator = makeGenerator();
01202 generator.init(200, &record, this->eval, state(),
01203 w.alpha(P) == w.beta(P),
01204 Move(), true);
01205 int tried_moves = 0;
01206 Move best_move;
01207
01208 if (! record.inCheck()) {
01209 if (! threatmate.maybeThreatmate(P)) {
01210 best_value = this->eval.value();
01211 } else {
01212 const int value = this->eval.value() + this->threatmatePenalty(P);
01213 best_value = EvalTraits<P>::max(best_value, value);
01214 }
01215 best_move = Move::PASS(P);
01216 if (EvalTraits<P>::betterThan(best_value, w.alpha(P))) {
01217 if (EvalTraits<P>::betterThan(best_value, w.beta(P)))
01218 return best_value;
01219 w.alpha(P) = best_value + EvalTraits<P>::delta;
01220 }
01221 }
01222
01223
01224 for (MoveLogProb m = generator.nextTacticalMove<P>(*this);
01225 m.validMove(); m = generator.nextTacticalMove<P>(*this)) {
01226 ++tried_moves;
01227 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01228 threatmate))
01229 goto finish;
01230 }
01231 for (MoveLogProb m = generator.nextMove<P>(*this);
01232 m.validMove(); m = generator.nextMove<P>(*this)) {
01233 ++tried_moves;
01234 if (quiesceWithMove<P>(m.move(), w, depth_left-1, best_move, best_value,
01235 threatmate))
01236 goto finish;
01237 }
01238
01239
01240 if (record.inCheck()) {
01241 if (tried_moves == 0) {
01242 if (lastMove().ptype() == PAWN && lastMove().isDrop())
01243 return this->winByFoul(P);
01244 return this->winByCheckmate(alt(P));
01245 }
01246 goto finish;
01247 }
01248 finish:
01249 return best_value;
01250 }
01251
01252 template <class EvalT>
01253 void osl::search::AlphaBeta2Tree<EvalT>::updateCheckmateCount()
01254 {
01255 #ifdef OSL_SMP
01256 if (shared) {
01257 this->recorder.setCheckmateCount(shared->checkmateCount());
01258 return;
01259 }
01260 #endif
01261 this->recorder.setCheckmateCount
01262 (checkmateSearcher().totalNodeCount());
01263 }
01264
01265 template <class EvalT>
01266 int
01267 osl::search::AlphaBeta2Tree<EvalT>::
01268 rootAlpha(Player P, int last_value, Progress16 progress)
01269 {
01270 int pawns = 3;
01271 if (eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(alt(P),KING))))
01272 {
01273 pawns = 10;
01274 }
01275 else if (progress.value() <= 1)
01276 {
01277 pawns = 3;
01278 }
01279 else if (progress.value() <= 7)
01280 {
01281 pawns = 4;
01282 }
01283 else if (progress.value() <= 9)
01284 {
01285 pawns = 5;
01286 }
01287 else if (progress.value() <= 10)
01288 {
01289 pawns = 6;
01290 }
01291 else
01292 {
01293 pawns = 7;
01294 }
01295 const int width = eval_t::captureValue(newPtypeO(alt(P),PAWN))*pawns/2;
01296 return last_value - width - eval::delta(P);
01297 }
01298
01299 template <class EvalT>
01300 int
01301 osl::search::AlphaBeta2Tree<EvalT>::
01302 stableThreshold(Player P, int last_value)
01303 {
01304 int pawns = 3;
01305 if (eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(alt(P),KING))))
01306 pawns = 10;
01307 else if (eval::betterThan(P, eval_t::captureValue(newPtypeO(alt(P),PAWN))*2, last_value)
01308 && eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(P,PAWN))*2))
01309 pawns = 2;
01310 const int width = eval_t::captureValue(newPtypeO(alt(P),PAWN))*pawns/2;
01311 return last_value - width - eval::delta(P);
01312 }
01313
01314 template <class EvalT>
01315 void osl::search::AlphaBeta2Tree<EvalT>::
01316 updateRootPV(Player P, std::ostream& os, int result, Move m)
01317 {
01318 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01319 this->makePV(m);
01320 const int last_root_value = shared_root->root_values_for_iteration.size() ? shared_root->root_values_for_iteration.back() : 0;
01321 const int threshold = stableThreshold(P, last_root_value);
01322 bool new_stable = eval::betterThan(P, result, threshold);
01323 shared_root->last_root_value_update = result;
01324
01325 if (new_stable && m != shared_root->last_root_move
01326 && (See::see(state(), m) < -eval::Ptype_Eval_Table.value(KNIGHT)*2
01327 || eval::betterThan(P, result, eval_t::captureValue(newPtypeO(alt(P),KING))))) {
01328 new_stable = false;
01329 }
01330 if (new_stable && shared_root->root_values_for_iteration.size() > 1) {
01331 const int last_root_value2 = shared_root->root_values_for_iteration[shared_root->root_values_for_iteration.size()-2];
01332 const int threshold2 = stableThreshold(P, last_root_value2);
01333 if (eval::betterThan(P, threshold2, result)
01334 && eval::betterThan(P, last_root_value2, last_root_value))
01335 new_stable = false;
01336 }
01337 this->shared_root->last_pv.push_back(RootPV(root_limit, pv[0], result));
01338 this->setStable(new_stable);
01339 #ifndef GPSONE
01340 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
01341 const double scale = OslConfig::usiOutputPawnValue()*2.0
01342 / eval_t::captureValue(newPtypeO(alt(P),PAWN));
01343 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01344 this->monitors())
01345 monitor->showPV(root_limit/200, this->recorder.allNodeCount(),
01346 this->elapsed(), static_cast<int>(result*scale),
01347 m, &*pv[0].begin(), &*pv[0].end());
01348 }
01349 #endif
01350 if (this->table->isVerbose()) {
01351 showPV(os, result, m, new_stable ? ' ' : '*');
01352 }
01353 }
01354
01355 template <class EvalT>
01356 void osl::search::AlphaBeta2Tree<EvalT>::
01357 addMultiPV(Player P, int result, Move m)
01358 {
01359 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01360 this->makePV(m);
01361 this->shared_root->last_pv.push_back(RootPV(root_limit, pv[0], result));
01362 std::swap(*this->shared_root->last_pv.rbegin(), *(this->shared_root->last_pv.rbegin()+1));
01363
01364 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
01365 const double scale = OslConfig::usiOutputPawnValue()*2.0
01366 / eval_t::captureValue(newPtypeO(alt(P),PAWN));
01367 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01368 this->monitors())
01369 monitor->showPV(root_limit/200, this->recorder.allNodeCount(),
01370 this->elapsed(), static_cast<int>(result*scale),
01371 m, &*pv[0].begin(), &*pv[0].end());
01372 }
01373
01374 if (this->table->isVerbose()) {
01375 showPV(std::cerr, result, m, '&');
01376 }
01377 }
01378
01379 template <class EvalT>
01380 void osl::search::AlphaBeta2Tree<EvalT>::
01381 showFailLow(int result, Move m) const
01382 {
01383 if (this->root_ignore_moves)
01384 std::cerr << "[" << this->root_ignore_moves->size() << "] ";
01385 std::cerr << " <" << std::setfill(' ') << std::setw(5)
01386 << static_cast<int>(result*200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN)))
01387 << " " << record::csa::show(m) << "\n";
01388 }
01389
01390 template <class EvalT>
01391 void osl::search::AlphaBeta2Tree<EvalT>::
01392 showPV(std::ostream& os, int result, Move m, char stable_char) const
01393 {
01394 assert(m.isNormal());
01395 if (this->root_ignore_moves)
01396 os << "[" << this->root_ignore_moves->size() << "] ";
01397 os << stable_char;
01398 os << " " << std::setfill(' ') << std::setw(5)
01399 << static_cast<int>(result*200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN))) << " ";
01400 BOOST_FOREACH(Move m, pv[0]) {
01401 os << record::csa::show(m);
01402 }
01403 const double elapsed = this->elapsed();
01404 if (elapsed > 1.0)
01405 os << " (" << elapsed << "s, " << OslConfig::memoryUseRatio()*100.0 << "%)";
01406 os << std::endl;
01407 #ifndef MINIMAL
01408 #ifndef _WIN32
01409 if (! OslConfig::usiMode())
01410 {
01411 NumEffectState state = this->state();
01412 std::string str; str.reserve(200); str = " ";
01413 for (size_t i=0; i<pv[0].size(); ++i) {
01414 str += record::ki2::show(pv[0][i], state, i ? pv[0][i-1] : Move());
01415 state.makeMove(pv[0][i]);
01416
01417
01418 const SimpleHashRecord *record
01419 = this->table->find(HashKey(state));
01420 if (record &&
01421 record->threatmate().isThreatmate(state.turn()))
01422 str += "(" + record::K_TSUMERO + ")";
01423 }
01424 std::string converted = IconvConvert::eucToLang(str);
01425 if (! converted.empty())
01426 os << converted << std::endl;
01427 }
01428 #endif
01429 #endif
01430
01431 #ifdef DEBUG_PV
01432 NumEffectState s = state();
01433 for (size_t i=0; i<pv[0].size(); ++i) {
01434 if (! pv[0][i].isPass() && ! s.isValidMove(pv[0][i])) {
01435 std::cerr << "root pv error " << pv[0][i] << " " << i << "\n";
01436 break;
01437 }
01438 ApplyMoveOfTurn::doMove(s, pv[0][i]);
01439 }
01440 #endif
01441 }
01442
01443 template <class EvalT>
01444 template <osl::Player P>
01445 struct osl::search::AlphaBeta2Tree<EvalT>::NextMove
01446 {
01447 AlphaBeta2Tree *searcher;
01448 const MoveLogProb& moved;
01449 Window window;
01450 int *result;
01451 bool in_pv;
01452 NextMove(AlphaBeta2Tree *s, const MoveLogProb& md, Window w, int *r,
01453 bool p)
01454 : searcher(s), moved(md), window(w), result(r), in_pv(p) {
01455 assert(P == md.player());
01456 }
01457 void operator()(Square ) {
01458 #ifndef NDEBUG
01459 const int cur_limit = searcher->curLimit();
01460 #endif
01461 *result =
01462 searcher->alphaBetaSearchAfterMove<P>(moved, window, in_pv);
01463 assert(cur_limit == searcher->curLimit() || searcher->SearchState2Core::abort());
01464 }
01465 };
01466
01467 template <class EvalT>
01468 template <osl::Player P>
01469 int osl::search::AlphaBeta2Tree<EvalT>::
01470 alphaBetaSearch(const MoveLogProb& search_move, Window w, bool in_pv)
01471 {
01472 assert(w.alpha(P) % 2);
01473 assert(w.beta(P) % 2);
01474 const Move move = search_move.move();
01475 assert(P == move.player());
01476 assert(P == state().turn());
01477 assert(eval::notLessThan(P, w.beta(P), w.alpha(P)));
01478
01479 testStop();
01480 pv[curDepth()+1].clear();
01481
01482 if (! move.isPass() ){
01483 if(MoveStackRejections::probe<P>(state(),history(),curDepth(),move,w.alpha(P),repetitionCounter().checkCount(alt(P)))){
01484 return this->winByLoop(alt(P));
01485 }
01486 if (move_classifier::MoveAdaptor<move_classifier::PawnDropCheckmate<P> >
01487 ::isMember(state(), move))
01488 return this->winByFoul(alt(P));
01489 }
01490
01491 const HashKey new_hash = currentHash().newHashWithMove(move);
01492 assert(P == move.player());
01493
01494 if (move.isPass())
01495 this->pass_count.inc(P);
01496
01497
01498 if (! this->pass_count.loopByBothPass()) {
01499 const Sennichite next_sennichite
01500 = repetition_counter.isAlmostSennichite(new_hash);
01501 if (next_sennichite.isDraw())
01502 return this->drawValue();
01503 if (next_sennichite.hasWinner())
01504 return this->winByFoul(next_sennichite.winner());
01505 assert(next_sennichite.isNormal());
01506 }
01507
01508 if (! move.isPass()) {
01509
01510 const DominanceCheck::Result has_dominance
01511 = DominanceCheck::detect(repetition_counter.history(), new_hash);
01512 if (has_dominance == DominanceCheck::LOSE)
01513 return this->winByLoop(alt(P));
01514 if (has_dominance == DominanceCheck::WIN)
01515 return this->winByLoop(P);
01516
01517 if (! move.isCapture()) {
01518 const int sacrifice_count = countSacrificeCheck2(this->curDepth());
01519 if (sacrifice_count == 2) {
01520
01521 const Square to = move.to();
01522 int offence = state().countEffect(P, to) + (move.isDrop() ? 1 : 0);
01523 const int deffense = state().hasEffectAt(alt(P), to);
01524 if (offence <= deffense)
01525 offence += AdditionalEffect::count2(state(), to, P);
01526 if (offence <= deffense) {
01527 return this->winByLoop(alt(P));
01528 }
01529 }
01530 }
01531 }
01532
01533 int result;
01534 NextMove<P> helper(this, search_move, w, &result, in_pv);
01535
01536 this->recorder.addNodeCount();
01537 const eval_t old_eval = this->eval;
01538 doUndoMoveOrPass<P,NextMove<P> >(new_hash, move, helper);
01539 this->eval = old_eval;
01540 if (move.isPass())
01541 this->pass_count.dec(P);
01542
01543 return result;
01544 }
01545
01546 template <class EvalT>
01547 template <osl::Player P>
01548 void osl::search::AlphaBeta2Tree<EvalT>::
01549 examineMovesRoot(const MoveLogProbVector& moves, size_t i, Window window,
01550 MoveLogProb& best_move, int& best_value)
01551 {
01552 for (;i<moves.size(); ++i) {
01553 testStop();
01554
01555 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_ROOT)
01556 if (shared && i > 8
01557 && moves.size() > i+1) {
01558 int smp_idle;
01559 {
01560 # ifdef OSL_USE_RACE_DETECTOR
01561 boost::mutex::scoped_lock lk(shared->lock_smp);
01562 # endif
01563 smp_idle = shared->smp_idle;
01564 }
01565 if (smp_idle) {
01566 try {
01567 examineMovesRootPar<P>(moves, i, window, best_move, best_value);
01568 break;
01569 } catch (AlphaBeta2ParallelCommon::SplitFailed&) {
01570 }
01571 }
01572 }
01573 #endif
01574
01575 const MoveLogProb& m = moves[i];
01576 #ifndef GPSONE
01577 if (this->elapsed() > 1.0)
01578 {
01579 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01580 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01581 this->monitors())
01582 monitor->rootMove(m.move());
01583 }
01584 if (this->multi_pv) {
01585 int width = this->multi_pv*this->eval.captureValue(newPtypeO(P, PAWN))/200;
01586 if (width % 2 == 0)
01587 width -= EvalTraits<P>::delta;
01588 window.alpha(P) = best_value + width;
01589 }
01590 #endif
01591 const int result = alphaBetaSearch<P>(m, window, false);
01592 if (eval::betterThan(P, result, best_value))
01593 {
01594 window.alpha(P) = result + EvalTraits<P>::delta;
01595 best_move = m;
01596 best_value = result;
01597 updateRootPV(P, std::cerr, result, m.move());
01598 if (eval::betterThan(P, result, window.beta(P))) {
01599 assert(! this->isWinValue(alt(P), result));
01600 break;
01601 }
01602 }
01603 #ifndef GPSONE
01604 else if (this->multi_pv && eval::betterThan(P, result, window.alpha(P)))
01605 {
01606 addMultiPV(P, result, m.move());
01607 }
01608 #endif
01609 if (this->root_limit >= 1600)
01610 this->checkmate_searcher->runGC(this->table->isVerbose(),
01611 lastMemoryUseRatio1000());
01612 }
01613 }
01614
01615
01616
01617 template <class EvalT>
01618 osl::search::AlphaBeta2<EvalT>::
01619 AlphaBeta2(const NumEffectState& s, checkmate_t& c,
01620 SimpleHashTable *t, CountRecorder& r)
01621 : AlphaBeta2Tree<EvalT>(s, c, t, r)
01622 {
01623 MoveGenerator::initOnce();
01624 #ifndef GPSONE
01625 if (OslConfig::usiMode()) {
01626 boost::shared_ptr<SearchMonitor> monitor(new UsiMonitor);
01627 this->addMonitor(monitor);
01628 }
01629 #endif
01630 }
01631
01632 template <class EvalT>
01633 osl::search::AlphaBeta2<EvalT>::
01634 ~AlphaBeta2()
01635 {
01636 }
01637
01638 template <class EvalT>
01639 typename osl::search::AlphaBeta2<EvalT>::PVCheckmateStatus osl::search::AlphaBeta2<EvalT>::
01640 findCheckmateInPV(int verify_node, CArray<bool,2>& king_in_threat)
01641 {
01642 king_in_threat.fill(false);
01643 if (this->shared_root->last_pv.empty())
01644 return PVStable;
01645 const SearchState2::PVVector& pv = this->shared_root->last_pv.back().pv;
01646 NumEffectState state = this->state();
01647 PathEncoding path = this->path();
01648 PVCheckmateStatus found = PVStable;
01649 SearchState2::checkmate_t *checkmate_searcher = this->checkmate_searcher;
01650 if (this->node_count < verify_node*pv.size())
01651 verify_node = this->node_count/(pv.size()+1)/4;
01652 for (size_t i=0; i<pv.size(); ++i)
01653 {
01654 this->checkmate_searcher->runGC(this->table->isVerbose(),
01655 this->lastMemoryUseRatio1000());
01656 assert(pv[i].isPass() || state.isValidMove(pv[i]));
01657 if (! pv[i].isPass() && ! state.isValidMove(pv[i]))
01658 {
01659 std::cerr << "pv error " << pv[i] << "\n" << state;
01660 return PVStable;
01661 }
01662 state.makeMove(pv[i]);
01663 path.pushMove(pv[i]);
01664 if (state.inCheck())
01665 continue;
01666 const HashKey key(state);
01667 SimpleHashRecord *record = this->table->allocate(key, 2000);
01668 if (! record)
01669 break;
01670 Move checkmate_move, threatmate_move;
01671 const bool old_win = this->isWinningState
01672 (*checkmate_searcher, state, key, path,
01673 0, checkmate_move, pv[i]);
01674 if (! old_win)
01675 {
01676 const bool new_win = this->isWinningState
01677 (*checkmate_searcher, state, key, path,
01678 verify_node, checkmate_move, pv[i], true);
01679 if (new_win)
01680 {
01681 found = PVCheckmate;
01682 this->recordWinByCheckmate(state.turn(), record, checkmate_move);
01683 king_in_threat[alt(state.turn())] = true;
01684 if (this->table->isVerbose())
01685 std::cerr << " pv checkmate " << record::csa::show(pv[i])
01686 << "(" << i << ")\n";
01687 }
01688 }
01689 state.changeTurn();
01690 const Player T = state.turn();
01691 const bool old_threatmate_in_record = record->threatmate().isThreatmate(alt(T));
01692 const bool old_threatmate = this->isWinningState
01693 (*checkmate_searcher, state, HashKey(state), PathEncoding(T),
01694 1, threatmate_move, Move::PASS(alt(T)));
01695 if (! old_threatmate)
01696 {
01697 const bool new_threatmate = this->isWinningState
01698 (*checkmate_searcher, state, HashKey(state), PathEncoding(T),
01699 verify_node, threatmate_move, Move::PASS(alt(T)), this->root_limit >= 1000 + this->rootLimitBias());
01700 if (new_threatmate)
01701 {
01702 record->threatmate().setThreatmate(alt(T), threatmate_move);
01703 king_in_threat[alt(T)] = true;
01704 if (! old_threatmate_in_record)
01705 found = PVThreatmate;
01706 else if (found == PVStable)
01707 found = PVThreatmateNotRecord;
01708 if (this->table->isVerbose())
01709 std::cerr << " pv threatmate " << record::csa::show(pv[i])
01710 << "(" << i << ")\n";
01711 }
01712 }
01713 state.changeTurn();
01714 }
01715 this->checkmate_searcher->runGC(this->table->isVerbose(),
01716 this->lastMemoryUseRatio1000());
01717 this->updateCheckmateCount();
01718 return found;
01719 }
01720
01721 template <class EvalT>
01722 int osl::search::AlphaBeta2<EvalT>::
01723 alphaBetaSearchRoot(MoveLogProb& best_move, int limit)
01724 {
01725 const Player Turn = this->state().turn();
01726 if (OslConfig::forceRootWindow()) {
01727 const double scale = this->eval.captureValue(newPtypeO(WHITE,PAWN))/2;
01728 std::pair<int,int> ab = OslConfig::rootWindow();
01729 ab = std::make_pair((int)(ab.first*scale/OslConfig::usiOutputPawnValue()),
01730 (int)(ab.second*scale/OslConfig::usiOutputPawnValue()));
01731 if (ab.first % 2 == 0)
01732 ab.first += (ab.first > 0) ? 1 : -1;
01733 if (ab.second % 2 == 0)
01734 ab.second += (ab.second > 0) ? 1 : -1;
01735 if (eval::betterThan(Turn, ab.first, ab.second))
01736 ab = std::make_pair(ab.second, ab.first);
01737 Window root_window(Turn, ab.first, ab.second);
01738 assert(root_window.isConsistent());
01739 return alphaBetaSearchRoot(root_window, best_move, limit);
01740 }
01741 Window root_window = this->fullWindow(Turn);
01742 return alphaBetaSearchRoot(root_window, best_move, limit);
01743 }
01744
01745 template <class EvalT>
01746 osl::Move osl::search::AlphaBeta2<EvalT>::
01747 computeBestMoveIteratively(int limit, const int step,
01748 int initial_limit, size_t node_limit,
01749 const TimeAssigned& assign,
01750 MoveWithComment *additional_info)
01751 {
01752 this->setStartTime(MilliSeconds::now());
01753 this->setTimeAssign(assign);
01754 if (this->table->verboseLevel() > 2)
01755 {
01756 const time_t now = time(0);
01757 char ctime_buf[64];
01758 std::cerr << "AlphaBeta2 " << ctime_r(&now, ctime_buf);
01759 }
01760 if (this->table->isVerbose()) {
01761 std::cerr << " time assign/max " << this->timeAssigned().standard.toSeconds()
01762 << "/" << this->timeAssigned().max.toSeconds()
01763 << " multipv " << this->multi_pv
01764 << " iteration " << this->nextIterationCoefficient()
01765 << " mem " << std::fixed << std::setprecision(2)
01766 << OslConfig::memoryUseRatio()*100.0 << "%";
01767 if (OslConfig::forceRootWindow()) {
01768 std::pair<int,int> ab = OslConfig::rootWindow();
01769 if (ab.first == ab.second)
01770 std::cerr << " root-null-window "
01771 << (ab.first*100.0/OslConfig::usiOutputPawnValue());
01772 else
01773 std::cerr << " root-window ["
01774 << (ab.first*100.0/OslConfig::usiOutputPawnValue())
01775 << ", "
01776 << (ab.second*100.0/OslConfig::usiOutputPawnValue())
01777 << "]";
01778 }
01779 std::cerr << "\n";
01780 }
01781 initial_limit = std::min(initial_limit, limit);
01782
01783 this->recorder.resetNodeCount();
01784
01785 double last_iteration_consumed = 0;
01786 double total_consumed = 0;
01787 int limit_iterative = initial_limit;
01788 Move last_best_move = Move::INVALID();
01789 this->shared_root->last_pv.clear();
01790
01791 #ifdef OSL_SMP
01792 # ifdef SPLIT_STAT
01793 if (this->shared) {
01794 this->shared->parallel_splits = 0;
01795 this->shared->cancelled_splits.setValue(0);
01796 this->shared->parallel_abort.setValue(0);
01797 }
01798 # endif
01799 #endif
01800 try
01801 {
01802 if (this->table->verboseLevel() > 1)
01803 {
01804 MoveVector moves;
01805 move_generator::LegalMoves::generate(this->state(), moves);
01806 BOOST_FOREACH(Move move, moves) {
01807 HashKey key = this->currentHash().newHashWithMove(move);
01808 const SimpleHashRecord *record = this->table->find(key);
01809 if (! record || record->lowerLimit() < SearchTable::HistorySpecialDepth)
01810 continue;
01811 std::cerr << "prebound value " << record::csa::show(move)
01812 << " " << record->lowerBound() << " " << record->upperBound() << "\n";
01813 }
01814 }
01815
01816 MoveLogProb search_move;
01817 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, 0));
01818 this->shared_root->last_root_move = search_move.move();
01819 this->shared_root->best_move_for_iteration.push_back(search_move.move());
01820 if (this->table->verboseLevel() > 1)
01821 std::cerr << "=> quiesce "
01822 << record::csa::show(search_move.move()) << "\n";
01823 while (limit_iterative < limit && ! this->stopping())
01824 {
01825 if (this->table->verboseLevel() > 1)
01826 std::cerr << "=> iteration " << limit_iterative
01827 << " (" << last_iteration_consumed << ", " << total_consumed << " sec)"
01828 << " mem " << OslConfig::memoryUseRatio()*100.0 << "%\n";
01829 this->recorder.startSearch(limit_iterative);
01830 const int previous_node_count = this->nodeCount();
01831 try {
01832 for (int i=0; i<8; ++i)
01833 {
01834 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, limit_iterative+this->rootLimitBias()));
01835 this->shared_root->last_root_move = search_move.move();
01836 last_best_move = search_move.move();
01837 if (this->stopping())
01838 break;
01839 PVCheckmateStatus need_more_verify = PVStable;
01840 CArray<bool, 2> king_in_threat;
01841 int verify_node_limit = limit <= (1200 + this->rootLimitBias()) ? 10000 : 40000;
01842 if (this->timeAssigned().standard.toSeconds() < 20)
01843 verify_node_limit /= 4;
01844 #ifdef DONT_USE_CHECKMATE
01845 break;
01846 #endif
01847 need_more_verify = findCheckmateInPV(verify_node_limit, king_in_threat);
01848 if (need_more_verify == PVStable
01849 || (i > 0 && need_more_verify == PVThreatmateNotRecord))
01850 break;
01851 if (this->isStableNow())
01852 this->setStable(i > 0 && king_in_threat[this->state().turn()] == false);
01853 }
01854 } catch (...) {
01855 last_iteration_consumed = this->elapsed() - total_consumed;
01856 total_consumed += last_iteration_consumed;
01857 this->updateCheckmateCount();
01858 this->recorder.finishSearch(search_move.move(), total_consumed,
01859 this->table->verboseLevel());
01860 throw;
01861 }
01862
01863 last_iteration_consumed = this->elapsed() - total_consumed;
01864 total_consumed += last_iteration_consumed;
01865 this->shared_root->best_move_for_iteration.push_back(last_best_move);
01866 this->shared_root->root_values_for_iteration.push_back
01867 (this->shared_root->root_values.back());
01868
01869 this->updateCheckmateCount();
01870 if (this->table->verboseLevel() > 2) {
01871 std::cerr << "<= "
01872 << record::csa::show(search_move.move());
01873 std::cerr << std::setprecision(4) << " mpn " << this->mpn.getAverage()
01874 << " cut " << this->mpn_cut.getAverage()
01875 << " alpha " << this->alpha_update.getAverage()
01876 << " last " << this->last_alpha_update.getAverage()
01877 << " ext " << 100.0*this->ext.getAverage() << "%"
01878 << " ext_limit " << this->ext_limit.getAverage()
01879 << " mem " << OslConfig::memoryUseRatio()*100.0;
01880 #ifdef OSL_SMP
01881 # ifdef SPLIT_STAT
01882 if (this->shared) {
01883 std::cerr << " split " << this->shared->parallel_splits << " cancel " << this->shared->cancelled_splits.value()
01884 << " abort " << this->shared->parallel_abort.value();
01885 }
01886 # endif
01887 #endif
01888 std::cerr << "\n";
01889 }
01890 bool time_over = false;
01891 if (this->hasSchedule()) {
01892 const double elapsed = this->elapsed();
01893 const double current_time_left = this->timeAssigned().standard.toSeconds() - elapsed;
01894 double coef = this->nextIterationCoefficient();
01895 if (! this->isStableNow())
01896 coef = std::min(0.5, coef);
01897 else {
01898 const int same_best_moves = this->shared_root->sameBestMoves();
01899 if (same_best_moves == 0) {
01900 if (this->table->verboseLevel() > 2 && coef > 0.75)
01901 std::cerr << "info: " << coef << " -> 0.75 by bestmove update\n";
01902 coef = std::min(0.75, coef);
01903 }
01904 else if (same_best_moves >= 3) {
01905 const Move last_move = this->lastMove();
01906 if (last_move.isNormal() && last_best_move.isNormal()
01907 && last_move.to() == last_best_move.to()
01908 && isMajor(last_best_move.capturePtype())
01909 && isMajorNonPieceOK(last_move.capturePtype())) {
01910 if (coef < 5.0 && this->table->verboseLevel() > 2)
01911 std::cerr << "info: " << coef << " -> 5.0 by takeback major piece\n";
01912 coef = std::max(5.0, coef);
01913 }
01914 }
01915 }
01916 if (current_time_left
01917 < last_iteration_consumed * coef)
01918 time_over = true;
01919 if (! time_over) {
01920 SimpleHashRecord *record
01921 = this->table->find(this->currentHash());
01922 if (record) {
01923 record->addNodeCount(this->nodeCount() - previous_node_count);
01924 }
01925 }
01926 }
01927 bool node_limit_over = (this->recorder.nodeCount() *4 > node_limit);
01928 this->recorder.finishSearch(search_move.move(),
01929 total_consumed,
01930 (time_over || node_limit_over) && this->table->verboseLevel());
01931 if (time_over || node_limit_over || this->stopping()) {
01932 if (this->table->isVerbose()) {
01933 const char *reason = "other reason";
01934 if (this->stopReason() == SearchTimerCommon::NoMoreMemory)
01935 reason = "memory full";
01936 else if (time_over || this->stopReason() == SearchTimerCommon::NoMoreTime)
01937 reason = "time";
01938 else if (node_limit_over)
01939 reason = "node count";
01940 else if (this->stopReason() == SearchTimerCommon::StopByOutside)
01941 reason = "outside";
01942 std::cerr << "iteration stop at " << limit_iterative << " by "
01943 << reason << "\n";
01944 }
01945 goto finish;
01946 }
01947 this->testStop();
01948 limit_iterative += step;
01949 }
01950 if (this->table->verboseLevel() > 1)
01951 std::cerr << "=> final iteration " << limit_iterative
01952 << " (" << last_iteration_consumed << ", " << total_consumed << " sec)"
01953 << " mem " << OslConfig::memoryUseRatio()*100.0 << "%\n";
01954 while (true) {
01955 this->recorder.startSearch(limit);
01956 try {
01957 for (int i=0; i<8; ++i)
01958 {
01959 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, limit+this->rootLimitBias()));
01960 this->shared_root->last_root_move = search_move.move();
01961 last_best_move = search_move.move();
01962 if (this->stopping())
01963 break;
01964 PVCheckmateStatus need_more_verify = PVStable;
01965 CArray<bool, 2> king_in_threat;
01966 int verify_node_limit = limit <= (1200 + this->rootLimitBias()) ? 10000 : 40000;
01967 if (this->timeAssigned().standard.toSeconds() < 20)
01968 verify_node_limit /= 4;
01969 #ifdef DONT_USE_CHECKMATE
01970 break;
01971 #endif
01972 need_more_verify = findCheckmateInPV(verify_node_limit, king_in_threat);
01973 if (need_more_verify == PVStable
01974 || (i > 0 && need_more_verify == PVThreatmateNotRecord))
01975 break;
01976 if (this->isStableNow())
01977 this->setStable(i > 0 && king_in_threat[this->state().turn()] == false);
01978 }
01979 } catch (...) {
01980 last_iteration_consumed = this->elapsed() - total_consumed;
01981 total_consumed += last_iteration_consumed;
01982 this->updateCheckmateCount();
01983 this->recorder.finishSearch(search_move.move(), total_consumed,
01984 this->table->verboseLevel());
01985 throw;
01986 }
01987 last_iteration_consumed = this->elapsed() - total_consumed;
01988 total_consumed += last_iteration_consumed;
01989 this->updateCheckmateCount();
01990 this->recorder.finishSearch(search_move.move(), total_consumed,
01991 this->table->verboseLevel());
01992 this->shared_root->best_move_for_iteration.push_back(last_best_move);
01993 this->shared_root->root_values_for_iteration.push_back
01994 (this->shared_root->root_values.back());
01995
01996 if (last_best_move.isNormal())
01997 break;
01998 this->testStop();
01999
02000
02001 if (limit >= 2000 || this->root_ignore_moves)
02002 break;
02003
02004 limit += 200;
02005 if (this->table->isVerbose())
02006 std::cerr << " extend limit to " << limit << " before resign\n";
02007 }
02008 }
02009 catch (std::exception& e)
02010 {
02011 if (! OslConfig::usiMode())
02012 std::cerr << "std exception " << e.what() << "\n";
02013 }
02014 catch (...)
02015 {
02016 std::cerr << "unknown exception\n";
02017 #ifndef NDEBUG
02018 throw;
02019 #endif
02020 }
02021 finish:
02022 if (this->table->verboseLevel() > 1) {
02023 std::cerr << "<= " << record::csa::show(last_best_move);
02024 std::cerr << std::setprecision(4) << " mpn " << this->mpn.getAverage()
02025 << " cut " << this->mpn_cut.getAverage()
02026 << " alpha " << this->alpha_update.getAverage()
02027 << " last " << this->last_alpha_update.getAverage()
02028 << " ext " << this->ext.getAverage()
02029 << " ext_limit " << this->ext_limit.getAverage()
02030 << " mem " << OslConfig::memoryUseRatio()*100.0;
02031 #ifdef OSL_SMP
02032 # ifdef SPLIT_STAT
02033 if (this->shared) {
02034 std::cerr << " split " << this->shared->parallel_splits << " cancel " << this->shared->cancelled_splits.value()
02035 << " abort " << this->shared->parallel_abort.value();
02036 }
02037 # endif
02038 #endif
02039 std::cerr << "\n";
02040 }
02041
02042 if (additional_info) {
02043 additional_info->node_count = this->nodeCount();
02044 additional_info->elapsed = this->elapsed();
02045 additional_info->moves.clear();
02046 additional_info->root_limit = this->root_limit;
02047 }
02048 if (additional_info && this->shared_root->root_values.size() > 1) {
02049 assert(last_best_move == this->shared_root->last_root_move);
02050 additional_info->move = last_best_move;
02051 const double scale = 200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN));
02052 additional_info->value = static_cast<int>(this->shared_root->last_root_value_update * scale);
02053 if (!this->shared_root->last_pv.empty()) {
02054 for (size_t i=1; i<this->shared_root->last_pv.back().pv.size(); ++i) {
02055 additional_info->moves.push_back(this->shared_root->last_pv.back().pv[i]);
02056 }
02057 }
02058 }
02059 return last_best_move;
02060 }
02061
02062 template <class EvalT>
02063 template <osl::Player P>
02064 int osl::search::AlphaBeta2<EvalT>::
02065 alphaBetaSearchRoot(Window window, MoveLogProb& best_move, int limit)
02066 {
02067 #ifndef GPSONE
02068 {
02069 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02070 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02071 this->monitors())
02072 monitor->newDepth(limit/200);
02073 }
02074 #endif
02075 assert(P == this->state().turn());
02076 assert(window.alpha(P) % 2);
02077 assert(window.beta(P) % 2);
02078 setRoot(limit);
02079 assert(this->curDepth() == 0);
02080 this->node_type[this->curDepth()] = base_t::PvNode;
02081 #ifdef NEW_DFPN
02082 this->checkmate_searcher->setRootPlayer(P);
02083 #endif
02084 #ifdef OSL_SMP
02085 if (this->shared)
02086 this->shared->threadStart();
02087 #endif
02088
02089 SimpleHashRecord *record_in_table
02090 = this->table->allocate(this->currentHash(), limit);
02091 SimpleHashRecord *record = record_in_table;
02092 boost::scoped_ptr<SimpleHashRecord> record_if_not_allocated;
02093 if (! record)
02094 {
02095 record_if_not_allocated.reset(new SimpleHashRecord());
02096 record = record_if_not_allocated.get();
02097 }
02098 assert(record);
02099 this->setRootRecord(record);
02100 assert(this->rootRecord() == record);
02101 assert(this->hasLastRecord() && this->lastRecord() == record);
02102 record->setInCheck(this->state().inCheck());
02103
02104 if (limit == 0) {
02105 int result = this->template quiesce<P>(fullWindow(P));
02106 best_move = MoveLogProb(record->qrecord.bestMove(), 100);
02107 if (this->root_ignore_moves
02108 && this->root_ignore_moves->isMember(best_move.move()))
02109 best_move = MoveLogProb();
02110 #ifndef GPSONE
02111 else if (this->hasMonitor() && !this->prediction_for_speculative_search)
02112 {
02113 const double scale = OslConfig::usiOutputPawnValue()*2.0
02114 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02115 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02116 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02117 this->monitors())
02118 monitor->showPV(1, this->recorder.allNodeCount(),
02119 this->elapsed(), static_cast<int>(result*scale),
02120 best_move.move(), 0, 0);
02121 }
02122 #endif
02123 return result;
02124 }
02125 if (record_in_table) {
02126 int table_value = 0;
02127 const MoveLogProb m = record_in_table->bestMove();
02128 if (! m.isNormal())
02129 record_in_table->resetValue();
02130 else if (record->hasGreaterLowerBound<P>(this->curLimit(), window.beta(P),
02131 table_value)) {
02132 if (! this->root_ignore_moves
02133 || ! this->root_ignore_moves->isMember(m.move())) {
02134 best_move = m;
02135 return table_value;
02136 }
02137 }
02138 }
02139
02140
02141 MoveLogProbVector moves;
02142 MoveGenerator& generator = this->makeGenerator();
02143 const MoveLogProb last_best_move = record->bestMove();
02144 {
02145 MoveLogProbVector raw_moves;
02146 assert(this->curLimit() > 0);
02147 const Move hash_move = last_best_move.isNormal()
02148 ? last_best_move.move() : record->qrecord.bestMove();
02149 generator.init(this->curLimit()+200, record, this->eval, this->state(), true, hash_move);
02150 if (last_best_move.isNormal())
02151 raw_moves.push_back(last_best_move);
02152 else if (record->qrecord.bestMove().isNormal())
02153 raw_moves.push_back(MoveLogProb(record->qrecord.bestMove(), 100));
02154 generator.generateAll<P>(*this, raw_moves);
02155
02156
02157 for (size_t i=0; i<raw_moves.size(); ++i) {
02158 const Move m = raw_moves[i].move();
02159 if (i > 0 && m == hash_move)
02160 continue;
02161 const HashKey key = this->currentHash().newHashWithMove(m);
02162 const SimpleHashRecord *record = this->table->find(key);
02163 assert(this->state().isValidMove(m));
02164 if (record) {
02165 if (record->hasUpperBound(SearchTable::HistorySpecialDepth)
02166 && this->isWinValue(alt(P), record->upperBound()))
02167 continue;
02168 }
02169 if (this->root_ignore_moves && this->root_ignore_moves->isMember(m))
02170 continue;
02171 if (! m.isDrop() && m.ptype() != KING
02172 && move_classifier::KingOpenMove<P>::isMember(this->state(), m.ptype(), m.from(), m.to()))
02173 continue;
02174 if (move_classifier::MoveAdaptor<move_classifier::PawnDropCheckmate<P> >
02175 ::isMember(this->state(), m))
02176 continue;
02177 raw_moves[i].setLogProbAtMost(limit);
02178 moves.push_back(raw_moves[i]);
02179 }
02180 }
02181
02182 if (! OslConfig::searchExactValueInOneReply()) {
02183 if (moves.size() == 1
02184 || (moves.size() == 2 && moves[0].move() == moves[1].move()))
02185 {
02186 best_move = moves[0];
02187 #ifndef GPSONE
02188 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
02189 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02190 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02191 this->monitors())
02192 monitor->rootForcedMove(best_move.move());
02193 }
02194 #endif
02195 return 0;
02196 }
02197 }
02198
02199 #ifndef DONT_USE_CHECKMATE
02200
02201 int checkmate_node = 0;
02202 if (! this->prediction_for_speculative_search) {
02203 int checkmate_max = 30000*std::max(limit - 300 - this->rootLimitBias(), 0)/100;
02204 if (limit >= 1000 + this->rootLimitBias())
02205 checkmate_max = std::min(400000, 60000*(limit - 800 - this->rootLimitBias())/100);
02206 if (this->timeAssigned().standard.toSeconds() < 20) {
02207 checkmate_node /= 4;
02208 if (this->timeAssigned().standard.toSeconds() < 10)
02209 checkmate_node /= 2;
02210 }
02211 checkmate_node = record->qrecord.checkmateNodesLeft(checkmate_max);
02212 #ifdef CHECKMATE_COUNT
02213 std::cerr << "limit " << limit << " checkmate " << checkmate_node << "\n";
02214 #endif
02215 }
02216 if (checkmate_node > 0)
02217 {
02218 const bool my_king_in_check
02219 = this->state().hasEffectAt(alt(P),this->state().kingSquare(P));
02220 if (my_king_in_check)
02221 {
02222
02223 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node/8);
02224 const bool lose = this->template isLosingState<P>(checkmate_node/8);
02225 this->recorder.backFromCheckmateSearch();
02226 this->updateCheckmateCount();
02227 if (lose)
02228 {
02229 best_move = MoveLogProb(Move::INVALID(),100);
02230 this->recordLoseByCheckmate(P, record);
02231 this->shared_root->last_pv.clear();
02232 this->shared_root->last_root_move = Move();
02233 this->shared_root->last_root_value_update = this->winByCheckmate(alt(P));
02234 #ifndef GPSONE
02235 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02236 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02237 this->monitors())
02238 monitor->rootLossByCheckmate();
02239 #endif
02240 return this->winByCheckmate(alt(P));
02241 }
02242 }
02243
02244 {
02245 Move checkmate_move;
02246 #ifdef CHECKMATE_COUNT
02247 size_t count = this->checkmateSearcher().totalNodeCount();
02248 #endif
02249 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node);
02250 const bool win = this->template isWinningState<P>
02251 (checkmate_node, checkmate_move, limit >= 1000 + this->rootLimitBias());
02252 this->recorder.backFromCheckmateSearch();
02253 this->updateCheckmateCount();
02254 #ifdef CHECKMATE_COUNT
02255 root_checkmate += this->checkmateSearcher().totalNodeCount() - count;
02256 #endif
02257 if (win)
02258 {
02259 best_move = MoveLogProb(checkmate_move,100);
02260 this->recordWinByCheckmate(P, record, checkmate_move);
02261 this->shared_root->last_pv.clear();
02262 this->shared_root->last_root_move = checkmate_move;
02263 this->shared_root->last_root_value_update = this->winByCheckmate(P);
02264 this->pv[1].clear();
02265 this->updateRootPV(P, std::cerr, this->winByCheckmate(P), checkmate_move);
02266 return this->winByCheckmate(P);
02267 }
02268 }
02269
02270 if ((! my_king_in_check)
02271 && (! (record->threatmate().isThreatmate(P))))
02272 {
02273 Move threatmate_move;
02274 #ifdef CHECKMATE_COUNT
02275 size_t count = this->checkmateSearcher().totalNodeCount();
02276 #endif
02277 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node);
02278 const bool threatmate
02279 = this->template isThreatmateState<P>
02280 (checkmate_node, threatmate_move, limit >= 1000 + this->rootLimitBias());
02281 #ifdef CHECKMATE_COUNT
02282 root_checkmate += this->checkmateSearcher().totalNodeCount() - count;
02283 #endif
02284 this->recorder.backFromCheckmateSearch();
02285 this->updateCheckmateCount();
02286 if (threatmate)
02287 {
02288 if (record)
02289 record->threatmate().setThreatmate(P, threatmate_move);
02290 if (this->table->verboseLevel() > 1)
02291 std::cerr << " root threatmate " << threatmate_move << "\n";
02292 }
02293 BOOST_FOREACH(Ptype ptype, PieceStand::order)
02294 {
02295 this->testStop();
02296 if (! this->state().hasPieceOnStand(P, ptype))
02297 continue;
02298 NumEffectState state(this->state().emulateHandPiece(P, alt(P), ptype));
02299 state.setTurn(alt(P));
02300 Move hand_move;
02301 this->template isWinningState<PlayerTraits<P>::opponent>
02302 (*this->checkmate_searcher, state, HashKey(state), PathEncoding(alt(P)),
02303 checkmate_node, hand_move, Move::PASS(P), limit >= 1000 + this->rootLimitBias());
02304 }
02305 }
02306 this->testStop();
02307 }
02308 this->checkmate_searcher->runGC(this->table->isVerbose(),
02309 this->lastMemoryUseRatio1000());
02310 #endif
02311 const int ValueNone = window.alpha(P) - EvalTraits<P>::delta;
02312 int best_value = ValueNone;
02313 try {
02314
02315 size_t i=0;
02316 if (limit >= 1000 && ! moves.empty() && window == fullWindow(P))
02317 {
02318
02319 const int root_alpha =
02320 this->rootAlpha(P, this->shared_root->root_values.size() ? this->shared_root->root_values.back() : 0,
02321 this->eval.progress16());
02322 if (EvalTraits<P>::betterThan(root_alpha, window.alpha(P))) {
02323 const Window window_copy = window;
02324 window.alpha(P) = root_alpha;
02325 #ifndef GPSONE
02326 {
02327 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02328 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02329 this->monitors())
02330 monitor->rootFirstMove(moves[0].move());
02331 }
02332 #endif
02333 const int result = this->template alphaBetaSearch<P>(moves[0], window, true);
02334 if (EvalTraits<P>::betterThan(result, root_alpha))
02335 {
02336 window.alpha(P) = result + EvalTraits<P>::delta;
02337 best_move = moves[0];
02338 best_value = result;
02339 this->updateRootPV(P, std::cerr, result, moves[0].move());
02340 ++i;
02341 }
02342 else
02343 {
02344 if (this->table->isVerbose())
02345 this->showFailLow(result, moves[0].move());
02346 this->setStable(false);
02347 window = window_copy;
02348 }
02349 this->checkmate_searcher->runGC(this->table->isVerbose(),
02350 this->lastMemoryUseRatio1000());
02351 }
02352 }
02353 for (; i<moves.size() && best_value == ValueNone
02354 && window == fullWindow(P); ++i) {
02355 const MoveLogProb& m = moves[i];
02356 #ifndef GPSONE
02357 {
02358 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02359 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02360 this->monitors())
02361 monitor->rootMove(m.move());
02362 }
02363 #endif
02364 const int result = this->template alphaBetaSearch<P>(m, window, true);
02365 if (eval::betterThan(P, result, best_value)) {
02366 window.alpha(P) = result + EvalTraits<P>::delta;
02367 best_move = m;
02368 best_value = result;
02369 this->updateRootPV(P, std::cerr, result, m.move());
02370 if (eval::betterThan(P, result, window.beta(P))) {
02371 assert(! this->isWinValue(alt(P), result));
02372 }
02373 }
02374 else if (result == ValueNone)
02375 this->setStable(false);
02376 this->checkmate_searcher->runGC(this->table->isVerbose(),
02377 this->lastMemoryUseRatio1000());
02378 }
02379
02380 if (! eval::betterThan(P, window.alpha(P), window.beta(P))) {
02381 this->template examineMovesRoot<P>(moves, i, window, best_move, best_value);
02382 }
02383 if (best_move.isNormal()) {
02384 if (best_value != ValueNone) {
02385 assert(! this->shared_root->last_pv.empty());
02386 assert(best_move.move() == this->shared_root->last_pv.back().pv[0]);
02387 }
02388 }
02389 } catch (std::runtime_error& e) {
02390 if (this->table->isVerbose())
02391 std::cerr << e.what() << "\n";
02392 assert(best_value % 2 == 0);
02393 this->stopNow();
02394 this->restoreRootState();
02395 if (best_value != ValueNone)
02396 record->setLowerBound(P, this->curLimit(), best_move, best_value);
02397 if (best_move.validMove()
02398 && best_move.move() != last_best_move.move()) {
02399 if (this->table->verboseLevel() > 1) {
02400 std::cerr << "! use better move than the last best move\n";
02401 if (best_value != ValueNone) {
02402 assert(! this->shared_root->last_pv.empty() &&
02403 ! this->shared_root->last_pv.back().pv.empty());
02404 assert(best_move.move() == this->shared_root->last_pv.back().pv[0]);
02405 }
02406 }
02407 }
02408 else {
02409 #ifdef OSL_SMP
02410 if (this->shared)
02411 this->shared->waitAll();
02412 #endif
02413 throw;
02414 }
02415 }
02416
02417 assert(best_value % 2 == 0);
02418 if (best_value != ValueNone)
02419 record->setLowerBound(P, this->curLimit(), best_move, best_value);
02420 #ifdef OSL_SMP
02421 if (this->shared)
02422 this->shared->waitAll();
02423 #endif
02424 #ifndef GPSONE
02425 if (best_value == ValueNone
02426 && this->hasMonitor() && !this->prediction_for_speculative_search)
02427 {
02428 const double scale = OslConfig::usiOutputPawnValue()*2.0
02429 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02430 const int value = OslConfig::forceRootWindow()
02431 ? ValueNone : this->winByCheckmate(alt(P));
02432 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02433 this->monitors())
02434 monitor->showPV(limit/200, this->recorder.allNodeCount(),
02435 this->elapsed(), static_cast<int>(value*scale),
02436 Move::INVALID(), 0, 0);
02437 }
02438 #endif
02439 return best_value;
02440 }
02441
02442 template <class EvalT>
02443 void osl::search::AlphaBeta2<EvalT>::setRoot(int limit)
02444 {
02445 SearchState2::setRoot(limit);
02446 SimpleHashRecord *record = this->table->allocate(this->currentHash(), std::max(1000,limit));
02447 assert(record);
02448 this->setRootRecord(record);
02449 this->move_type[this->curDepth()] = base_t::INITIAL;
02450 }
02451
02452 template <class EvalT>
02453 void osl::search::AlphaBeta2<EvalT>::makeMove(Move move)
02454 {
02455 assert(this->state().isValidMove(move));
02456 SearchState2::makeMove(move);
02457 this->eval.update(this->state(), move);
02458
02459 SimpleHashRecord *record
02460 = this->table->allocate(this->currentHash(), this->curLimit());
02461 assert(record);
02462 this->move_type[this->curDepth()] = base_t::INITIAL;
02463 record->setInCheck(this->state().inCheck());
02464 this->setCurrentRecord(record);
02465 }
02466
02467 template <class EvalT>
02468 bool osl::search::AlphaBeta2<EvalT>::
02469 isReasonableMove(Move , int )
02470 {
02471 return true;
02472 }
02473
02474 template <class EvalT>
02475 void osl::search::AlphaBeta2<EvalT>::
02476 showNodeDepth(std::ostream& os)
02477 {
02478 #ifndef MINIMAL
02479 int max_depth=0;
02480 for (int i=base_t::MaxDepth-1; i>=0; --i) {
02481 if (base_t::depth_node_count[i] || base_t::depth_node_count_quiesce[i]) {
02482 max_depth = i;
02483 break;
02484 }
02485 }
02486 int max_count=0;
02487 for (int i=0; i<=max_depth; i+=2) {
02488 max_count = std::max(max_count,
02489 base_t::depth_node_count[i]+base_t::depth_node_count_quiesce[i]);
02490 }
02491
02492 int unit = std::max(max_count/79, 100);
02493 for (int i=0; i<=max_depth; i+=2) {
02494 os << std::setw(3) << i << " "
02495 << std::string(base_t::depth_node_count[i]/unit, '*')
02496 << std::string(base_t::depth_node_count_quiesce[i]/unit, '+')
02497 << std::endl;
02498 }
02499 # ifdef CHECKMATE_COUNT
02500 std::cerr << "checkmate root " << root_checkmate << " quiesce " << quiesce_checkmate
02501 << "\nnormal before " << checkmate_before
02502 << " after " << checkmate_after << " threatmate " << count_threatmate
02503 << "\n";
02504 # endif
02505 #endif
02506 }
02507
02508 template <class EvalT>
02509 void osl::search::AlphaBeta2<EvalT>::
02510 clearNodeDepth()
02511 {
02512 #ifndef MINIMAL
02513 base_t::depth_node_count.fill(0);
02514 base_t::depth_node_count_quiesce.fill(0);
02515 #endif
02516 }
02517
02518 namespace osl
02519 {
02520 namespace search
02521 {
02522 #ifndef MINIMAL
02523 template class AlphaBeta2<eval::ProgressEval>;
02524 template class AlphaBeta2Tree<eval::ProgressEval>;
02525 template
02526 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRoot<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02527 template
02528 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRoot<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02529 #endif
02530 template class AlphaBeta2<eval::ml::OpenMidEndingEval>;
02531 template class AlphaBeta2Tree<eval::ml::OpenMidEndingEval>;
02532 template
02533 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRoot<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02534 template
02535 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRoot<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02536 }
02537 }
02538
02539
02540
02541
02542
02543