searchPlayer.cc
Go to the documentation of this file.
00001 /* searchPlayer.cc
00002  */
00003 #include "osl/game_playing/searchPlayer.h"
00004 #include "osl/game_playing/searchPlayer.tcc"
00005 #include "osl/game_playing/historyToTable.h"
00006 #include "osl/game_playing/gameState.h"
00007 #include "osl/game_playing/pvHistory.h"
00008 #include "osl/search/searchRecorder.h"
00009 #include "osl/search/simpleHashRecord.h"
00010 #include "osl/search/simpleHashTable.h"
00011 #include "osl/search/timeControl.h"
00012 #include "osl/search/searchTimer.h"
00013 #include "osl/search/fixedEval.h"
00014 #include "osl/search/bigramKillerMove.h"
00015 #include "osl/enter_king/enterKing.h"
00016 #include "osl/progress/effect5x3.h"
00017 #include "osl/container/moveStack.h"
00018 #include "osl/hash/hashRandom.h"
00019 #include "osl/misc/nonBlockDelete.h"
00020 #include "osl/misc/ctime.h"
00021 #include <iostream>
00022 #include <ctime>
00023 #include <cmath>
00024 
00025 osl::game_playing::SearchPlayer::
00026 Config::Config()
00027   : limit(1200), node_limit(800000), table_size(10000),
00028     table_record_limit(0), initial_limit(600), deepening_step(200),
00029     verbose(0), next_iteration_coefficient(3), draw_coef(0),
00030     save_pv(true), node_count_hard_limit(std::numeric_limits<uint64_t>::max()),
00031     multi_pv_width(0)
00032 {
00033 }
00034 
00035 bool osl::game_playing::operator==(const SearchPlayer::Config& l, 
00036                                    const SearchPlayer::Config& r)
00037 {
00038   return (l.limit == r.limit) && (l.node_limit == r.node_limit)
00039     && (l.table_size == r.table_size) 
00040     && (l.table_record_limit == r.table_record_limit)
00041     && (l.initial_limit == r.initial_limit)
00042     && (l.deepening_step == r.deepening_step)
00043     && (l.verbose == r.verbose)
00044     && (l.next_iteration_coefficient == r.next_iteration_coefficient)
00045     && (l.draw_coef == r.draw_coef);
00046 }
00047 
00048 osl::game_playing::
00049 SearchPlayer::SearchPlayer()
00050   : recorder_ptr(new CountRecorder()),
00051     searching(0),
00052     plan_stop(false), root_ignore_moves(0), 
00053     prediction_for_speculative_search(0), 
00054     pv_history(new PVHistory)
00055 {
00056 }
00057 
00058 osl::game_playing::
00059 SearchPlayer::SearchPlayer(const SearchPlayer& copy)
00060   : ComputerPlayer(copy), ComputerPlayerSelectBestMoveInTime(copy),
00061     config(copy.config),
00062     recorder_ptr(new CountRecorder()),
00063     searching(0), plan_stop(false),
00064     root_ignore_moves(0), prediction_for_speculative_search(0),
00065     pv_history(new PVHistory(*copy.pv_history))
00066 {
00067 }
00068 
00069 osl::game_playing::
00070 SearchPlayer::~SearchPlayer()
00071 {
00072 }
00073 
00074 void osl::game_playing::
00075 SearchPlayer::swapTable(SearchPlayer& other)
00076 {
00077   table_ptr.swap(other.table_ptr);
00078 }
00079 
00080 bool osl::game_playing::
00081 SearchPlayer::canStopSearch()
00082 {
00083   return searching;
00084 }
00085 
00086 bool osl::game_playing::
00087 SearchPlayer::stopSearchNow()
00088 {
00089   plan_stop = true;
00090   if (! searching)
00091   {
00092     std::cerr << "SearchPlayer not searching ";
00093     const time_t now = time(0);
00094     char ctime_buf[64];
00095     std::cerr << ctime_r(&now, ctime_buf);
00096     return false;
00097   }
00098   searcher->stopNow();
00099   return true;
00100 }
00101 
00102 void osl::game_playing::
00103 SearchPlayer::resetRecorder(search::CountRecorder *new_recorder)
00104 {
00105   recorder_ptr.reset(new_recorder);
00106 }
00107 
00108 const osl::search::TimeAssigned osl::game_playing::
00109 SearchPlayer::adjust(const search::TimeAssigned& org, const MilliSeconds::Interval& consumed)
00110 {
00111   if (consumed < org.standard/8)
00112     return search::TimeAssigned(org.standard - consumed, org.max - consumed);
00113   // spent too much seconds in preparation
00114   search::SearchTimer::adjustMemoryUseLimit(0.9);
00115     return search::TimeAssigned(std::min(org.standard - org.standard/8, org.max - consumed),
00116                                 org.max - consumed);
00117 }
00118 
00119 const osl::MilliSeconds::Interval osl::game_playing::
00120 SearchPlayer::setUpTable(const GameState& gs, int pawn_value)
00121 {
00122   const MilliSeconds started = MilliSeconds::now();
00123   time_t t = time(0);
00124   char ctime_buf[64];
00125   if (table_ptr.get() && table_ptr->verboseLevel() > 2)
00126   {
00127     std::cerr << "setUpTable " << ctime_r(&t, ctime_buf)
00128               << std::flush;
00129   }
00130   
00131   // release cherkmate_ptr first
00132   NonBlockDelete::reset(checkmate_ptr);
00133 
00134   const int black_win = search::FixedEval::winByLoop(BLACK);
00135   const int white_win = search::FixedEval::winByLoop(WHITE);
00136   if (table_ptr.get()) {
00137     table_ptr->clear();
00138   }
00139   else {
00140     try 
00141     {
00142       table_ptr.reset(new SimpleHashTable(config.table_size, 
00143                                           config.table_record_limit, config.verbose));
00144     }
00145     catch (std::bad_alloc&)
00146     {
00147       NonBlockDelete::deleteAll();
00148       std::cerr << "\atable allocation failed, try agaian" << std::endl;
00149       table_ptr.reset(new SimpleHashTable(config.table_size, 
00150                                           config.table_record_limit, config.verbose));
00151     }
00152   }
00153   table_ptr->setVerbose(config.verbose);
00154   NonBlockDelete::deleteAll();
00155 
00156   HistoryToTable::adjustTable(gs, *table_ptr, black_win, config.draw_coef*pawn_value, white_win);
00157   if (config.save_pv)
00158     HistoryToTable::setPV(*pv_history, gs, *table_ptr);
00159   try 
00160   {
00161     checkmate_ptr.reset(new DualDfpn());
00162   }
00163   catch (std::bad_alloc&)
00164   {
00165     NonBlockDelete::deleteAll();
00166     std::cerr << "\acheckmate allocation failed, try agaian" << std::endl;
00167     checkmate_ptr.reset(new DualDfpn());
00168   }
00169   checkmate_ptr->writeRootHistory(gs.counter(), gs.moveHistory(),
00170                                   gs.state(), gs.state().turn());
00171 
00172   if (table_ptr->verboseLevel() > 2)
00173   {
00174     t = time(0);
00175     std::cerr << "setup done in " << (MilliSeconds::now() - started).toSeconds()
00176               << " sec.  " << ctime_r(&t, ctime_buf) 
00177               << std::flush;
00178   }
00179 #ifndef MINIMAL
00180   if (OslConfig::evalRandom())
00181     HashRandom::setUp(1.0*OslConfig::evalRandom()*pawn_value/100);
00182 #endif
00183   return MilliSeconds::now() - started;
00184 }
00185 
00186 void osl::game_playing::
00187 SearchPlayer::setDepthLimit(int l, int il, int ds)
00188 {
00189   config.limit = l;
00190   config.initial_limit = il;
00191   config.deepening_step = ds;
00192 }
00193 
00194 void osl::game_playing::
00195 SearchPlayer::setNodeLimit(size_t nl)
00196 {
00197   config.node_limit = nl;
00198 }
00199 
00200 void osl::game_playing::
00201 SearchPlayer::setNodeCountHardLimit(size_t nl)
00202 {
00203   config.node_count_hard_limit = nl;
00204 }
00205 
00206 void osl::game_playing::
00207 SearchPlayer::setTableLimit(size_t size, int record_limit)
00208 {
00209   config.table_size = size;
00210   config.table_record_limit = record_limit;
00211 
00212   table_ptr.reset();
00213 }
00214 
00215 void osl::game_playing::
00216 SearchPlayer::setVerbose(int v)
00217 {
00218   config.verbose = v;
00219   if (table_ptr)
00220     table_ptr->setVerbose(v);
00221 }
00222 
00223 void osl::game_playing::
00224 SearchPlayer::setNextIterationCoefficient(double new_value)
00225 {
00226   config.next_iteration_coefficient = new_value;
00227   if (searcher)
00228     searcher->setNextIterationCoefficient(new_value);
00229 }
00230 
00231 void osl::game_playing::
00232 SearchPlayer::addMonitor(const boost::shared_ptr<search::SearchMonitor>& m)
00233 {
00234   config.monitors.push_back(m);
00235 }
00236 
00237 
00238 void osl::game_playing::
00239 SearchPlayer::pushMove(Move /*move*/)
00240 {
00241   NonBlockDelete::reset(checkmate_ptr);
00242   if (speculative_search_allowed)
00243     NonBlockDelete::reset(table_ptr);
00244 }
00245 void osl::game_playing::
00246 SearchPlayer::popMove()
00247 {
00248   NonBlockDelete::reset(checkmate_ptr);
00249   if (speculative_search_allowed)
00250     NonBlockDelete::reset(table_ptr);
00251 }
00252 
00253 int osl::game_playing::
00254 SearchPlayer::secondsForThisMove(const GameState& state, int time_limit, int elapsed,
00255                                  int byoyomi) const
00256 {
00257   return secondsForThisMove(state, time_limit, elapsed, byoyomi,
00258                             table_ptr.get() ? table_ptr->verboseLevel() : 0);
00259 }
00260 
00261 int osl::game_playing::
00262 SearchPlayer::secondsForThisMove(const GameState& state, int time_limit, int elapsed,
00263                                  int byoyomi, int verboseness)
00264 {
00265   if (byoyomi < 0)
00266     return -1; // 無限
00267 
00268   if (time_limit - elapsed < byoyomi)
00269     return byoyomi;
00270   const int time_limit_org = time_limit;
00271   const int moves = state.moveHistory().size();
00272   if (byoyomi == 0)
00273   {
00274     // 330手指せるようにする
00275     // http://www32.ocn.ne.jp/~yss/csa14rep.html
00276     // 90秒から1秒将棋に入るので240で良い (330-90)
00277     time_limit -= std::max(0, (240 - moves));
00278   }
00279   const int time_left = time_limit - elapsed;
00280   if (time_left <= 1)
00281     return 1;
00282   int seconds_for_this_move 
00283     = search::TimeControl::secondsForThisMove(time_left);
00284 
00285   // Think more if book leads us to near endgame
00286   const progress::Effect5x3 progress(state.state());
00287   if (time_left >= 600*time_limit_org/1500)
00288   {
00289     if ((progress.progress16().value() >= 15)
00290         && ((progress.progress16(BLACK).value() >= 13)
00291             || (progress.progress16(WHITE).value() >= 13))) {
00292       if (verboseness)
00293         std::cerr << "  time control endgame ext\n";
00294       return seconds_for_this_move*2;
00295     }
00296   }
00297   if (byoyomi == 0 || time_left >= byoyomi*60) 
00298   {
00299     // do not think too much in opening
00300     if (progress.progress16().value() == 0) {
00301       if (verboseness)
00302         std::cerr << "  time control progress0 limit " << 25*time_limit_org/1500 << "\n";
00303       return std::min(std::max(1, 25*time_limit_org/1500), seconds_for_this_move);
00304     }
00305     if (progress.progress16().value() <= 3 && moves <= 40) {
00306       if (verboseness)
00307         std::cerr << "  time control progress4 limit " << 38*time_limit_org/1500 << "\n";
00308       return std::min(std::max(1, 38*time_limit_org/1500), seconds_for_this_move);
00309     }
00310   }
00311   // others
00312   seconds_for_this_move += byoyomi;
00313   if (byoyomi >= 10 && time_left >= byoyomi*2)
00314     seconds_for_this_move += byoyomi/2;
00315   return seconds_for_this_move;
00316 }
00317 
00318 const osl::search::TimeAssigned osl::game_playing::
00319 SearchPlayer::assignTime(const GameState& state, int limit, int elapsed,
00320                          int byoyomi) const
00321 {
00322   return assignTime(state, limit, elapsed, byoyomi, 
00323                     table_ptr.get() ? table_ptr->verboseLevel() : 0);
00324 }
00325 
00326 const osl::search::TimeAssigned osl::game_playing::
00327 SearchPlayer::assignTime(const GameState& state, int limit, int elapsed,
00328                          int byoyomi, int verboseness)
00329 {
00330   const int seconds_for_this_move
00331     = secondsForThisMove(state, limit, elapsed, byoyomi, verboseness);
00332   const int seconds_max = (byoyomi && (limit - elapsed) < byoyomi)
00333     ? seconds_for_this_move
00334     : std::min(seconds_for_this_move*5, std::max(seconds_for_this_move, (limit-elapsed)/2));
00335   return search::TimeAssigned(MilliSeconds::Interval(seconds_for_this_move*1000),
00336                               MilliSeconds::Interval(seconds_max*1000));
00337 }
00338 
00339 const osl::search::MoveWithComment osl::game_playing::
00340 SearchPlayer::selectBestMove(const GameState& state, int limit, int elapsed,
00341                              int byoyomi)
00342 {
00343   return selectBestMoveInTime(state, assignTime(state, limit, elapsed, byoyomi));
00344 }
00345 
00346 const osl::search::MoveWithComment osl::game_playing::
00347 SearchPlayer::selectBestMoveInTime(const GameState& state, const search::TimeAssigned& msec)
00348 {
00349   if (EnterKing::canDeclareWin(state.state()))
00350     return MoveWithComment(Move::DeclareWin());
00351   if (msec.standard == msec.max
00352       && config.next_iteration_coefficient > 1.0)
00353     setNextIterationCoefficient(1.0);
00354   return searchWithSecondsForThisMove(state, msec);
00355 }
00356 
00357 void osl::game_playing::
00358 SearchPlayer::saveSearchResult(const GameState& state, const MoveWithComment& best_move)
00359 {
00360   (*pv_history)[state.moveHistory().size() % pv_history->size()] = best_move;
00361 }
00362 
00363 void osl::game_playing::
00364 SearchPlayer::setTimeAssign(const search::TimeAssigned& new_assign)
00365 {
00366   if (searcher)
00367   {
00368     searcher->setTimeAssign(new_assign);
00369   }
00370 }
00371 const osl::MilliSeconds osl::game_playing::
00372 SearchPlayer::startTime() const
00373 {
00374   if (searcher)
00375   {
00376     return searcher->startTime();
00377   }
00378   return MilliSeconds();
00379 }
00380 
00381 #ifdef USE_NTESUKI
00382 osl::game_playing::SearchPlayer::
00383 NtesukiThread::NtesukiThread(Move& next_move,
00384               volatile bool *thread_finished,
00385               volatile bool *stop_flag,
00386               NumEffectState state)
00387   : next_move(next_move), thread_finished(thread_finished),
00388     stop_flag(stop_flag), state(state)
00389 {
00390 }
00391 
00392 void osl::game_playing::SearchPlayer::
00393 NtesukiThread::operator()()
00394 {
00395   std::cerr << "start ntesuki search\n";
00396   *thread_finished = false;
00397 
00398   const Player P = state.turn();
00399   const HashKey key = osl::HashKey::calcHash(state);;
00400 
00401   boost::scoped_ptr<osl::ntesuki::NtesukiAttackMoveGenerator>
00402     gam(new osl::ntesuki::GetAttackMoves());
00403   boost::scoped_ptr<osl::ntesuki::NtesukiDefenseMoveGenerator>
00404     gdm(new osl::ntesuki::GetDefenseMoves());
00405 
00406   osl::ntesuki::NtesukiSearcher
00407     searcher(state, gam.get(), gdm.get(), 500000u, stop_flag, true, 2);
00408    
00409   try
00410   {
00411     int ntesuki_num = searcher.searchSlow(P, 10000000);
00412     if (-1 != ntesuki_num)
00413     {
00414       const osl::ntesuki::PdNtesukiTable& table
00415         = searcher.getTableSlow(P);
00416       const osl::ntesuki::PdNtesukiRecord *record
00417         = table.find(key);
00418       next_move = record->getBestMove(ntesuki_num).move();
00419     }
00420   }
00421   catch (ntesuki::ReadNodeLimit& e)
00422   {
00423   }
00424   catch (ntesuki::TableFull& e)
00425   {
00426   }
00427   catch (std::runtime_error& e)
00428   {
00429     std::cerr << e.what() << "\n";
00430   }
00431   std::cerr << "end ntesuki search\n";
00432   *thread_finished = true;
00433 }
00434 #endif
00435 /* ------------------------------------------------------------------------- */
00436 // ;;; Local Variables:
00437 // ;;; mode:c++
00438 // ;;; c-basic-offset:2
00439 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines