00001
00002
00003 #include "osl/game_playing/speculativeAllMoves.h"
00004 #include "osl/game_playing/gameState.h"
00005 #include "osl/game_playing/searchPlayer.h"
00006 #include "osl/game_playing/gameState.h"
00007 #include "osl/search/timeControl.h"
00008 #include "osl/search/searchRecorder.h"
00009 #include "osl/search/simpleHashTable.h"
00010 #include "osl/search/usiReporter.h"
00011 #include "osl/move_generator/legalMoves.h"
00012 #include "osl/misc/milliSeconds.h"
00013 #include "osl/record/csa.h"
00014 #include "osl/sennichite.h"
00015 #include "osl/misc/lightMutex.h"
00016 #include "osl/misc/nonBlockDelete.h"
00017 #include "osl/misc/ctime.h"
00018 #include <boost/foreach.hpp>
00019 #include <boost/thread.hpp>
00020 #include <boost/thread/xtime.hpp>
00021 #include <exception>
00022 #include <iostream>
00023 #include <cmath>
00024 #ifndef _MSC_VER
00025 # include <unistd.h>
00026 #endif
00027
00028
00029 osl::game_playing::SpeculativeAllMoves::
00030 ResultVector::ResultVector()
00031 {
00032 }
00033
00034 osl::game_playing::SpeculativeAllMoves::
00035 ResultVector::~ResultVector()
00036 {
00037 }
00038
00039 void osl::game_playing::SpeculativeAllMoves::ResultVector::
00040 add(Move prediction, const MoveWithComment& result)
00041 {
00042 SCOPED_LOCK(lk,mutex);
00043 data.push_back(std::make_pair(prediction, result));
00044 }
00045 const osl::search::MoveWithComment* osl::game_playing::SpeculativeAllMoves::ResultVector::
00046 find(Move prediction) const
00047 {
00048 SCOPED_LOCK(lk,mutex);
00049 BOOST_FOREACH(const vector_t::value_type& v, data) {
00050 if (v.first == prediction)
00051 return &v.second;
00052 }
00053 return 0;
00054 }
00055 void osl::game_playing::SpeculativeAllMoves::ResultVector::
00056 clear()
00057 {
00058 SCOPED_LOCK(lk,mutex);
00059 data.clear();
00060 }
00061 void osl::game_playing::SpeculativeAllMoves::ResultVector::
00062 show(std::ostream& os) const
00063 {
00064 SCOPED_LOCK(lk,mutex);
00065 for (size_t i=0; i<data.size(); ++i) {
00066 if (i)
00067 os << ", ";
00068 os << record::csa::show(data[i].first) << "=>" << record::csa::show(data[i].second.move);
00069 }
00070 os << std::endl;
00071 }
00072
00073
00074
00075 struct osl::game_playing::SpeculativeAllMoves::SearchAllMoves::StatusLock
00076 {
00077 volatile Status& status;
00078 boost::mutex& mutex;
00079 boost::condition *condition;
00080 const Status in, out;
00081 StatusLock(volatile Status *s, boost::mutex *m, boost::condition* c, Status i, Status o)
00082 : status(*s), mutex(*m), condition(c), in(i), out(o)
00083 {
00084 boost::mutex::scoped_lock lk(mutex);
00085 status = in;
00086 condition->notify_all();
00087 }
00088 StatusLock(volatile Status *s, boost::mutex *m, Status i)
00089 : status(*s), mutex(*m), condition(0), in(i), out(*s)
00090 {
00091 boost::mutex::scoped_lock lk(mutex);
00092 status = in;
00093 }
00094 ~StatusLock()
00095 {
00096 status = out;
00097 if (condition)
00098 condition->notify_all();
00099 }
00100 };
00101
00102 struct osl::game_playing::SpeculativeAllMoves::SearchAllMoves::Generator
00103 {
00104 GameState& state;
00105 SearchPlayer& player;
00106 MoveVector tried_moves;
00107 volatile Status& status;
00108 boost::mutex& mutex;
00109 int index, seconds;
00110 bool has_byoyomi;
00111 Generator(GameState& s, SearchPlayer& p, SearchAllMoves& parent, int sec, bool byoyomi)
00112 : state(s), player(p), status(parent.status), mutex(parent.mutex), index(-1), seconds(sec),
00113 has_byoyomi(byoyomi)
00114 {
00115 }
00116 Move pickUpMove()
00117 {
00118 try
00119 {
00120 MoveWithComment result;
00121 {
00122 StatusLock lk(&status, &mutex, PREDICTION2);
00123 player.setRootIgnoreMoves(&tried_moves, true);
00124 player.setVerbose(0);
00125 const int sec = std::max(1, has_byoyomi ? seconds/10 : seconds/7);
00126 result = player.selectBestMove(state, 0, 0, sec);
00127 player.setRootIgnoreMoves(0, false);
00128 player.setVerbose(1);
00129 }
00130 #ifndef NDEBUG
00131 if (result.move.isNormal()) {
00132 std::cerr << "search prediction ";
00133 std::cerr << record::csa::show(result.move);
00134 std::cerr << "\n";
00135 }
00136 #endif
00137 return result.move;
00138 }
00139 catch (std::exception& e)
00140 {
00141 std::cerr << "Generator::bestMove " << e.what() << "\n";
00142 }
00143 return Move::INVALID();
00144 }
00145 const Move nextMove()
00146 {
00147 const Move move = pickUpMove();
00148 if (! move.isNormal()) {
00149 std::cerr << "finish\n";
00150 return Move::INVALID();
00151 }
00152 tried_moves.push_back(move);
00153 return move;
00154 }
00155 };
00156
00157 osl::game_playing::SpeculativeAllMoves::
00158 SearchAllMoves::SearchAllMoves(ResultVector& r)
00159 : results(r), next_iteration_coefficient(1.0),
00160 current_move(Move::INVALID()), status(INITIAL), seconds(-1),
00161 stop_flag(false)
00162 {
00163 }
00164
00165 osl::game_playing::SpeculativeAllMoves::
00166 SearchAllMoves::~SearchAllMoves()
00167 {
00168 }
00169
00170 void osl::game_playing::SpeculativeAllMoves::
00171 SearchAllMoves::setUp(const GameState& main_state,
00172 const SearchPlayer& main_player,
00173 int standard_seconds, bool has_byoyomi)
00174 {
00175 NonBlockDelete::reset(player);
00176 next_iteration_coefficient = main_player.nextIterationCoefficient();
00177 try
00178 {
00179 player.reset(dynamic_cast<SearchPlayer*>(main_player.clone()));
00180 player->setVerbose(1);
00181 player->setNextIterationCoefficient(std::max(1.0, next_iteration_coefficient/2));
00182 state = main_state.clone();
00183 generator.reset(new Generator(*state, *player, *this, standard_seconds, has_byoyomi));
00184 seconds = standard_seconds;
00185 if (has_byoyomi)
00186 seconds += std::min(30, standard_seconds/2);
00187 }
00188 catch (std::exception& e)
00189 {
00190 player.reset();
00191 std::cerr << "setUp " << e.what() << "\n";
00192 throw;
00193 }
00194 }
00195
00196 void osl::game_playing::SpeculativeAllMoves::
00197 SearchAllMoves::run()
00198 {
00199 StatusLock lk(&status, &mutex, &condition, RUNNING, FINISHED);
00200 if (! player)
00201 return;
00202 while (true)
00203 {
00204 boost::thread::yield();
00205 if (stop_flag)
00206 return;
00207 while (NonBlockDelete::deleteOne() && !stop_flag)
00208 ;
00209 if (stop_flag)
00210 return;
00211 Move prediction;
00212 {
00213 StatusLock lk(&status, &mutex, PREDICTION1);
00214 prediction = generator->nextMove();
00215 }
00216 if (! prediction.isNormal())
00217 return;
00218 if (stop_flag)
00219 return;
00220 const MoveWithComment result = testMove(prediction);
00221 results.add(prediction, result);
00222 if (! stop_flag)
00223 results.show(std::cerr);
00224 }
00225 }
00226
00227 const osl::search::MoveWithComment osl::game_playing::SpeculativeAllMoves::
00228 SearchAllMoves::testMove(Move predicted_move)
00229 {
00230 StatusLock lk(&status, &mutex, SEARCH1);
00231 {
00232 Mutex::scoped_lock lk(mutex);
00233 current_move = predicted_move;
00234 }
00235 assert(state);
00236 state->pushMove(predicted_move);
00237 assert(player);
00238 player->pushMove(predicted_move);
00239 MoveWithComment result(Move::INVALID());
00240 std::cerr << "\nprediction (" << seconds << ") "
00241 << record::csa::show(predicted_move) << " ";
00242 const time_t now = time(0);
00243 char ctime_buf[64];
00244 std::cerr << ctime_r(&now, ctime_buf);
00245 try
00246 {
00247 StatusLock lk(&status, &mutex, SEARCH2);
00248 if (seconds <= 0)
00249 seconds = 120;
00250 const MilliSeconds::Interval msec(seconds*1000);
00251 result =
00252 player->searchWithSecondsForThisMove(*state, search::TimeAssigned(msec, msec*5));
00253 }
00254 catch (std::exception& e)
00255 {
00256
00257 std::cerr << "error in speculative thread " << e.what() << "\n";
00258 stop_flag = true;
00259 NonBlockDelete::deleteAll();
00260 }
00261 catch (...)
00262 {
00263 std::cerr << "error in speculative thread\n";
00264 stop_flag = true;
00265 }
00266 state->popMove();
00267 player->popMove();
00268 {
00269 Mutex::scoped_lock lk(mutex);
00270 current_move = Move::INVALID();
00271 }
00272 return result;
00273 }
00274
00275 void osl::game_playing::SpeculativeAllMoves::
00276 SearchAllMoves::stopOtherThan(Move the_move)
00277 {
00278 stop_flag = true;
00279 if (currentMove() != the_move)
00280 stopNow();
00281 else
00282 {
00283 #ifndef NDEBUG
00284 std::cerr << "match " << record::csa::show(the_move) << "\n";
00285 #endif
00286 #ifndef GPSONE
00287 if (OslConfig::usiMode())
00288 OslConfig::setUsiSilent(false);
00289 #endif
00290 assert(player);
00291 player->setNextIterationCoefficient(next_iteration_coefficient);
00292 player->setVerbose(2);
00293 }
00294 }
00295
00296 void osl::game_playing::SpeculativeAllMoves::
00297 SearchAllMoves::stopNow()
00298 {
00299 stop_flag = true;
00300 waitRunning();
00301 if (player && status != FINISHED)
00302 {
00303 std::cerr << "stopNow " << status << "\n";
00304 const bool success
00305 = player->stopSearchNow();
00306 if (! success)
00307 std::cerr << "stop search failed\n";
00308 }
00309 }
00310
00311 void osl::game_playing::SpeculativeAllMoves::
00312 SearchAllMoves::waitRunning()
00313 {
00314 Mutex::scoped_lock lk(mutex);
00315 while (status == INITIAL)
00316 {
00317 condition.wait(lk);
00318 if (!player)
00319 return;
00320 }
00321 }
00322
00323 void osl::game_playing::SpeculativeAllMoves::
00324 SearchAllMoves::setTimeAssign(const search::TimeAssigned& new_assign)
00325 {
00326 if (player && status != FINISHED)
00327 {
00328 waitRunning();
00329 player->setTimeAssign(new_assign);
00330 }
00331 }
00332 const osl::MilliSeconds osl::game_playing::SpeculativeAllMoves::
00333 SearchAllMoves::startTime()
00334 {
00335 if (player && status != FINISHED)
00336 {
00337 waitRunning();
00338 return player->startTime();
00339 }
00340 return MilliSeconds();
00341 }
00342
00343 const osl::Move osl::game_playing::SpeculativeAllMoves::
00344 SearchAllMoves::currentMove() const
00345 {
00346 Mutex::scoped_lock lk(mutex);
00347 return current_move;
00348 }
00349
00350
00351
00352
00353 struct osl::game_playing::SpeculativeAllMoves::Runner
00354 {
00355 SpeculativeAllMoves *parent;
00356 Runner(SpeculativeAllMoves *p) : parent(p)
00357 {
00358 }
00359 void
00360 #ifdef __GNUC__
00361 # ifdef _WIN32
00362 __attribute__((noinline))
00363 __attribute__((force_align_arg_pointer))
00364 # endif
00365 #endif
00366 operator()()
00367 {
00368 parent->searcher->run();
00369 }
00370 };
00371
00372 osl::game_playing::SpeculativeAllMoves::
00373 SpeculativeAllMoves()
00374 : results(new ResultVector()), last_search_seconds(-1), has_byoyomi(false),
00375 allowed(true)
00376 {
00377 }
00378
00379 osl::game_playing::
00380 SpeculativeAllMoves::~SpeculativeAllMoves()
00381 {
00382 stopAll();
00383 selectBestMoveCleanUp();
00384 }
00385
00386 void osl::game_playing::SpeculativeAllMoves::
00387 startSpeculative(const boost::shared_ptr<GameState> state,
00388 const SearchPlayer& main_player)
00389 {
00390 boost::mutex::scoped_lock lk(mutex);
00391 if (! allowed)
00392 return;
00393
00394 try
00395 {
00396 search_state = HashKey(state->state());
00397 results->clear();
00398 searcher.reset(new SearchAllMoves(*results));
00399 searcher->setUp(*state, main_player, last_search_seconds, has_byoyomi);
00400 thread.reset(new boost::thread(Runner(this)));
00401 }
00402 catch (std::exception& e)
00403 {
00404 std::cerr << "startSpeculative " << e.what();
00405 searcher.reset();
00406 }
00407 }
00408
00409 void osl::game_playing::SpeculativeAllMoves::
00410 clearResource()
00411 {
00412 boost::mutex::scoped_lock lk(mutex);
00413 assert(! thread);
00414 searcher.reset();
00415 }
00416
00417 void osl::game_playing::SpeculativeAllMoves::
00418 stopOtherThan(Move the_move)
00419 {
00420 boost::mutex::scoped_lock lk(mutex);
00421 if (searcher)
00422 searcher->stopOtherThan(the_move);
00423 }
00424
00425 void osl::game_playing::SpeculativeAllMoves::
00426 stopAll()
00427 {
00428 boost::mutex::scoped_lock lk(mutex);
00429 if (searcher)
00430 searcher->stopNow();
00431 }
00432
00433 const osl::search::MoveWithComment
00434 osl::game_playing::SpeculativeAllMoves::
00435 waitResult(Move last_move, search::TimeAssigned wait_for,
00436 SearchPlayer& main_player, int byoyomi)
00437 {
00438 {
00439 boost::mutex::scoped_lock lk(mutex);
00440 if (! allowed || ! searcher)
00441 return MoveWithComment(Move::INVALID());
00442 }
00443 last_search_seconds = (int)ceil(wait_for.standard.toSeconds());
00444 has_byoyomi = (byoyomi > 0);
00445
00446 const time_t start_time = time(0);
00447 const MilliSeconds start_time_msec = MilliSeconds::now();
00448 assert(last_move.isNormal());
00449 const MoveWithComment *result = 0;
00450 bool stop_now = false;
00451 if (searcher->currentMove() != last_move)
00452 {
00453 stop_now = true;
00454 wait_for = search::TimeAssigned(MilliSeconds::Interval(0));
00455 }
00456
00457 const time_t stop_time = start_time + static_cast<int>(ceil(wait_for.max.toSeconds()));
00458
00459 const MilliSeconds started = searcher->startTime();
00460 const bool already_finished = searcher->isFinished();
00461 if (! already_finished)
00462 {
00463 char ctime_buf[64];
00464 std::cerr << "wait for (" << wait_for.standard.toSeconds()
00465 << "/" << wait_for.max.toSeconds()
00466 << ") "
00467 << ctime_r(&stop_time, ctime_buf);
00468 const MilliSeconds::Interval diff = (start_time_msec - started);
00469 wait_for.standard = wait_for.standard + diff;
00470 wait_for.max = wait_for.max + diff;
00471 searcher->setTimeAssign(wait_for);
00472 }
00473 {
00474 int wait_count = 0;
00475 while (true)
00476 {
00477 const bool now_finished = searcher->isFinished();
00478 if ((result = results->find(last_move))) {
00479 #ifndef GPSONE
00480 if (wait_count == 0 && OslConfig::usiMode()) {
00481 search::UsiReporter::showPV(std::cout, result->root_limit/200, result->node_count, result->elapsed,
00482 last_move.player() == BLACK ? -result->value : result->value,
00483 result->move, &*result->moves.begin(), &*result->moves.end(),
00484 true);
00485 }
00486 #endif
00487 break;
00488 }
00489 assert(searcher);
00490 if (now_finished)
00491 return MoveWithComment(Move::INVALID());
00492 if (stop_now && ++wait_count > 60) {
00493 std::cerr << "error stop now failed for 60 times\n";
00494 abort();
00495 }
00496
00497 if (! stop_now)
00498 {
00499 time_t now = time(0);
00500 stop_now = now >= stop_time;
00501 }
00502 if (stop_now) {
00503 searcher->stopNow();
00504 }
00505 boost::mutex::scoped_lock lk(searcher->mutex);
00506 boost::xtime xt;
00507 boost::xtime_get(&xt, boost::TIME_UTC);
00508 if (wait_count <= 10)
00509 xt.sec += 1;
00510 else
00511 xt.sec += 2;
00512 searcher->condition.timed_wait(lk, xt);
00513 }
00514 }
00515 if (! searcher->isFinished())
00516 searcher->stopNow();
00517 if (result->move.isNormal()) {
00518 SearchPlayer *player = searcher->currentPlayer();
00519 if (player)
00520 main_player.swapTable(*player);
00521 }
00522 return *result;
00523 }
00524
00525 void osl::game_playing::SpeculativeAllMoves::
00526 selectBestMoveCleanUp()
00527 {
00528 if (! thread)
00529 return;
00530
00531 {
00532 boost::mutex::scoped_lock lk(mutex);
00533 if (searcher && ! searcher->isFinished())
00534 searcher->stopNow();
00535 }
00536 thread->join();
00537 thread.reset();
00538 if (searcher)
00539 NonBlockDelete::reset(searcher);
00540 }
00541
00542
00543
00544
00545
00546
00547