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/tableStack.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/fixedEval.h"
00013 #include "osl/search/bigramKillerMove.h"
00014 #include "osl/search/searchMoveList.h"
00015 #include "osl/search/analyzer/tableAnalyzer.h"
00016 #include "osl/checkmate/checkmateSearcher.h"
00017 #include "osl/enter_king/enterKing.h"
00018 #include "osl/progress/effect5x3.h"
00019 #include "osl/container/moveStack.h"
00020 #include "osl/misc/realTime.h"
00021 #include "osl/misc/nonBlockDelete.h"
00022 #include <iostream>
00023 #include <ctime>
00024 #include <cmath>
00025
00026 osl::game_playing::SearchPlayer::
00027 Config::Config()
00028 : limit(1200), node_limit(800000), table_size(10000),
00029 table_record_limit(0), initial_limit(600), deepening_step(200),
00030 total_checkmate_limit(CHECKMATE_DEFAULT_TOTAL_NODE_LIMIT),
00031 verbose(0), next_iteration_coefficient(4), draw_coef(-1)
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.total_checkmate_limit == r.total_checkmate_limit)
00044 && (l.verbose == r.verbose)
00045 && (l.next_iteration_coefficient == r.next_iteration_coefficient)
00046 && (l.draw_coef == r.draw_coef);
00047 }
00048
00049 osl::game_playing::
00050 SearchPlayer::SearchPlayer()
00051 : recorder_ptr(new SearchRecorder("/dev/null")),
00052 bigram_ptr(new search::BigramKillerMove()),
00053 table_stack_ptr(new TableStack()),
00054 key_of_move(new KeyOfMove()),
00055 stop_flag(0),
00056 plan_stop(false), root_ignore_moves(0)
00057 {
00058 }
00059
00060 osl::game_playing::
00061 SearchPlayer::SearchPlayer(const SearchPlayer& copy)
00062 : ComputerPlayer(copy), config(copy.config),
00063 recorder_ptr(new SearchRecorder("/dev/null")),
00064 bigram_ptr(new search::BigramKillerMove()),
00065 table_stack_ptr(),
00066 key_of_move(new KeyOfMove()),
00067 stop_flag(0), plan_stop(false),
00068 root_ignore_moves(0)
00069 {
00070 if (copy.table_stack_ptr)
00071 table_stack_ptr.reset(new TableStack(*copy.table_stack_ptr));
00072 }
00073
00074 osl::game_playing::
00075 SearchPlayer::~SearchPlayer()
00076 {
00077 }
00078
00079 void osl::game_playing::
00080 SearchPlayer::enableTableStack(bool enable)
00081 {
00082 if (enable)
00083 {
00084 if (! table_stack_ptr)
00085 table_stack_ptr.reset(new TableStack());
00086 }
00087 else
00088 {
00089 table_stack_ptr.reset();
00090 }
00091 }
00092
00093 void osl::game_playing::
00094 SearchPlayer::swapTable(SearchPlayer& other)
00095 {
00096 table_ptr.swap(other.table_ptr);
00097 }
00098
00099 bool osl::game_playing::
00100 SearchPlayer::canStopSearch()
00101 {
00102 return stop_flag.notify;
00103 }
00104
00105 bool osl::game_playing::
00106 SearchPlayer::stopSearchNow()
00107 {
00108 plan_stop = true;
00109 if (! stop_flag.notify)
00110 {
00111 std::cerr << "SearchPlayer stop_flag is null ";
00112 const time_t now = time(0);
00113 char ctime_buf[64];
00114 std::cerr << ctime_r(&now, ctime_buf);
00115 return false;
00116 }
00117 *stop_flag.notify = Alarm::TIMEOUT;
00118 return true;
00119 }
00120
00121 void osl::game_playing::
00122 SearchPlayer::setTotalCheckmateLimit(int limit)
00123 {
00124 config.total_checkmate_limit = limit;
00125 }
00126
00127 void osl::game_playing::
00128 SearchPlayer::resetRecorder(search::SearchRecorder *new_recorder)
00129 {
00130 recorder_ptr.reset(new_recorder);
00131 }
00132
00133 int osl::game_playing::
00134 SearchPlayer::setUpTable(const GameState& gs, int pawn_value)
00135 {
00136 misc::RealTime timer(0);
00137 time_t t = time(0);
00138 char ctime_buf[64];
00139 if (table_ptr.get() && table_ptr->verboseLevel() > 1)
00140 std::cerr << "setUpTable " << ctime_r(&t, ctime_buf) << std::flush;
00141
00142
00143 NonBlockDelete::reset(checkmate_ptr);
00144
00145 const int black_win = search::FixedEval::winByLoop(BLACK);
00146 const int white_win = search::FixedEval::winByLoop(WHITE);
00147 if (table_ptr.get()) {
00148 table_ptr->clear();
00149 }
00150 else {
00151 try
00152 {
00153 table_ptr.reset(new SimpleHashTable(config.table_size,
00154 config.table_record_limit, config.verbose));
00155 }
00156 catch (std::bad_alloc&)
00157 {
00158 enableTableStack(false);
00159 NonBlockDelete::deleteAll();
00160 std::cerr << "\atable allocation failed, try agaian" << std::endl;
00161 table_ptr.reset(new SimpleHashTable(config.table_size,
00162 config.table_record_limit, config.verbose));
00163 }
00164 }
00165 table_ptr->setVerbose(config.verbose);
00166 if (table_stack_ptr)
00167 table_stack_ptr->migrate(*table_ptr);
00168
00169 HistoryToTable::adjustTable(gs, *table_ptr, black_win, config.draw_coef*pawn_value, white_win);
00170 try
00171 {
00172 checkmate_ptr.reset(new CheckmateSearcher(config.total_checkmate_limit));
00173 }
00174 catch (std::bad_alloc&)
00175 {
00176 enableTableStack(false);
00177 NonBlockDelete::deleteAll();
00178 std::cerr << "\acheckmate allocation failed, try agaian" << std::endl;
00179 checkmate_ptr.reset(new CheckmateSearcher(config.total_checkmate_limit));
00180 }
00181 checkmate_ptr->writeRootHistory(gs.counter(), gs.moveHistory(),
00182 gs.state(), gs.state().getTurn());
00183
00184 if (table_ptr->verboseLevel() > 1)
00185 {
00186 t = time(0);
00187 std::cerr << "setup done " << ctime_r(&t, ctime_buf) << std::flush;
00188 }
00189 return static_cast<int>(ceil(timer.getConsumedInDouble()));
00190 }
00191
00192 void osl::game_playing::
00193 SearchPlayer::setDepthLimit(int l, int il, int ds)
00194 {
00195 config.limit = l;
00196 config.initial_limit = il;
00197 config.deepening_step = ds;
00198 }
00199
00200 void osl::game_playing::
00201 SearchPlayer::setNodeLimit(int nl)
00202 {
00203 config.node_limit = nl;
00204 }
00205
00206 void osl::game_playing::
00207 SearchPlayer::setTableLimit(int 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::pushMove(Move move)
00233 {
00234 if (table_stack_ptr)
00235 {
00236 try
00237 {
00238 if (table_ptr.get() && (move == key_of_move->best_move))
00239 {
00240 table_stack_ptr->pushMove(move, key_of_move->root, *table_ptr);
00241 }
00242 else
00243 {
00244 table_stack_ptr->pushMove(move);
00245 }
00246 }
00247 catch (std::bad_alloc&)
00248 {
00249 std::cerr << "disabled table stack because of bad_alloc\n";
00250 enableTableStack(false);
00251 NonBlockDelete::deleteAll();
00252 }
00253 }
00254 }
00255 void osl::game_playing::
00256 SearchPlayer::popMove()
00257 {
00258 if (table_stack_ptr)
00259 table_stack_ptr->popMove();
00260 }
00261
00262 int osl::game_playing::
00263 SearchPlayer::secondsForThisMove(const GameState& state, int total_seconds_left,
00264 int byoyomi)
00265 {
00266 if (byoyomi < 0)
00267 return -1;
00268
00269 if (total_seconds_left < byoyomi)
00270 return byoyomi;
00271
00272 if (byoyomi == 0)
00273 {
00274
00275
00276
00277 total_seconds_left -= (240 - static_cast<int>(state.moveHistory().size()));
00278 }
00279 const int seconds_for_this_move
00280 = ((total_seconds_left <= 1)
00281 ? 1
00282 : search::TimeControl::secondsForThisMove(total_seconds_left));
00283
00284
00285 const progress::Effect5x3 progress(state.state());
00286 if (total_seconds_left >= 600)
00287 {
00288 if ((progress.progress16().value() >= 15)
00289 && ((progress.progress16(BLACK).value() >= 13)
00290 || (progress.progress16(WHITE).value() >= 13)))
00291 return seconds_for_this_move*2;
00292 }
00293
00294 if (progress.progress16().value() == 0 && byoyomi == 0)
00295 return std::min(25, seconds_for_this_move);
00296
00297 return std::max(byoyomi, seconds_for_this_move);
00298 }
00299
00300 const osl::game_playing::MoveWithComment osl::game_playing::
00301 SearchPlayer::selectBestMove(const GameState& state, int total_seconds_left,
00302 int byoyomi)
00303 {
00304 if (EnterKing::canDeclareWin(state.state()))
00305 return MoveWithComment(Move::DeclareWin());
00306 const int seconds_for_this_move
00307 = secondsForThisMove(state, total_seconds_left, byoyomi);
00308 if (byoyomi > 0
00309 && total_seconds_left < byoyomi
00310 && config.next_iteration_coefficient > 1.0)
00311 setNextIterationCoefficient(1.0);
00312 return searchWithSecondsForThisMove(state, seconds_for_this_move);
00313 }
00314
00315 void osl::game_playing::
00316 SearchPlayer::pickUpBestMoves(const GameState& game_state, MoveVector& out) const
00317 {
00318 if (!table_ptr)
00319 return;
00320
00321 const HashKey& key = game_state.state().getHash();
00322 const SimpleHashRecord *record = table_ptr->find(key);
00323 if (! record)
00324 return;
00325
00326 if (record->qrecord.bestMove().isNormal())
00327 out.push_back(record->qrecord.bestMove());
00328
00329 const Player turn = game_state.state().getTurn();
00330
00331 int best_value = search::FixedEval::winByCheckmate(alt(turn));
00332 Move best_move = Move::INVALID();
00333 for (int limit=600; limit<=1200; limit+=200)
00334 {
00335 SearchMoveSet::const_range r(record->moves());
00336 for (SearchMoveSet::const_iterator it=r.first; it != r.last; it++)
00337 {
00338 if (! it->record)
00339 continue;
00340 if (out.isMember(it->getMove()))
00341 continue;
00342 if (! it->getMove().isNormal())
00343 continue;
00344 if (it->getLogProb() > limit)
00345 continue;
00346 if (it->record->hasLowerBound(0)
00347 && eval::betterThan(turn, it->record->lowerBound(), best_value)
00348 && (! out.isMember(it->getMove())))
00349 {
00350 best_move = it->getMove();
00351 best_value = it->record->upperBound();
00352 }
00353 }
00354 if (best_move.isNormal())
00355 {
00356 out.push_back(best_move);
00357 break;
00358 }
00359 }
00360 if (record->bestMove().getMove().isNormal())
00361 out.push_back(record->bestMove().getMove());
00362 out.unique();
00363 }
00364
00365 void osl::game_playing::
00366 SearchPlayer::fillMoveComment(MoveWithComment& move, const GameState& state,
00367 const SimpleHashTable *table, int pawn_value)
00368 {
00369 if (! table)
00370 return;
00371 HashKey key = state.state().getHash();
00372 const SimpleHashRecord *record = table->find(key);
00373 if (! record || ! record->hasLowerBound(0))
00374 return;
00375 move.value = static_cast<int>(record->lowerBound() * 100.0 / pawn_value);
00376 try
00377 {
00378 if (! move.move.isInvalid()) {
00379 MoveVector pv;
00380 table->getPV(key.newHashWithMove(move.move), pv);
00381 for (size_t i=0; i<pv.size(); ++i) {
00382 move.moves.push_back(pv[i]);
00383 }
00384 }
00385 }
00386 catch (std::bad_alloc&)
00387 {
00388 std::cerr << "stop fillMoveComment by bad_alloc" << std::cerr;
00389 NonBlockDelete::deleteAll();
00390 }
00391 }
00392
00393 void osl::game_playing::
00394 SearchPlayer::setStopSchedule(const misc::RealTime& new_timer)
00395 {
00396 if (searcher)
00397 {
00398 searcher->setStopSchedule(new_timer);
00399 }
00400 }
00401
00402 #ifdef USE_NTESUKI
00403 osl::game_playing::SearchPlayer::
00404 NtesukiThread::NtesukiThread(Move& next_move,
00405 volatile bool *thread_finished,
00406 volatile bool *stop_flag,
00407 NumEffectState state)
00408 : next_move(next_move), thread_finished(thread_finished),
00409 stop_flag(stop_flag), state(state)
00410 {
00411 }
00412
00413 void osl::game_playing::SearchPlayer::
00414 NtesukiThread::operator()()
00415 {
00416 std::cerr << "start ntesuki search\n";
00417 *thread_finished = false;
00418
00419 const Player P = state.getTurn();
00420 const HashKey key = osl::HashKey::calcHash(state);;
00421
00422 boost::scoped_ptr<osl::ntesuki::NtesukiAttackMoveGenerator>
00423 gam(new osl::ntesuki::GetAttackMoves());
00424 boost::scoped_ptr<osl::ntesuki::NtesukiDefenseMoveGenerator>
00425 gdm(new osl::ntesuki::GetDefenseMoves());
00426
00427 osl::ntesuki::NtesukiSearcher
00428 searcher(state, gam.get(), gdm.get(), 500000u, stop_flag, true, 2);
00429
00430 try
00431 {
00432 int ntesuki_num = searcher.searchSlow(P, 10000000);
00433 if (-1 != ntesuki_num)
00434 {
00435 const osl::ntesuki::PdNtesukiTable& table
00436 = searcher.getTableSlow(P);
00437 const osl::ntesuki::PdNtesukiRecord *record
00438 = table.find(key);
00439 next_move = record->getBestMove(ntesuki_num).getMove();
00440 }
00441 }
00442 catch (ntesuki::ReadNodeLimit& e)
00443 {
00444 }
00445 catch (ntesuki::TableFull& e)
00446 {
00447 }
00448 catch (std::runtime_error& e)
00449 {
00450 std::cerr << e.what() << "\n";
00451 }
00452 std::cerr << "end ntesuki search\n";
00453 *thread_finished = true;
00454 }
00455 #endif
00456
00457
00458
00459
00460