speculativeAllMoves.cc
Go to the documentation of this file.
00001 /* speculativeAllMoves.cc
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(); // test whether opponent's move has arrived
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     // TODO table full はclear?
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   // const time_t standard_time = start_time + ceil(wait_for.standard.toSeconds());
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;            // 1 sec
00510       else
00511         xt.sec += 2;            // 2 sec
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 // ;;; Local Variables:
00544 // ;;; mode:c++
00545 // ;;; c-basic-offset:2
00546 // ;;; coding:utf-8
00547 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines