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), current_move(Move::INVALID()), status(INITIAL), seconds(-1),
00160 stop_flag(false)
00161 {
00162 }
00163
00164 osl::game_playing::SpeculativeAllMoves::
00165 SearchAllMoves::~SearchAllMoves()
00166 {
00167 }
00168
00169 void osl::game_playing::SpeculativeAllMoves::
00170 SearchAllMoves::setUp(const GameState& main_state,
00171 const SearchPlayer& main_player,
00172 int standard_seconds, bool has_byoyomi)
00173 {
00174 NonBlockDelete::reset(player);
00175 try
00176 {
00177 player.reset(dynamic_cast<SearchPlayer*>(main_player.clone()));
00178 player->setVerbose(1);
00179 player->setNextIterationCoefficient(has_byoyomi ? 1 : 2);
00180 state = main_state.clone();
00181 generator.reset(new Generator(*state, *player, *this, standard_seconds, has_byoyomi));
00182 seconds = standard_seconds;
00183 if (has_byoyomi)
00184 seconds += std::min(30, standard_seconds/2);
00185 }
00186 catch (std::exception& e)
00187 {
00188 player.reset();
00189 std::cerr << "setUp " << e.what() << "\n";
00190 throw;
00191 }
00192 }
00193
00194 void osl::game_playing::SpeculativeAllMoves::
00195 SearchAllMoves::run()
00196 {
00197 StatusLock lk(&status, &mutex, &condition, RUNNING, FINISHED);
00198 if (! player)
00199 return;
00200 while (true)
00201 {
00202 boost::thread::yield();
00203 if (stop_flag)
00204 return;
00205 while (NonBlockDelete::deleteOne() && !stop_flag)
00206 ;
00207 if (stop_flag)
00208 return;
00209 Move prediction;
00210 {
00211 StatusLock lk(&status, &mutex, PREDICTION1);
00212 prediction = generator->nextMove();
00213 }
00214 if (! prediction.isNormal())
00215 return;
00216 if (stop_flag)
00217 return;
00218 const MoveWithComment result = testMove(prediction);
00219 results.add(prediction, result);
00220 if (! stop_flag)
00221 results.show(std::cerr);
00222 }
00223 }
00224
00225 const osl::search::MoveWithComment osl::game_playing::SpeculativeAllMoves::
00226 SearchAllMoves::testMove(Move predicted_move)
00227 {
00228 StatusLock lk(&status, &mutex, SEARCH1);
00229 {
00230 Mutex::scoped_lock lk(mutex);
00231 current_move = predicted_move;
00232 }
00233 assert(state);
00234 state->pushMove(predicted_move);
00235 assert(player);
00236 player->pushMove(predicted_move);
00237 MoveWithComment result(Move::INVALID());
00238 std::cerr << "\nprediction (" << seconds << ") "
00239 << record::csa::show(predicted_move) << " ";
00240 const time_t now = time(0);
00241 char ctime_buf[64];
00242 std::cerr << ctime_r(&now, ctime_buf);
00243 try
00244 {
00245 StatusLock lk(&status, &mutex, SEARCH2);
00246 if (seconds <= 0)
00247 seconds = 120;
00248 const MilliSeconds::Interval msec(seconds*1000);
00249 result =
00250 player->searchWithSecondsForThisMove(*state, search::TimeAssigned(msec, msec*5));
00251 }
00252 catch (std::exception& e)
00253 {
00254
00255 std::cerr << "error in speculative thread " << e.what() << "\n";
00256 stop_flag = true;
00257 NonBlockDelete::deleteAll();
00258 }
00259 catch (...)
00260 {
00261 std::cerr << "error in speculative thread\n";
00262 stop_flag = true;
00263 }
00264 state->popMove();
00265 player->popMove();
00266 {
00267 Mutex::scoped_lock lk(mutex);
00268 current_move = Move::INVALID();
00269 }
00270 return result;
00271 }
00272
00273 void osl::game_playing::SpeculativeAllMoves::
00274 SearchAllMoves::stopOtherThan(Move the_move)
00275 {
00276 stop_flag = true;
00277 if (currentMove() != the_move)
00278 stopNow();
00279 else
00280 {
00281 #ifndef NDEBUG
00282 std::cerr << "match " << record::csa::show(the_move) << "\n";
00283 #endif
00284 #ifndef GPSONE
00285 if (OslConfig::usiMode())
00286 OslConfig::setUsiSilent(false);
00287 #endif
00288 assert(player);
00289 player->setNextIterationCoefficient(4);
00290 player->setVerbose(2);
00291 }
00292 }
00293
00294 void osl::game_playing::SpeculativeAllMoves::
00295 SearchAllMoves::stopNow()
00296 {
00297 stop_flag = true;
00298 waitRunning();
00299 if (player && status != FINISHED)
00300 {
00301 std::cerr << "stopNow " << status << "\n";
00302 const bool success
00303 = player->stopSearchNow();
00304 if (! success)
00305 std::cerr << "stop search failed\n";
00306 }
00307 }
00308
00309 void osl::game_playing::SpeculativeAllMoves::
00310 SearchAllMoves::waitRunning()
00311 {
00312 Mutex::scoped_lock lk(mutex);
00313 while (status == INITIAL)
00314 {
00315 condition.wait(lk);
00316 if (!player)
00317 return;
00318 }
00319 }
00320
00321 void osl::game_playing::SpeculativeAllMoves::
00322 SearchAllMoves::setTimeAssign(const search::TimeAssigned& new_assign)
00323 {
00324 if (player && status != FINISHED)
00325 {
00326 waitRunning();
00327 player->setTimeAssign(new_assign);
00328 }
00329 }
00330 const osl::MilliSeconds osl::game_playing::SpeculativeAllMoves::
00331 SearchAllMoves::startTime()
00332 {
00333 if (player && status != FINISHED)
00334 {
00335 waitRunning();
00336 return player->startTime();
00337 }
00338 return MilliSeconds();
00339 }
00340
00341 const osl::Move osl::game_playing::SpeculativeAllMoves::
00342 SearchAllMoves::currentMove() const
00343 {
00344 Mutex::scoped_lock lk(mutex);
00345 return current_move;
00346 }
00347
00348
00349
00350
00351 struct osl::game_playing::SpeculativeAllMoves::Runner
00352 {
00353 SpeculativeAllMoves *parent;
00354 Runner(SpeculativeAllMoves *p) : parent(p)
00355 {
00356 }
00357 void
00358 #ifdef __GNUC__
00359 # ifdef _WIN32
00360 __attribute__((noinline))
00361 __attribute__((force_align_arg_pointer))
00362 # endif
00363 #endif
00364 operator()()
00365 {
00366 parent->searcher->run();
00367 }
00368 };
00369
00370 osl::game_playing::SpeculativeAllMoves::
00371 SpeculativeAllMoves()
00372 : results(new ResultVector()), last_search_seconds(-1), has_byoyomi(false),
00373 allowed(true)
00374 {
00375 }
00376
00377 osl::game_playing::
00378 SpeculativeAllMoves::~SpeculativeAllMoves()
00379 {
00380 stopAll();
00381 selectBestMoveCleanUp();
00382 }
00383
00384 void osl::game_playing::SpeculativeAllMoves::
00385 startSpeculative(const boost::shared_ptr<GameState> state,
00386 const SearchPlayer& main_player)
00387 {
00388 boost::mutex::scoped_lock lk(mutex);
00389 if (! allowed)
00390 return;
00391
00392 try
00393 {
00394 search_state = HashKey(state->state());
00395 results->clear();
00396 searcher.reset(new SearchAllMoves(*results));
00397 searcher->setUp(*state, main_player, last_search_seconds, has_byoyomi);
00398 thread.reset(new boost::thread(Runner(this)));
00399 }
00400 catch (std::exception& e)
00401 {
00402 std::cerr << "startSpeculative " << e.what();
00403 searcher.reset();
00404 }
00405 }
00406
00407 void osl::game_playing::SpeculativeAllMoves::
00408 clearResource()
00409 {
00410 boost::mutex::scoped_lock lk(mutex);
00411 assert(! thread);
00412 searcher.reset();
00413 }
00414
00415 void osl::game_playing::SpeculativeAllMoves::
00416 stopOtherThan(Move the_move)
00417 {
00418 boost::mutex::scoped_lock lk(mutex);
00419 if (searcher)
00420 searcher->stopOtherThan(the_move);
00421 }
00422
00423 void osl::game_playing::SpeculativeAllMoves::
00424 stopAll()
00425 {
00426 boost::mutex::scoped_lock lk(mutex);
00427 if (searcher)
00428 searcher->stopNow();
00429 }
00430
00431 const osl::search::MoveWithComment
00432 osl::game_playing::SpeculativeAllMoves::
00433 waitResult(Move last_move, search::TimeAssigned wait_for,
00434 SearchPlayer& main_player, int byoyomi)
00435 {
00436 {
00437 boost::mutex::scoped_lock lk(mutex);
00438 if (! allowed || ! searcher)
00439 return MoveWithComment(Move::INVALID());
00440 }
00441 last_search_seconds = (int)ceil(wait_for.standard.toSeconds());
00442 has_byoyomi = (byoyomi > 0);
00443
00444 const time_t start_time = time(0);
00445 const MilliSeconds start_time_msec = MilliSeconds::now();
00446 assert(last_move.isNormal());
00447 const MoveWithComment *result = 0;
00448 bool stop_now = false;
00449 if (searcher->currentMove() != last_move)
00450 {
00451 stop_now = true;
00452 wait_for = search::TimeAssigned(MilliSeconds::Interval(0));
00453 }
00454
00455 const time_t stop_time = start_time + static_cast<int>(ceil(wait_for.max.toSeconds()));
00456
00457 const MilliSeconds started = searcher->startTime();
00458 const bool already_finished = searcher->isFinished();
00459 if (! already_finished)
00460 {
00461 char ctime_buf[64];
00462 std::cerr << "wait for (" << wait_for.standard.toSeconds()
00463 << "/" << wait_for.max.toSeconds()
00464 << ") "
00465 << ctime_r(&stop_time, ctime_buf);
00466 const MilliSeconds::Interval diff = (start_time_msec - started);
00467 wait_for.standard = wait_for.standard + diff;
00468 wait_for.max = wait_for.max + diff;
00469 searcher->setTimeAssign(wait_for);
00470 }
00471 {
00472 int wait_count = 0;
00473 while (true)
00474 {
00475 const bool now_finished = searcher->isFinished();
00476 if ((result = results->find(last_move))) {
00477 #ifndef GPSONE
00478 if (wait_count == 0 && OslConfig::usiMode()) {
00479 double elapsed = wait_for.standard.toSeconds();
00480 if (! already_finished) {
00481 MilliSeconds now = MilliSeconds::now();
00482 elapsed = (now - started).toSeconds();
00483 }
00484 search::UsiReporter::showPV(result->root_limit/200, result->node_count, result->elapsed,
00485 last_move.player() == BLACK ? -result->value : result->value,
00486 result->move, &*result->moves.begin(), &*result->moves.end(),
00487 true);
00488 }
00489 #endif
00490 break;
00491 }
00492 assert(searcher);
00493 if (now_finished)
00494 return MoveWithComment(Move::INVALID());
00495 if (stop_now && ++wait_count > 60) {
00496 std::cerr << "error stop now failed for 60 times\n";
00497 abort();
00498 }
00499
00500 if (! stop_now)
00501 {
00502 time_t now = time(0);
00503 stop_now = now >= stop_time;
00504 }
00505 if (stop_now) {
00506 searcher->stopNow();
00507 }
00508 boost::mutex::scoped_lock lk(searcher->mutex);
00509 boost::xtime xt;
00510 boost::xtime_get(&xt, boost::TIME_UTC);
00511 if (wait_count <= 10)
00512 xt.sec += 1;
00513 else
00514 xt.sec += 2;
00515 searcher->condition.timed_wait(lk, xt);
00516 }
00517 }
00518 if (! searcher->isFinished())
00519 searcher->stopNow();
00520 if (result->move.isNormal()) {
00521 SearchPlayer *player = searcher->currentPlayer();
00522 if (player)
00523 main_player.swapTable(*player);
00524 }
00525 return *result;
00526 }
00527
00528 void osl::game_playing::SpeculativeAllMoves::
00529 selectBestMoveCleanUp()
00530 {
00531 if (! thread)
00532 return;
00533
00534 {
00535 boost::mutex::scoped_lock lk(mutex);
00536 if (searcher && ! searcher->isFinished())
00537 searcher->stopNow();
00538 }
00539 thread->join();
00540 thread.reset();
00541 if (searcher)
00542 NonBlockDelete::reset(searcher);
00543 }
00544
00545
00546
00547
00548
00549
00550