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 this->recorder.setCheckmateCount(shared->checkmateCount());
01264 return;
01265 }
01266 #endif
01267 this->recorder.setCheckmateCount
01268 (checkmateSearcher().totalNodeCount());
01269 }
01270
01271 template <class EvalT>
01272 int
01273 osl::search::AlphaBeta2Tree<EvalT>::
01274 rootAlpha(Player P, int last_value, Progress16 progress)
01275 {
01276 int pawns = 3;
01277 if (eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(alt(P),KING))))
01278 {
01279 pawns = 10;
01280 }
01281 else if (progress.value() <= 1)
01282 {
01283 pawns = 3;
01284 }
01285 else if (progress.value() <= 7)
01286 {
01287 pawns = 4;
01288 }
01289 else if (progress.value() <= 9)
01290 {
01291 pawns = 5;
01292 }
01293 else if (progress.value() <= 10)
01294 {
01295 pawns = 6;
01296 }
01297 else
01298 {
01299 pawns = 7;
01300 }
01301 const int width = eval_t::captureValue(newPtypeO(alt(P),PAWN))*pawns/2;
01302 return last_value - width - eval::delta(P);
01303 }
01304
01305 template <class EvalT>
01306 int
01307 osl::search::AlphaBeta2Tree<EvalT>::
01308 stableThreshold(Player P, int last_value)
01309 {
01310 int pawns = 3;
01311 if (eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(alt(P),KING))))
01312 pawns = 10;
01313 else if (eval::betterThan(P, eval_t::captureValue(newPtypeO(alt(P),PAWN))*2, last_value)
01314 && eval::betterThan(P, last_value, eval_t::captureValue(newPtypeO(P,PAWN))*2))
01315 pawns = 2;
01316 const int width = eval_t::captureValue(newPtypeO(alt(P),PAWN))*pawns/2;
01317 return last_value - width - eval::delta(P);
01318 }
01319
01320 namespace osl
01321 {
01322 namespace
01323 {
01324 void find_threatmate(const SimpleHashTable& table, HashKey key,
01325 const search::SearchState2::PVVector& pv,
01326 CArray<bool, search::SearchState2::MaxDepth>& threatmate)
01327 {
01328 for (size_t i=0; i<pv.size(); ++i) {
01329 key = key.newMakeMove(pv[i]);
01330 const SimpleHashRecord *record = table.find(key);
01331 threatmate[i] = record
01332 && record->threatmate().isThreatmate(key.turn());
01333 }
01334 }
01335 }
01336 }
01337
01338 template <class EvalT>
01339 void osl::search::AlphaBeta2Tree<EvalT>::
01340 updateRootPV(Player P, std::ostream& os, int result, Move m)
01341 {
01342 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01343 this->makePV(m);
01344 const int last_root_value = shared_root->root_values_for_iteration.size() ? shared_root->root_values_for_iteration.back() : 0;
01345 const int threshold = stableThreshold(P, last_root_value);
01346 bool new_stable = eval::betterThan(P, result, threshold);
01347 shared_root->last_root_value_update = result;
01348
01349 if (new_stable && m != shared_root->last_root_move
01350 && (See::see(state(), m) < -eval::Ptype_Eval_Table.value(KNIGHT)*2
01351 || eval::betterThan(P, result, eval_t::captureValue(newPtypeO(alt(P),KING))))) {
01352 new_stable = false;
01353 }
01354 if (new_stable && shared_root->root_values_for_iteration.size() > 1) {
01355 const int last_root_value2 = shared_root->root_values_for_iteration[shared_root->root_values_for_iteration.size()-2];
01356 const int threshold2 = stableThreshold(P, last_root_value2);
01357 if (eval::betterThan(P, threshold2, result)
01358 && eval::betterThan(P, last_root_value2, last_root_value))
01359 new_stable = false;
01360 }
01361 this->shared_root->last_pv.push_back(RootPV(root_limit, pv[0], result));
01362 this->setStable(new_stable);
01363 #ifndef GPSONE
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 CArray<bool, MaxDepth> threatmate = {{ 0 }};
01368 find_threatmate(*this->table, currentHash(), pv[0], threatmate);
01369 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01370 this->monitors())
01371 monitor->showPV(root_limit/200, this->recorder.allNodeCount(),
01372 this->elapsed(), static_cast<int>(result*scale),
01373 m, &*pv[0].begin(), &*pv[0].end(),
01374 &threatmate[0], &threatmate[0]+pv[0].size());
01375 }
01376 #endif
01377 if (this->table->isVerbose()) {
01378 showPV(os, result, m, new_stable ? ' ' : '*');
01379 }
01380 }
01381
01382 template <class EvalT>
01383 void osl::search::AlphaBeta2Tree<EvalT>::
01384 addMultiPV(Player P, int result, Move m)
01385 {
01386 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01387 this->makePV(m);
01388 this->shared_root->last_pv.push_back(RootPV(root_limit, pv[0], result));
01389 std::swap(*this->shared_root->last_pv.rbegin(), *(this->shared_root->last_pv.rbegin()+1));
01390
01391 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
01392 const double scale = OslConfig::usiOutputPawnValue()*2.0
01393 / eval_t::captureValue(newPtypeO(alt(P),PAWN));
01394 CArray<bool, MaxDepth> threatmate = {{ 0 }};
01395 find_threatmate(*this->table, currentHash(), pv[0], threatmate);
01396 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01397 this->monitors())
01398 monitor->showPV(root_limit/200, this->recorder.allNodeCount(),
01399 this->elapsed(), static_cast<int>(result*scale),
01400 m, &*pv[0].begin(), &*pv[0].end(),
01401 &threatmate[0], &threatmate[0]+pv[0].size());
01402 }
01403
01404 if (this->table->isVerbose()) {
01405 showPV(std::cerr, result, m, '&');
01406 }
01407 }
01408
01409 template <class EvalT>
01410 void osl::search::AlphaBeta2Tree<EvalT>::
01411 showFailLow(int result, Move m) const
01412 {
01413 if (this->root_ignore_moves)
01414 std::cerr << "[" << this->root_ignore_moves->size() << "] ";
01415 std::cerr << " <" << std::setfill(' ') << std::setw(5)
01416 << static_cast<int>(result*200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN)))
01417 << " " << record::csa::show(m) << "\n";
01418 }
01419
01420 template <class EvalT>
01421 void osl::search::AlphaBeta2Tree<EvalT>::
01422 showPV(std::ostream& os, int result, Move m, char stable_char) const
01423 {
01424 assert(m.isNormal());
01425 if (this->root_ignore_moves)
01426 os << "[" << this->root_ignore_moves->size() << "] ";
01427 os << stable_char;
01428 os << " " << std::setfill(' ') << std::setw(5)
01429 << static_cast<int>(result*200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN))) << " ";
01430 BOOST_FOREACH(Move m, pv[0]) {
01431 os << record::csa::show(m);
01432 }
01433 const double elapsed = this->elapsed();
01434 if (elapsed > 1.0)
01435 os << " (" << elapsed << "s, " << OslConfig::memoryUseRatio()*100.0 << "%)";
01436 os << std::endl;
01437 #ifndef MINIMAL
01438 #ifndef _WIN32
01439 if (! OslConfig::usiMode())
01440 {
01441 NumEffectState state = this->state();
01442 std::string str; str.reserve(200); str = " ";
01443 for (size_t i=0; i<pv[0].size(); ++i) {
01444 str += record::ki2::show(pv[0][i], state, i ? pv[0][i-1] : Move());
01445 state.makeMove(pv[0][i]);
01446
01447
01448 const SimpleHashRecord *record
01449 = this->table->find(HashKey(state));
01450 if (record &&
01451 record->threatmate().isThreatmate(state.turn()))
01452 str += "(" K_TSUMERO ")";
01453 }
01454 std::string converted = IconvConvert::eucToLang(str);
01455 if (! converted.empty())
01456 os << converted << std::endl;
01457 }
01458 #endif
01459 #endif
01460
01461 #ifdef DEBUG_PV
01462 NumEffectState s = state();
01463 for (size_t i=0; i<pv[0].size(); ++i) {
01464 if (! pv[0][i].isPass() && ! s.isValidMove(pv[0][i])) {
01465 std::cerr << "root pv error " << pv[0][i] << " " << i << "\n";
01466 break;
01467 }
01468 ApplyMoveOfTurn::doMove(s, pv[0][i]);
01469 }
01470 #endif
01471 }
01472
01473 template <class EvalT>
01474 template <osl::Player P>
01475 struct osl::search::AlphaBeta2Tree<EvalT>::NextMove
01476 {
01477 AlphaBeta2Tree *searcher;
01478 const MoveLogProb& moved;
01479 Window window;
01480 int *result;
01481 bool in_pv;
01482 NextMove(AlphaBeta2Tree *s, const MoveLogProb& md, Window w, int *r,
01483 bool p)
01484 : searcher(s), moved(md), window(w), result(r), in_pv(p) {
01485 assert(P == md.player());
01486 }
01487 void operator()(Square ) {
01488 #ifndef NDEBUG
01489 const int cur_limit = searcher->curLimit();
01490 #endif
01491 *result =
01492 searcher->alphaBetaSearchAfterMove<P>(moved, window, in_pv);
01493 assert(cur_limit == searcher->curLimit() || searcher->SearchState2Core::abort());
01494 }
01495 };
01496
01497 template <class EvalT>
01498 template <osl::Player P>
01499 int osl::search::AlphaBeta2Tree<EvalT>::
01500 alphaBetaSearch(const MoveLogProb& search_move, Window w, bool in_pv)
01501 {
01502 assert(w.alpha(P) % 2);
01503 assert(w.beta(P) % 2);
01504 const Move move = search_move.move();
01505 assert(P == move.player());
01506 assert(P == state().turn());
01507 assert(eval::notLessThan(P, w.beta(P), w.alpha(P)));
01508
01509 testStop();
01510 pv[curDepth()+1].clear();
01511
01512 if (! move.isPass() ){
01513 if(MoveStackRejections::probe<P>(state(),history(),curDepth(),move,w.alpha(P),repetitionCounter().checkCount(alt(P)))){
01514 return this->winByLoop(alt(P));
01515 }
01516 if (move_classifier::MoveAdaptor<move_classifier::PawnDropCheckmate<P> >
01517 ::isMember(state(), move))
01518 return this->winByFoul(alt(P));
01519 }
01520
01521 const HashKey new_hash = currentHash().newHashWithMove(move);
01522 assert(P == move.player());
01523
01524 if (move.isPass())
01525 this->pass_count.inc(P);
01526
01527
01528 if (! this->pass_count.loopByBothPass()) {
01529 const Sennichite next_sennichite
01530 = repetition_counter.isAlmostSennichite(new_hash);
01531 if (next_sennichite.isDraw())
01532 return this->drawValue();
01533 if (next_sennichite.hasWinner())
01534 return this->winByFoul(next_sennichite.winner());
01535 assert(next_sennichite.isNormal());
01536 }
01537
01538 if (! move.isPass()) {
01539
01540 const DominanceCheck::Result has_dominance
01541 = DominanceCheck::detect(repetition_counter.history(), new_hash);
01542 if (has_dominance == DominanceCheck::LOSE)
01543 return this->winByLoop(alt(P));
01544 if (has_dominance == DominanceCheck::WIN)
01545 return this->winByLoop(P);
01546
01547 if (! move.isCapture()) {
01548 const int sacrifice_count = countSacrificeCheck2(this->curDepth());
01549 if (sacrifice_count == 2) {
01550
01551 const Square to = move.to();
01552 int offence = state().countEffect(P, to) + (move.isDrop() ? 1 : 0);
01553 const int deffense = state().hasEffectAt(alt(P), to);
01554 if (offence <= deffense)
01555 offence += AdditionalEffect::count2(state(), to, P);
01556 if (offence <= deffense) {
01557 return this->winByLoop(alt(P));
01558 }
01559 }
01560 }
01561 }
01562
01563 int result;
01564 NextMove<P> helper(this, search_move, w, &result, in_pv);
01565
01566 this->recorder.addNodeCount();
01567 const eval_t old_eval = this->eval;
01568 doUndoMoveOrPass<P,NextMove<P> >(new_hash, move, helper);
01569 this->eval = old_eval;
01570 if (move.isPass())
01571 this->pass_count.dec(P);
01572
01573 return result;
01574 }
01575
01576 template <class EvalT>
01577 template <osl::Player P>
01578 void osl::search::AlphaBeta2Tree<EvalT>::
01579 examineMovesRoot(const MoveLogProbVector& moves, size_t i, Window window,
01580 MoveLogProb& best_move, int& best_value)
01581 {
01582 for (;i<moves.size(); ++i) {
01583 testStop();
01584
01585 #if (defined OSL_SMP) && (! defined OSL_SMP_NO_SPLIT_ROOT)
01586 if (shared && i > 8
01587 && moves.size() > i+1) {
01588 int smp_idle;
01589 {
01590 # ifdef OSL_USE_RACE_DETECTOR
01591 boost::mutex::scoped_lock lk(shared->lock_smp);
01592 # endif
01593 smp_idle = shared->smp_idle;
01594 }
01595 if (smp_idle) {
01596 try {
01597 examineMovesRootPar<P>(moves, i, window, best_move, best_value);
01598 break;
01599 } catch (AlphaBeta2ParallelCommon::SplitFailed&) {
01600 }
01601 }
01602 }
01603 #endif
01604
01605 const MoveLogProb& m = moves[i];
01606 #ifndef GPSONE
01607 if (this->elapsed() > 1.0)
01608 {
01609 boost::mutex::scoped_lock lk(OslConfig::lock_io);
01610 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
01611 this->monitors())
01612 monitor->rootMove(m.move());
01613 }
01614 if (this->multi_pv) {
01615 int width = this->multi_pv*this->eval.captureValue(newPtypeO(P, PAWN))/200;
01616 if (width % 2 == 0)
01617 width -= EvalTraits<P>::delta;
01618 window.alpha(P) = best_value + width;
01619 }
01620 #endif
01621 const int result = alphaBetaSearch<P>(m, window, false);
01622 if (eval::betterThan(P, result, best_value))
01623 {
01624 window.alpha(P) = result + EvalTraits<P>::delta;
01625 best_move = m;
01626 best_value = result;
01627 updateRootPV(P, std::cerr, result, m.move());
01628 if (eval::betterThan(P, result, window.beta(P))) {
01629 assert(! this->isWinValue(alt(P), result));
01630 break;
01631 }
01632 }
01633 #ifndef GPSONE
01634 else if (this->multi_pv && eval::betterThan(P, result, window.alpha(P)))
01635 {
01636 addMultiPV(P, result, m.move());
01637 }
01638 #endif
01639 if (this->root_limit >= 1600)
01640 this->checkmate_searcher->runGC(this->table->isVerbose(),
01641 lastMemoryUseRatio1000());
01642 }
01643 }
01644
01645
01646
01647 template <class EvalT>
01648 osl::search::AlphaBeta2<EvalT>::
01649 AlphaBeta2(const NumEffectState& s, checkmate_t& c,
01650 SimpleHashTable *t, CountRecorder& r)
01651 : AlphaBeta2Tree<EvalT>(s, c, t, r)
01652 {
01653 MoveGenerator::initOnce();
01654 }
01655
01656 template <class EvalT>
01657 osl::search::AlphaBeta2<EvalT>::
01658 ~AlphaBeta2()
01659 {
01660 }
01661
01662 template <class EvalT>
01663 typename osl::search::AlphaBeta2<EvalT>::PVCheckmateStatus osl::search::AlphaBeta2<EvalT>::
01664 findCheckmateInPV(int verify_node, CArray<bool,2>& king_in_threat)
01665 {
01666 king_in_threat.fill(false);
01667 if (this->shared_root->last_pv.empty())
01668 return PVStable;
01669 const SearchState2::PVVector& pv = this->shared_root->last_pv.back().pv;
01670 NumEffectState state = this->state();
01671 PathEncoding path = this->path();
01672 PVCheckmateStatus found = PVStable;
01673 SearchState2::checkmate_t *checkmate_searcher = this->checkmate_searcher;
01674 if (this->node_count < verify_node*pv.size())
01675 verify_node = this->node_count/(pv.size()+1)/4;
01676 for (size_t i=0; i<pv.size(); ++i)
01677 {
01678 this->checkmate_searcher->runGC(this->table->isVerbose(),
01679 this->lastMemoryUseRatio1000());
01680 assert(pv[i].isPass() || state.isValidMove(pv[i]));
01681 if (! pv[i].isPass() && ! state.isValidMove(pv[i]))
01682 {
01683 std::cerr << "pv error " << pv[i] << "\n" << state;
01684 return PVStable;
01685 }
01686 state.makeMove(pv[i]);
01687 path.pushMove(pv[i]);
01688 if (state.inCheck())
01689 continue;
01690 const HashKey key(state);
01691 SimpleHashRecord *record = this->table->allocate(key, 2000);
01692 if (! record)
01693 break;
01694 Move checkmate_move, threatmate_move;
01695 const bool old_win = this->isWinningState
01696 (*checkmate_searcher, state, key, path,
01697 0, checkmate_move, pv[i]);
01698 if (! old_win)
01699 {
01700 const bool new_win = this->isWinningState
01701 (*checkmate_searcher, state, key, path,
01702 verify_node, checkmate_move, pv[i], true);
01703 if (new_win)
01704 {
01705 found = PVCheckmate;
01706 this->recordWinByCheckmate(state.turn(), record, checkmate_move);
01707 king_in_threat[alt(state.turn())] = true;
01708 if (this->table->isVerbose())
01709 std::cerr << " pv checkmate " << record::csa::show(pv[i])
01710 << "(" << i << ")\n";
01711 }
01712 }
01713 state.changeTurn();
01714 const Player T = state.turn();
01715 const bool old_threatmate_in_record = record->threatmate().isThreatmate(alt(T));
01716 const bool old_threatmate = this->isWinningState
01717 (*checkmate_searcher, state, HashKey(state), PathEncoding(T),
01718 1, threatmate_move, Move::PASS(alt(T)));
01719 if (! old_threatmate)
01720 {
01721 const bool new_threatmate = this->isWinningState
01722 (*checkmate_searcher, state, HashKey(state), PathEncoding(T),
01723 verify_node, threatmate_move, Move::PASS(alt(T)), this->root_limit >= 1000 + this->rootLimitBias());
01724 if (new_threatmate)
01725 {
01726 record->threatmate().setThreatmate(alt(T), threatmate_move);
01727 king_in_threat[alt(T)] = true;
01728 if (! old_threatmate_in_record)
01729 found = PVThreatmate;
01730 else if (found == PVStable)
01731 found = PVThreatmateNotRecord;
01732 if (this->table->isVerbose())
01733 std::cerr << " pv threatmate " << record::csa::show(pv[i])
01734 << "(" << i << ")\n";
01735 }
01736 }
01737 state.changeTurn();
01738 }
01739 this->checkmate_searcher->runGC(this->table->isVerbose(),
01740 this->lastMemoryUseRatio1000());
01741 this->updateCheckmateCount();
01742 return found;
01743 }
01744
01745 template <class EvalT>
01746 int osl::search::AlphaBeta2<EvalT>::
01747 alphaBetaSearchRoot(MoveLogProb& best_move, int limit)
01748 {
01749 const Player Turn = this->state().turn();
01750 Window root_window = this->fullWindow(Turn);
01751 return alphaBetaSearchRoot(root_window, best_move, limit);
01752 }
01753
01754 template <class EvalT>
01755 osl::Move osl::search::AlphaBeta2<EvalT>::
01756 computeBestMoveIteratively(int limit, const int step,
01757 int initial_limit, size_t node_limit,
01758 const TimeAssigned& assign,
01759 MoveWithComment *additional_info)
01760 {
01761 this->setStartTime(MilliSeconds::now());
01762 this->setTimeAssign(assign);
01763 if (this->table->verboseLevel() > 2)
01764 {
01765 const time_t now = time(0);
01766 char ctime_buf[64];
01767 std::cerr << "AlphaBeta2 " << ctime_r(&now, ctime_buf);
01768 }
01769 if (this->table->isVerbose()) {
01770 std::cerr << " time assign/max " << this->timeAssigned().standard.toSeconds()
01771 << "/" << this->timeAssigned().max.toSeconds()
01772 << " multipv " << this->multi_pv
01773 << " iteration " << this->nextIterationCoefficient()
01774 << " mem " << std::fixed << std::setprecision(2)
01775 << OslConfig::memoryUseRatio()*100.0 << "%";
01776 std::cerr << "\n";
01777 }
01778 initial_limit = std::min(initial_limit, limit);
01779
01780 this->recorder.resetNodeCount();
01781
01782 double last_iteration_consumed = 0;
01783 double total_consumed = 0;
01784 int limit_iterative = initial_limit;
01785 Move last_best_move = Move::INVALID();
01786 this->shared_root->last_pv.clear();
01787
01788 #ifdef OSL_SMP
01789 # ifdef SPLIT_STAT
01790 if (this->shared) {
01791 this->shared->parallel_splits = 0;
01792 this->shared->cancelled_splits.setValue(0);
01793 this->shared->parallel_abort.setValue(0);
01794 }
01795 # endif
01796 #endif
01797 try
01798 {
01799 if (this->table->verboseLevel() > 1)
01800 {
01801 MoveVector moves;
01802 move_generator::LegalMoves::generate(this->state(), moves);
01803 BOOST_FOREACH(Move move, moves) {
01804 HashKey key = this->currentHash().newHashWithMove(move);
01805 const SimpleHashRecord *record = this->table->find(key);
01806 if (! record || record->lowerLimit() < SearchTable::HistorySpecialDepth)
01807 continue;
01808 std::cerr << "prebound value " << record::csa::show(move)
01809 << " " << record->lowerBound() << " " << record->upperBound() << "\n";
01810 }
01811 }
01812
01813 MoveLogProb search_move;
01814 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, 0));
01815 this->shared_root->last_root_move = search_move.move();
01816 this->shared_root->best_move_for_iteration.push_back(search_move.move());
01817 if (this->table->verboseLevel() > 1)
01818 std::cerr << "=> quiesce "
01819 << record::csa::show(search_move.move()) << "\n";
01820 while (limit_iterative < limit && ! this->stopping())
01821 {
01822 if (this->table->verboseLevel() > 1)
01823 std::cerr << "=> iteration " << limit_iterative
01824 << " (" << last_iteration_consumed << ", " << total_consumed << " sec)"
01825 << " mem " << OslConfig::memoryUseRatio()*100.0 << "%\n";
01826 this->recorder.startSearch(limit_iterative);
01827 const int previous_node_count = this->nodeCount();
01828 try {
01829 for (int i=0; i<8; ++i)
01830 {
01831 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, limit_iterative+this->rootLimitBias()));
01832 this->shared_root->last_root_move = search_move.move();
01833 last_best_move = search_move.move();
01834 if (this->stopping())
01835 break;
01836 PVCheckmateStatus need_more_verify = PVStable;
01837 CArray<bool, 2> king_in_threat;
01838 int verify_node_limit = limit <= (1200 + this->rootLimitBias()) ? 10000 : 40000;
01839 if (this->timeAssigned().standard.toSeconds() < 20)
01840 verify_node_limit /= 4;
01841 #ifdef DONT_USE_CHECKMATE
01842 break;
01843 #endif
01844 need_more_verify = findCheckmateInPV(verify_node_limit, king_in_threat);
01845 if (need_more_verify == PVStable
01846 || (i > 0 && need_more_verify == PVThreatmateNotRecord))
01847 break;
01848 if (this->isStableNow())
01849 this->setStable(i > 0 && king_in_threat[this->state().turn()] == false);
01850 }
01851 } catch (...) {
01852 last_iteration_consumed = this->elapsed() - total_consumed;
01853 total_consumed += last_iteration_consumed;
01854 this->updateCheckmateCount();
01855 this->recorder.finishSearch(search_move.move(), total_consumed,
01856 this->table->verboseLevel());
01857 throw;
01858 }
01859
01860 last_iteration_consumed = this->elapsed() - total_consumed;
01861 total_consumed += last_iteration_consumed;
01862 this->shared_root->best_move_for_iteration.push_back(last_best_move);
01863 this->shared_root->root_values_for_iteration.push_back
01864 (this->shared_root->root_values.back());
01865
01866 this->updateCheckmateCount();
01867 if (this->table->verboseLevel() > 2) {
01868 std::cerr << "<= "
01869 << record::csa::show(search_move.move());
01870 std::cerr << std::setprecision(4) << " mpn " << this->mpn.getAverage()
01871 << " cut " << this->mpn_cut.getAverage()
01872 << " alpha " << this->alpha_update.getAverage()
01873 << " last " << this->last_alpha_update.getAverage()
01874 << " ext " << 100.0*this->ext.getAverage() << "%"
01875 << " ext_limit " << this->ext_limit.getAverage()
01876 << " mem " << OslConfig::memoryUseRatio()*100.0;
01877 #ifdef OSL_SMP
01878 # ifdef SPLIT_STAT
01879 if (this->shared) {
01880 std::cerr << " split " << this->shared->parallel_splits << " cancel " << this->shared->cancelled_splits.value()
01881 << " abort " << this->shared->parallel_abort.value();
01882 }
01883 # endif
01884 #endif
01885 std::cerr << "\n";
01886 }
01887 bool time_over = false;
01888 if (this->hasSchedule()) {
01889 const double elapsed = this->elapsed();
01890 const double current_time_left = this->timeAssigned().standard.toSeconds() - elapsed;
01891 double coef = this->nextIterationCoefficient();
01892 if (! this->isStableNow())
01893 coef = std::min(0.5, coef);
01894 else {
01895 const int same_best_moves = this->shared_root->sameBestMoves();
01896 if (same_best_moves == 0) {
01897 if (this->table->verboseLevel() > 2 && coef > 0.75)
01898 std::cerr << "info: " << coef << " -> 0.75 by bestmove update\n";
01899 coef = std::min(0.75, coef);
01900 }
01901 else if (same_best_moves >= 3) {
01902 const Move last_move = this->lastMove();
01903 if (last_move.isNormal() && last_best_move.isNormal()
01904 && last_move.to() == last_best_move.to()
01905 && isMajor(last_best_move.capturePtype())
01906 && isMajorNonPieceOK(last_move.capturePtype())) {
01907 if (coef < 5.0 && this->table->verboseLevel() > 2)
01908 std::cerr << "info: " << coef << " -> 5.0 by takeback major piece\n";
01909 coef = std::max(5.0, coef);
01910 }
01911 }
01912 }
01913 if (current_time_left
01914 < last_iteration_consumed * coef)
01915 time_over = true;
01916 if (! time_over) {
01917 SimpleHashRecord *record
01918 = this->table->find(this->currentHash());
01919 if (record) {
01920 record->addNodeCount(this->nodeCount() - previous_node_count);
01921 }
01922 }
01923 }
01924 bool node_limit_over = (this->recorder.nodeCount() *4 > node_limit);
01925 this->recorder.finishSearch(search_move.move(),
01926 total_consumed,
01927 (time_over || node_limit_over) && this->table->verboseLevel());
01928 if (time_over || node_limit_over || this->stopping()) {
01929 if (this->table->isVerbose()) {
01930 const char *reason = "other reason";
01931 if (this->stopReason() == SearchTimerCommon::NoMoreMemory)
01932 reason = "memory full";
01933 else if (time_over || this->stopReason() == SearchTimerCommon::NoMoreTime)
01934 reason = "time";
01935 else if (node_limit_over)
01936 reason = "node count";
01937 else if (this->stopReason() == SearchTimerCommon::StopByOutside)
01938 reason = "outside";
01939 std::cerr << "iteration stop at " << limit_iterative << " by "
01940 << reason << "\n";
01941 }
01942 goto finish;
01943 }
01944 this->testStop();
01945 limit_iterative += step;
01946 }
01947 if (this->table->verboseLevel() > 1)
01948 std::cerr << "=> final iteration " << limit_iterative
01949 << " (" << last_iteration_consumed << ", " << total_consumed << " sec)"
01950 << " mem " << OslConfig::memoryUseRatio()*100.0 << "%\n";
01951 while (true) {
01952 this->recorder.startSearch(limit);
01953 try {
01954 for (int i=0; i<8; ++i)
01955 {
01956 this->shared_root->root_values.push_back(alphaBetaSearchRoot(search_move, limit+this->rootLimitBias()));
01957 this->shared_root->last_root_move = search_move.move();
01958 last_best_move = search_move.move();
01959 if (this->stopping())
01960 break;
01961 PVCheckmateStatus need_more_verify = PVStable;
01962 CArray<bool, 2> king_in_threat;
01963 int verify_node_limit = limit <= (1200 + this->rootLimitBias()) ? 10000 : 40000;
01964 if (this->timeAssigned().standard.toSeconds() < 20)
01965 verify_node_limit /= 4;
01966 #ifdef DONT_USE_CHECKMATE
01967 break;
01968 #endif
01969 need_more_verify = findCheckmateInPV(verify_node_limit, king_in_threat);
01970 if (need_more_verify == PVStable
01971 || (i > 0 && need_more_verify == PVThreatmateNotRecord))
01972 break;
01973 if (this->isStableNow())
01974 this->setStable(i > 0 && king_in_threat[this->state().turn()] == false);
01975 }
01976 } catch (...) {
01977 last_iteration_consumed = this->elapsed() - total_consumed;
01978 total_consumed += last_iteration_consumed;
01979 this->updateCheckmateCount();
01980 this->recorder.finishSearch(search_move.move(), total_consumed,
01981 this->table->verboseLevel());
01982 throw;
01983 }
01984 last_iteration_consumed = this->elapsed() - total_consumed;
01985 total_consumed += last_iteration_consumed;
01986 this->updateCheckmateCount();
01987 this->recorder.finishSearch(search_move.move(), total_consumed,
01988 this->table->verboseLevel());
01989 this->shared_root->best_move_for_iteration.push_back(last_best_move);
01990 this->shared_root->root_values_for_iteration.push_back
01991 (this->shared_root->root_values.back());
01992
01993 if (last_best_move.isNormal())
01994 break;
01995 this->testStop();
01996
01997
01998 if (limit >= 2000 || this->root_ignore_moves)
01999 break;
02000
02001 limit += 200;
02002 if (this->table->isVerbose())
02003 std::cerr << " extend limit to " << limit << " before resign\n";
02004 }
02005 }
02006 catch (std::exception& e)
02007 {
02008 if (! OslConfig::usiMode())
02009 std::cerr << "std exception " << e.what() << "\n";
02010 }
02011 catch (...)
02012 {
02013 std::cerr << "unknown exception\n";
02014 #ifndef NDEBUG
02015 throw;
02016 #endif
02017 }
02018 finish:
02019 if (this->table->verboseLevel() > 1) {
02020 std::cerr << "<= " << record::csa::show(last_best_move);
02021 std::cerr << std::setprecision(4) << " mpn " << this->mpn.getAverage()
02022 << " cut " << this->mpn_cut.getAverage()
02023 << " alpha " << this->alpha_update.getAverage()
02024 << " last " << this->last_alpha_update.getAverage()
02025 << " ext " << this->ext.getAverage()
02026 << " ext_limit " << this->ext_limit.getAverage()
02027 << " mem " << OslConfig::memoryUseRatio()*100.0;
02028 #ifdef OSL_SMP
02029 # ifdef SPLIT_STAT
02030 if (this->shared) {
02031 std::cerr << " split " << this->shared->parallel_splits << " cancel " << this->shared->cancelled_splits.value()
02032 << " abort " << this->shared->parallel_abort.value();
02033 }
02034 # endif
02035 #endif
02036 std::cerr << "\n";
02037 }
02038
02039 if (additional_info) {
02040 additional_info->node_count = this->nodeCount();
02041 additional_info->elapsed = this->elapsed();
02042 additional_info->moves.clear();
02043 additional_info->root_limit = this->root_limit;
02044 }
02045 if (additional_info && this->shared_root->root_values.size() > 1) {
02046 assert(last_best_move == this->shared_root->last_root_move);
02047 additional_info->move = last_best_move;
02048 const double scale = 200.0/this->eval.captureValue(newPtypeO(WHITE,PAWN));
02049 additional_info->value = static_cast<int>(this->shared_root->last_root_value_update * scale);
02050 if (!this->shared_root->last_pv.empty()) {
02051 for (size_t i=1; i<this->shared_root->last_pv.back().pv.size(); ++i) {
02052 additional_info->moves.push_back(this->shared_root->last_pv.back().pv[i]);
02053 }
02054 }
02055 }
02056 return last_best_move;
02057 }
02058
02059 template <class EvalT>
02060 template <osl::Player P>
02061 int osl::search::AlphaBeta2<EvalT>::
02062 alphaBetaSearchRoot(Window window, MoveLogProb& best_move, int limit)
02063 {
02064 #ifndef GPSONE
02065 {
02066 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02067 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02068 this->monitors())
02069 monitor->newDepth(limit/200);
02070 }
02071 #endif
02072 assert(P == this->state().turn());
02073 assert(window.alpha(P) % 2);
02074 assert(window.beta(P) % 2);
02075 setRoot(limit);
02076 assert(this->curDepth() == 0);
02077 this->node_type[this->curDepth()] = base_t::PvNode;
02078 #ifdef NEW_DFPN
02079 this->checkmate_searcher->setRootPlayer(P);
02080 #endif
02081 #ifdef OSL_SMP
02082 if (this->shared)
02083 this->shared->threadStart();
02084 #endif
02085
02086 SimpleHashRecord *record_in_table
02087 = this->table->allocate(this->currentHash(), limit);
02088 SimpleHashRecord *record = record_in_table;
02089 boost::scoped_ptr<SimpleHashRecord> record_if_not_allocated;
02090 if (! record)
02091 {
02092 record_if_not_allocated.reset(new SimpleHashRecord());
02093 record = record_if_not_allocated.get();
02094 }
02095 assert(record);
02096 this->setRootRecord(record);
02097 assert(this->rootRecord() == record);
02098 assert(this->hasLastRecord() && this->lastRecord() == record);
02099 record->setInCheck(this->state().inCheck());
02100
02101 if (limit == 0) {
02102 int result = this->template quiesce<P>(fullWindow(P));
02103 best_move = MoveLogProb(record->qrecord.bestMove(), 100);
02104 if (this->root_ignore_moves
02105 && this->root_ignore_moves->isMember(best_move.move()))
02106 best_move = MoveLogProb();
02107 #ifndef GPSONE
02108 else if (this->hasMonitor() && !this->prediction_for_speculative_search)
02109 {
02110 const double scale = OslConfig::usiOutputPawnValue()*2.0
02111 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02112 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02113 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02114 this->monitors())
02115 monitor->showPV(1, this->recorder.allNodeCount(),
02116 this->elapsed(), static_cast<int>(result*scale),
02117 best_move.move(), 0, 0, 0, 0);
02118 }
02119 #endif
02120 return result;
02121 }
02122 if (record_in_table) {
02123 int table_value = 0;
02124 const MoveLogProb m = record_in_table->bestMove();
02125 if (! m.isNormal())
02126 record_in_table->resetValue();
02127 else if (record->hasGreaterLowerBound<P>(this->curLimit(), window.beta(P),
02128 table_value)) {
02129 if (! this->root_ignore_moves
02130 || ! this->root_ignore_moves->isMember(m.move())) {
02131 best_move = m;
02132 return table_value;
02133 }
02134 }
02135 }
02136
02137
02138 MoveLogProbVector moves;
02139 MoveGenerator& generator = this->makeGenerator();
02140 const MoveLogProb last_best_move = record->bestMove();
02141 {
02142 MoveLogProbVector raw_moves;
02143 assert(this->curLimit() > 0);
02144 const Move hash_move = last_best_move.isNormal()
02145 ? last_best_move.move() : record->qrecord.bestMove();
02146 generator.init(this->curLimit()+200, record, this->eval, this->state(), true, hash_move);
02147 if (last_best_move.isNormal())
02148 raw_moves.push_back(last_best_move);
02149 else if (record->qrecord.bestMove().isNormal())
02150 raw_moves.push_back(MoveLogProb(record->qrecord.bestMove(), 100));
02151 generator.generateAll<P>(*this, raw_moves);
02152
02153
02154 for (size_t i=0; i<raw_moves.size(); ++i) {
02155 const Move m = raw_moves[i].move();
02156 if (i > 0 && m == hash_move)
02157 continue;
02158 const HashKey key = this->currentHash().newHashWithMove(m);
02159 const SimpleHashRecord *record = this->table->find(key);
02160 assert(this->state().isValidMove(m));
02161 if (record) {
02162 if (record->hasUpperBound(SearchTable::HistorySpecialDepth)
02163 && this->isWinValue(alt(P), record->upperBound()))
02164 continue;
02165 }
02166 if (this->root_ignore_moves && this->root_ignore_moves->isMember(m))
02167 continue;
02168 if (! m.isDrop() && m.ptype() != KING
02169 && move_classifier::KingOpenMove<P>::isMember(this->state(), m.ptype(), m.from(), m.to()))
02170 continue;
02171 if (move_classifier::MoveAdaptor<move_classifier::PawnDropCheckmate<P> >
02172 ::isMember(this->state(), m))
02173 continue;
02174 raw_moves[i].setLogProbAtMost(limit);
02175 moves.push_back(raw_moves[i]);
02176 }
02177 }
02178
02179 if (! OslConfig::searchExactValueInOneReply()) {
02180 if (moves.size() == 1
02181 || (moves.size() == 2 && moves[0].move() == moves[1].move()))
02182 {
02183 best_move = moves[0];
02184 #ifndef GPSONE
02185 if (this->hasMonitor() && !this->prediction_for_speculative_search) {
02186 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02187 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02188 this->monitors())
02189 monitor->rootForcedMove(best_move.move());
02190 }
02191 #endif
02192 return 0;
02193 }
02194 }
02195
02196 #ifndef DONT_USE_CHECKMATE
02197
02198 int checkmate_node = 0;
02199 if (! this->prediction_for_speculative_search) {
02200 int checkmate_max = 30000*std::max(limit - 300 - this->rootLimitBias(), 0)/100;
02201 if (limit >= 1000 + this->rootLimitBias())
02202 checkmate_max = std::min(400000, 60000*(limit - 800 - this->rootLimitBias())/100);
02203 if (this->timeAssigned().standard.toSeconds() < 20) {
02204 checkmate_node /= 4;
02205 if (this->timeAssigned().standard.toSeconds() < 10)
02206 checkmate_node /= 2;
02207 }
02208 checkmate_node = record->qrecord.checkmateNodesLeft(checkmate_max);
02209 #ifdef CHECKMATE_COUNT
02210 std::cerr << "limit " << limit << " checkmate " << checkmate_node << "\n";
02211 #endif
02212 }
02213 if (checkmate_node > 0)
02214 {
02215 const bool my_king_in_check
02216 = this->state().hasEffectAt(alt(P),this->state().kingSquare(P));
02217 if (my_king_in_check)
02218 {
02219
02220 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node/8);
02221 const bool lose = this->template isLosingState<P>(checkmate_node/8);
02222 this->recorder.backFromCheckmateSearch();
02223 this->updateCheckmateCount();
02224 if (lose)
02225 {
02226 best_move = MoveLogProb(Move::INVALID(),100);
02227 this->recordLoseByCheckmate(P, record);
02228 this->shared_root->last_pv.clear();
02229 this->shared_root->last_root_move = Move();
02230 this->shared_root->last_root_value_update = this->winByCheckmate(alt(P));
02231 #ifndef GPSONE
02232 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02233 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02234 this->monitors())
02235 monitor->rootLossByCheckmate();
02236 #endif
02237 return this->winByCheckmate(alt(P));
02238 }
02239 }
02240
02241 {
02242 Move checkmate_move;
02243 #ifdef CHECKMATE_COUNT
02244 size_t count = this->checkmateSearcher().totalNodeCount();
02245 #endif
02246 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node);
02247 const bool win = this->template isWinningState<P>
02248 (checkmate_node, checkmate_move, limit >= 1000 + this->rootLimitBias());
02249 this->recorder.backFromCheckmateSearch();
02250 this->updateCheckmateCount();
02251 #ifdef CHECKMATE_COUNT
02252 root_checkmate += this->checkmateSearcher().totalNodeCount() - count;
02253 #endif
02254 if (win)
02255 {
02256 best_move = MoveLogProb(checkmate_move,100);
02257 this->recordWinByCheckmate(P, record, checkmate_move);
02258 this->shared_root->last_pv.clear();
02259 this->shared_root->last_root_move = checkmate_move;
02260 this->shared_root->last_root_value_update = this->winByCheckmate(P);
02261 this->pv[1].clear();
02262 this->updateRootPV(P, std::cerr, this->winByCheckmate(P), checkmate_move);
02263 return this->winByCheckmate(P);
02264 }
02265 }
02266
02267 if ((! my_king_in_check)
02268 && (! (record->threatmate().isThreatmate(P))))
02269 {
02270 Move threatmate_move;
02271 #ifdef CHECKMATE_COUNT
02272 size_t count = this->checkmateSearcher().totalNodeCount();
02273 #endif
02274 this->recorder.gotoCheckmateSearch(this->state(), checkmate_node);
02275 const bool threatmate
02276 = this->template isThreatmateState<P>
02277 (checkmate_node, threatmate_move, limit >= 1000 + this->rootLimitBias());
02278 #ifdef CHECKMATE_COUNT
02279 root_checkmate += this->checkmateSearcher().totalNodeCount() - count;
02280 #endif
02281 this->recorder.backFromCheckmateSearch();
02282 this->updateCheckmateCount();
02283 if (threatmate)
02284 {
02285 if (record)
02286 record->threatmate().setThreatmate(P, threatmate_move);
02287 if (this->table->verboseLevel() > 1)
02288 std::cerr << " root threatmate " << threatmate_move << "\n";
02289 }
02290 BOOST_FOREACH(Ptype ptype, PieceStand::order)
02291 {
02292 this->testStop();
02293 if (! this->state().hasPieceOnStand(P, ptype))
02294 continue;
02295 NumEffectState state(this->state().emulateHandPiece(P, alt(P), ptype));
02296 state.setTurn(alt(P));
02297 Move hand_move;
02298 this->template isWinningState<PlayerTraits<P>::opponent>
02299 (*this->checkmate_searcher, state, HashKey(state), PathEncoding(alt(P)),
02300 checkmate_node, hand_move, Move::PASS(P), limit >= 1000 + this->rootLimitBias());
02301 }
02302 }
02303 this->testStop();
02304 }
02305 this->checkmate_searcher->runGC(this->table->isVerbose(),
02306 this->lastMemoryUseRatio1000());
02307 #endif
02308 const int ValueNone = window.alpha(P) - EvalTraits<P>::delta;
02309 int best_value = ValueNone;
02310 try {
02311
02312 size_t i=0;
02313 if (limit >= 1000 && ! moves.empty() && window == fullWindow(P))
02314 {
02315
02316 const int root_alpha =
02317 this->rootAlpha(P, this->shared_root->root_values.size() ? this->shared_root->root_values.back() : 0,
02318 this->eval.progress16());
02319 if (EvalTraits<P>::betterThan(root_alpha, window.alpha(P))) {
02320 const Window window_copy = window;
02321 window.alpha(P) = root_alpha;
02322 #ifndef GPSONE
02323 {
02324 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02325 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02326 this->monitors())
02327 monitor->rootFirstMove(moves[0].move());
02328 }
02329 #endif
02330 const int result = this->template alphaBetaSearch<P>(moves[0], window, true);
02331 if (EvalTraits<P>::betterThan(result, root_alpha))
02332 {
02333 window.alpha(P) = result + EvalTraits<P>::delta;
02334 best_move = moves[0];
02335 best_value = result;
02336 this->updateRootPV(P, std::cerr, result, moves[0].move());
02337 ++i;
02338 }
02339 else
02340 {
02341 if (this->table->isVerbose())
02342 this->showFailLow(result, moves[0].move());
02343 this->setStable(false);
02344 window = window_copy;
02345 }
02346 this->checkmate_searcher->runGC(this->table->isVerbose(),
02347 this->lastMemoryUseRatio1000());
02348 }
02349 }
02350 for (; i<moves.size() && best_value == ValueNone
02351 && window == fullWindow(P); ++i) {
02352 const MoveLogProb& m = moves[i];
02353 #ifndef GPSONE
02354 {
02355 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02356 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02357 this->monitors())
02358 monitor->rootMove(m.move());
02359 }
02360 #endif
02361 const int result = this->template alphaBetaSearch<P>(m, window, true);
02362 if (eval::betterThan(P, result, best_value)) {
02363 window.alpha(P) = result + EvalTraits<P>::delta;
02364 best_move = m;
02365 best_value = result;
02366 this->updateRootPV(P, std::cerr, result, m.move());
02367 if (eval::betterThan(P, result, window.beta(P))) {
02368 assert(! this->isWinValue(alt(P), result));
02369 }
02370 }
02371 else if (result == ValueNone)
02372 this->setStable(false);
02373 this->checkmate_searcher->runGC(this->table->isVerbose(),
02374 this->lastMemoryUseRatio1000());
02375 }
02376
02377 if (! eval::betterThan(P, window.alpha(P), window.beta(P))) {
02378 this->template examineMovesRoot<P>(moves, i, window, best_move, best_value);
02379 }
02380 if (best_move.isNormal()) {
02381 if (best_value != ValueNone) {
02382 assert(! this->shared_root->last_pv.empty());
02383 assert(best_move.move() == this->shared_root->last_pv.back().pv[0]);
02384 }
02385 }
02386 #ifndef GPSONE
02387 {
02388 boost::mutex::scoped_lock lk(OslConfig::lock_io);
02389 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02390 this->monitors())
02391 monitor->depthFinishedNormally(limit/200);
02392 }
02393 #endif
02394 } catch (std::runtime_error& e) {
02395 if (this->table->isVerbose())
02396 std::cerr << e.what() << "\n";
02397 assert(best_value % 2 == 0);
02398 this->stopNow();
02399 this->restoreRootState();
02400 if (best_value != ValueNone)
02401 record->setLowerBound(P, this->curLimit(), best_move, best_value);
02402 if (best_move.validMove()
02403 && best_move.move() != last_best_move.move()) {
02404 if (this->table->verboseLevel() > 1) {
02405 std::cerr << "! use better move than the last best move\n";
02406 if (best_value != ValueNone) {
02407 assert(! this->shared_root->last_pv.empty() &&
02408 ! this->shared_root->last_pv.back().pv.empty());
02409 assert(best_move.move() == this->shared_root->last_pv.back().pv[0]);
02410 }
02411 }
02412 }
02413 else {
02414 #ifdef OSL_SMP
02415 if (this->shared)
02416 this->shared->waitAll();
02417 #endif
02418 throw;
02419 }
02420 }
02421
02422 assert(best_value % 2 == 0);
02423 if (best_value != ValueNone)
02424 record->setLowerBound(P, this->curLimit(), best_move, best_value);
02425 #ifdef OSL_SMP
02426 if (this->shared)
02427 this->shared->waitAll();
02428 #endif
02429 #ifndef GPSONE
02430 if (best_value == ValueNone
02431 && this->hasMonitor() && !this->prediction_for_speculative_search)
02432 {
02433 const double scale = OslConfig::usiOutputPawnValue()*2.0
02434 / this->eval.captureValue(newPtypeO(alt(P),PAWN));
02435 const int value = this->winByCheckmate(alt(P));
02436 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
02437 this->monitors())
02438 monitor->showPV(limit/200, this->recorder.allNodeCount(),
02439 this->elapsed(), static_cast<int>(value*scale),
02440 Move::INVALID(), 0, 0, 0, 0);
02441 }
02442 #endif
02443 return best_value;
02444 }
02445
02446 template <class EvalT>
02447 void osl::search::AlphaBeta2<EvalT>::setRoot(int limit)
02448 {
02449 SearchState2::setRoot(limit);
02450 SimpleHashRecord *record = this->table->allocate(this->currentHash(), std::max(1000,limit));
02451 assert(record);
02452 this->setRootRecord(record);
02453 this->move_type[this->curDepth()] = base_t::INITIAL;
02454 }
02455
02456 template <class EvalT>
02457 void osl::search::AlphaBeta2<EvalT>::makeMove(Move move)
02458 {
02459 assert(this->state().isValidMove(move));
02460 SearchState2::makeMove(move);
02461 this->eval.update(this->state(), move);
02462
02463 SimpleHashRecord *record
02464 = this->table->allocate(this->currentHash(), this->curLimit());
02465 assert(record);
02466 this->move_type[this->curDepth()] = base_t::INITIAL;
02467 record->setInCheck(this->state().inCheck());
02468 this->setCurrentRecord(record);
02469 }
02470
02471 template <class EvalT>
02472 bool osl::search::AlphaBeta2<EvalT>::
02473 isReasonableMove(Move , int )
02474 {
02475 return true;
02476 }
02477
02478 template <class EvalT>
02479 void osl::search::AlphaBeta2<EvalT>::
02480 showNodeDepth(std::ostream& os)
02481 {
02482 #ifndef MINIMAL
02483 int max_depth=0;
02484 for (int i=base_t::MaxDepth-1; i>=0; --i) {
02485 if (base_t::depth_node_count[i] || base_t::depth_node_count_quiesce[i]) {
02486 max_depth = i;
02487 break;
02488 }
02489 }
02490 int max_count=0;
02491 for (int i=0; i<=max_depth; i+=2) {
02492 max_count = std::max(max_count,
02493 base_t::depth_node_count[i]+base_t::depth_node_count_quiesce[i]);
02494 }
02495
02496 int unit = std::max(max_count/79, 100);
02497 for (int i=0; i<=max_depth; i+=2) {
02498 os << std::setw(3) << i << " "
02499 << std::string(base_t::depth_node_count[i]/unit, '*')
02500 << std::string(base_t::depth_node_count_quiesce[i]/unit, '+')
02501 << std::endl;
02502 }
02503 # ifdef CHECKMATE_COUNT
02504 std::cerr << "checkmate root " << root_checkmate << " quiesce " << quiesce_checkmate
02505 << "\nnormal before " << checkmate_before
02506 << " after " << checkmate_after << " threatmate " << count_threatmate
02507 << "\n";
02508 # endif
02509 #endif
02510 }
02511
02512 template <class EvalT>
02513 void osl::search::AlphaBeta2<EvalT>::
02514 clearNodeDepth()
02515 {
02516 #ifndef MINIMAL
02517 base_t::depth_node_count.fill(0);
02518 base_t::depth_node_count_quiesce.fill(0);
02519 #endif
02520 }
02521
02522 namespace osl
02523 {
02524 namespace search
02525 {
02526 #ifndef MINIMAL
02527 template class AlphaBeta2<eval::ProgressEval>;
02528 template class AlphaBeta2Tree<eval::ProgressEval>;
02529 template
02530 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRoot<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02531 template
02532 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRoot<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02533 #endif
02534 template class AlphaBeta2<eval::ml::OpenMidEndingEval>;
02535 template class AlphaBeta2Tree<eval::ml::OpenMidEndingEval>;
02536 template
02537 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRoot<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02538 template
02539 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRoot<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
02540 }
02541 }
02542
02543
02544
02545
02546
02547