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