00001
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(4), draw_coef(-1),
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
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
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().getTurn());
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 )
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
00275
00276
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 const int seconds_for_this_move
00283 = search::TimeControl::secondsForThisMove(time_left);
00284
00285
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)
00298 {
00299
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
00312 return std::max(byoyomi, seconds_for_this_move);
00313 }
00314
00315 const osl::search::TimeAssigned osl::game_playing::
00316 SearchPlayer::assignTime(const GameState& state, int limit, int elapsed,
00317 int byoyomi) const
00318 {
00319 return assignTime(state, limit, elapsed, byoyomi,
00320 table_ptr.get() ? table_ptr->verboseLevel() : 0);
00321 }
00322
00323 const osl::search::TimeAssigned osl::game_playing::
00324 SearchPlayer::assignTime(const GameState& state, int limit, int elapsed,
00325 int byoyomi, int verboseness)
00326 {
00327 const int seconds_for_this_move
00328 = secondsForThisMove(state, limit, elapsed, byoyomi, verboseness);
00329 const int seconds_max = (byoyomi && (limit - elapsed) < byoyomi)
00330 ? seconds_for_this_move
00331 : std::min(seconds_for_this_move*5, std::max(seconds_for_this_move, (limit-elapsed)/2));
00332 return search::TimeAssigned(MilliSeconds::Interval(seconds_for_this_move*1000),
00333 MilliSeconds::Interval(seconds_max*1000));
00334 }
00335
00336 const osl::search::MoveWithComment osl::game_playing::
00337 SearchPlayer::selectBestMove(const GameState& state, int limit, int elapsed,
00338 int byoyomi)
00339 {
00340 return selectBestMoveInTime(state, assignTime(state, limit, elapsed, byoyomi));
00341 }
00342
00343 const osl::search::MoveWithComment osl::game_playing::
00344 SearchPlayer::selectBestMoveInTime(const GameState& state, const search::TimeAssigned& msec)
00345 {
00346 if (EnterKing::canDeclareWin(state.state()))
00347 return MoveWithComment(Move::DeclareWin());
00348 if (msec.standard == msec.max
00349 && config.next_iteration_coefficient > 1.0)
00350 setNextIterationCoefficient(1.0);
00351 return searchWithSecondsForThisMove(state, msec);
00352 }
00353
00354 void osl::game_playing::
00355 SearchPlayer::saveSearchResult(const GameState& state, const MoveWithComment& best_move)
00356 {
00357 (*pv_history)[state.moveHistory().size() % pv_history->size()] = best_move;
00358 }
00359
00360 void osl::game_playing::
00361 SearchPlayer::setTimeAssign(const search::TimeAssigned& new_assign)
00362 {
00363 if (searcher)
00364 {
00365 searcher->setTimeAssign(new_assign);
00366 }
00367 }
00368 const osl::MilliSeconds osl::game_playing::
00369 SearchPlayer::startTime() const
00370 {
00371 if (searcher)
00372 {
00373 return searcher->startTime();
00374 }
00375 return MilliSeconds();
00376 }
00377
00378 #ifdef USE_NTESUKI
00379 osl::game_playing::SearchPlayer::
00380 NtesukiThread::NtesukiThread(Move& next_move,
00381 volatile bool *thread_finished,
00382 volatile bool *stop_flag,
00383 NumEffectState state)
00384 : next_move(next_move), thread_finished(thread_finished),
00385 stop_flag(stop_flag), state(state)
00386 {
00387 }
00388
00389 void osl::game_playing::SearchPlayer::
00390 NtesukiThread::operator()()
00391 {
00392 std::cerr << "start ntesuki search\n";
00393 *thread_finished = false;
00394
00395 const Player P = state.getTurn();
00396 const HashKey key = osl::HashKey::calcHash(state);;
00397
00398 boost::scoped_ptr<osl::ntesuki::NtesukiAttackMoveGenerator>
00399 gam(new osl::ntesuki::GetAttackMoves());
00400 boost::scoped_ptr<osl::ntesuki::NtesukiDefenseMoveGenerator>
00401 gdm(new osl::ntesuki::GetDefenseMoves());
00402
00403 osl::ntesuki::NtesukiSearcher
00404 searcher(state, gam.get(), gdm.get(), 500000u, stop_flag, true, 2);
00405
00406 try
00407 {
00408 int ntesuki_num = searcher.searchSlow(P, 10000000);
00409 if (-1 != ntesuki_num)
00410 {
00411 const osl::ntesuki::PdNtesukiTable& table
00412 = searcher.getTableSlow(P);
00413 const osl::ntesuki::PdNtesukiRecord *record
00414 = table.find(key);
00415 next_move = record->getBestMove(ntesuki_num).getMove();
00416 }
00417 }
00418 catch (ntesuki::ReadNodeLimit& e)
00419 {
00420 }
00421 catch (ntesuki::TableFull& e)
00422 {
00423 }
00424 catch (std::runtime_error& e)
00425 {
00426 std::cerr << e.what() << "\n";
00427 }
00428 std::cerr << "end ntesuki search\n";
00429 *thread_finished = true;
00430 }
00431 #endif
00432
00433
00434
00435
00436