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