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