All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
searchPlayer.cc
Go to the documentation of this file.
1 /* searchPlayer.cc
2  */
11 #include "osl/search/timeControl.h"
12 #include "osl/search/searchTimer.h"
13 #include "osl/search/fixedEval.h"
16 #include "osl/progress/effect5x3.h"
18 #include "osl/hash/hashRandom.h"
20 #include "osl/misc/ctime.h"
21 #include <iostream>
22 #include <ctime>
23 #include <cmath>
24 
27  : limit(1200), node_limit(800000), table_size(10000),
28  table_record_limit(0), initial_limit(600), deepening_step(200),
29  verbose(0), next_iteration_coefficient(3), draw_coef(0),
30  save_pv(true), node_count_hard_limit(std::numeric_limits<uint64_t>::max()),
31  multi_pv_width(0)
32 {
33 }
34 
36  const SearchPlayer::Config& r)
37 {
38  return (l.limit == r.limit) && (l.node_limit == r.node_limit)
39  && (l.table_size == r.table_size)
41  && (l.initial_limit == r.initial_limit)
42  && (l.deepening_step == r.deepening_step)
43  && (l.verbose == r.verbose)
45  && (l.draw_coef == r.draw_coef);
46 }
47 
50  : recorder_ptr(new CountRecorder()),
51  searching(0),
52  plan_stop(false), root_ignore_moves(0),
55 {
56 }
57 
61  config(copy.config),
62  recorder_ptr(new CountRecorder()),
63  searching(0), plan_stop(false),
64  root_ignore_moves(0), prediction_for_speculative_search(0),
65  pv_history(new PVHistory(*copy.pv_history)),
66  almost_resign_count(0)
67 {
68 }
69 
72 {
73 }
74 
77 {
78  table_ptr.swap(other.table_ptr);
79 }
80 
83 {
84  return searching;
85 }
86 
89 {
90  plan_stop = true;
91  if (! searching)
92  {
93  std::cerr << "SearchPlayer not searching ";
94  const time_t now = time(0);
95  char ctime_buf[64];
96  std::cerr << ctime_r(&now, ctime_buf);
97  return false;
98  }
99  searcher->stopNow();
100  return true;
101 }
102 
105 {
106  recorder_ptr.reset(new_recorder);
107 }
108 
110 SearchPlayer::adjust(const search::TimeAssigned& org, const MilliSeconds::Interval& consumed)
111 {
112  if (consumed < org.standard/8)
113  return search::TimeAssigned(org.standard - consumed, org.max - consumed);
114  // spent too much seconds in preparation
116  return search::TimeAssigned(std::min(org.standard - org.standard/8, org.max - consumed),
117  org.max - consumed);
118 }
119 
120 const osl::MilliSeconds::Interval osl::game_playing::
121 SearchPlayer::setUpTable(const GameState& gs, int pawn_value)
122 {
123  const MilliSeconds started = MilliSeconds::now();
124  time_t t = time(0);
125  char ctime_buf[64];
126  if (table_ptr.get() && table_ptr->verboseLevel() > 2)
127  {
128  std::cerr << "setUpTable " << ctime_r(&t, ctime_buf)
129  << std::flush;
130  }
131 
132  // release cherkmate_ptr first
133  NonBlockDelete::reset(checkmate_ptr);
134 
135  const int black_win = search::FixedEval::winByLoop(BLACK);
136  const int white_win = search::FixedEval::winByLoop(WHITE);
137  if (table_ptr.get()) {
138  table_ptr->clear();
139  }
140  else {
141  try
142  {
143  table_ptr.reset(new SimpleHashTable(config.table_size,
144  config.table_record_limit, config.verbose));
145  }
146  catch (std::bad_alloc&)
147  {
148  NonBlockDelete::deleteAll();
149  std::cerr << "\atable allocation failed, try agaian" << std::endl;
150  table_ptr.reset(new SimpleHashTable(config.table_size,
151  config.table_record_limit, config.verbose));
152  }
153  }
154  table_ptr->setVerbose(config.verbose);
155  NonBlockDelete::deleteAll();
156 
157  HistoryToTable::adjustTable(gs, *table_ptr, black_win, config.draw_coef*pawn_value, white_win);
158  if (config.save_pv)
159  HistoryToTable::setPV(*pv_history, gs, *table_ptr);
160  try
161  {
162  checkmate_ptr.reset(new DualDfpn());
163  }
164  catch (std::bad_alloc&)
165  {
166  NonBlockDelete::deleteAll();
167  std::cerr << "\acheckmate allocation failed, try agaian" << std::endl;
168  checkmate_ptr.reset(new DualDfpn());
169  }
170  checkmate_ptr->writeRootHistory(gs.counter(), gs.moveHistory(),
171  gs.state(), gs.state().turn());
172 
173  if (table_ptr->verboseLevel() > 2)
174  {
175  t = time(0);
176  std::cerr << "setup done in " << (MilliSeconds::now() - started).toSeconds()
177  << " sec. " << ctime_r(&t, ctime_buf)
178  << std::flush;
179  }
180 #ifndef MINIMAL
181  if (OslConfig::evalRandom())
182  HashRandom::setUp(1.0*OslConfig::evalRandom()*pawn_value/100);
183 #endif
184  return MilliSeconds::now() - started;
185 }
186 
188 SearchPlayer::setDepthLimit(int l, int il, int ds)
189 {
190  config.limit = l;
191  config.initial_limit = il;
192  config.deepening_step = ds;
193 }
194 
197 {
198  config.node_limit = nl;
199 }
200 
203 {
204  config.node_count_hard_limit = nl;
205 }
206 
208 SearchPlayer::setTableLimit(size_t size, int record_limit)
209 {
210  config.table_size = size;
211  config.table_record_limit = record_limit;
212 
213  table_ptr.reset();
214 }
215 
218 {
219  config.verbose = v;
220  if (table_ptr)
221  table_ptr->setVerbose(v);
222 }
223 
226 {
227  config.next_iteration_coefficient = new_value;
228  if (searcher)
229  searcher->setNextIterationCoefficient(new_value);
230 }
231 
233 SearchPlayer::addMonitor(const boost::shared_ptr<search::SearchMonitor>& m)
234 {
235  config.monitors.push_back(m);
236 }
237 
238 
241 {
242  NonBlockDelete::reset(checkmate_ptr);
243  if (speculative_search_allowed)
244  NonBlockDelete::reset(table_ptr);
245 }
248 {
249  NonBlockDelete::reset(checkmate_ptr);
250  if (speculative_search_allowed)
251  NonBlockDelete::reset(table_ptr);
252 }
253 
255 SearchPlayer::secondsForThisMove(const GameState& state, int time_limit, int elapsed,
256  int byoyomi) const
257 {
258  return secondsForThisMove(state, time_limit, elapsed, byoyomi,
259  table_ptr.get() ? table_ptr->verboseLevel() : 0);
260 }
261 
263 SearchPlayer::secondsForThisMove(const GameState& state, int time_limit, int elapsed,
264  int byoyomi, int verboseness)
265 {
266  if (byoyomi < 0)
267  return -1; // 無限
268 
269  if (time_limit - elapsed < byoyomi)
270  return byoyomi;
271  const int time_limit_org = time_limit;
272  const int moves = state.moveHistory().size();
273  if (byoyomi == 0)
274  {
275  // 330手指せるようにする
276  // http://www32.ocn.ne.jp/~yss/csa14rep.html
277  // 90秒から1秒将棋に入るので240で良い (330-90)
278  time_limit -= std::max(0, (240 - moves));
279  }
280  const int time_left = time_limit - elapsed;
281  if (time_left <= 1)
282  return 1;
283  int seconds_for_this_move
285 
286  // Think more if book leads us to near endgame
287  const progress::Effect5x3 progress(state.state());
288  if (time_left >= 600*time_limit_org/1500)
289  {
290  if ((progress.progress16().value() >= 15)
291  && ((progress.progress16(BLACK).value() >= 13)
292  || (progress.progress16(WHITE).value() >= 13))) {
293  if (verboseness)
294  std::cerr << " time control endgame ext\n";
295  return seconds_for_this_move*2;
296  }
297  }
298  if (byoyomi == 0 || time_left >= byoyomi*60)
299  {
300  // do not think too much in opening
301  if (progress.progress16().value() == 0) {
302  if (verboseness)
303  std::cerr << " time control progress0 limit " << 25*time_limit_org/1500 << "\n";
304  return std::min(std::max(1, 25*time_limit_org/1500), seconds_for_this_move);
305  }
306  if (progress.progress16().value() <= 3 && moves <= 40) {
307  if (verboseness)
308  std::cerr << " time control progress4 limit " << 38*time_limit_org/1500 << "\n";
309  return std::min(std::max(1, 38*time_limit_org/1500), seconds_for_this_move);
310  }
311  }
312  // others
313  seconds_for_this_move += byoyomi;
314  if (byoyomi >= 10 && time_left >= byoyomi*2)
315  seconds_for_this_move += byoyomi/2;
316  return seconds_for_this_move;
317 }
318 
320 SearchPlayer::assignTime(const GameState& state, int limit, int elapsed,
321  int byoyomi) const
322 {
323  return assignTime(state, limit, elapsed, byoyomi,
324  table_ptr.get() ? table_ptr->verboseLevel() : 0);
325 }
326 
328 SearchPlayer::assignTime(const GameState& state, int limit, int elapsed,
329  int byoyomi, int verboseness)
330 {
331  const int seconds_for_this_move
332  = secondsForThisMove(state, limit, elapsed, byoyomi, verboseness);
333  const int seconds_max = (byoyomi && (limit - elapsed) < byoyomi)
334  ? seconds_for_this_move
335  : std::min(seconds_for_this_move*5, std::max(seconds_for_this_move, (limit-elapsed)/2));
336  return search::TimeAssigned(MilliSeconds::Interval(seconds_for_this_move*1000),
337  MilliSeconds::Interval(seconds_max*1000));
338 }
339 
341 SearchPlayer::selectBestMove(const GameState& state, int limit, int elapsed,
342  int byoyomi)
343 {
344  return selectBestMoveInTime(state, assignTime(state, limit, elapsed, byoyomi));
345 }
346 
349 {
350  if (EnterKing::canDeclareWin(state.state()))
351  return MoveWithComment(Move::DeclareWin());
352  if (msec.standard == msec.max
353  && config.next_iteration_coefficient > 1.0)
354  setNextIterationCoefficient(1.0);
355  return searchWithSecondsForThisMove(state, msec);
356 }
357 
359 SearchPlayer::saveSearchResult(const GameState& state, const MoveWithComment& best_move)
360 {
361  (*pv_history)[state.moveHistory().size() % pv_history->size()] = best_move;
362 }
363 
366 {
367  if (searcher)
368  {
369  searcher->setTimeAssign(new_assign);
370  }
371 }
372 const osl::MilliSeconds osl::game_playing::
374 {
375  if (searcher)
376  {
377  return searcher->startTime();
378  }
379  return MilliSeconds();
380 }
381 
382 #ifdef USE_NTESUKI
383 osl::game_playing::SearchPlayer::
384 NtesukiThread::NtesukiThread(Move& next_move,
385  volatile bool *thread_finished,
386  volatile bool *stop_flag,
387  NumEffectState state)
388  : next_move(next_move), thread_finished(thread_finished),
389  stop_flag(stop_flag), state(state)
390 {
391 }
392 
393 void osl::game_playing::SearchPlayer::
394 NtesukiThread::operator()()
395 {
396  std::cerr << "start ntesuki search\n";
397  *thread_finished = false;
398 
399  const Player P = state.turn();
400  const HashKey key = osl::HashKey::calcHash(state);;
401 
402  boost::scoped_ptr<osl::ntesuki::NtesukiAttackMoveGenerator>
403  gam(new osl::ntesuki::GetAttackMoves());
404  boost::scoped_ptr<osl::ntesuki::NtesukiDefenseMoveGenerator>
405  gdm(new osl::ntesuki::GetDefenseMoves());
406 
408  searcher(state, gam.get(), gdm.get(), 500000u, stop_flag, true, 2);
409 
410  try
411  {
412  int ntesuki_num = searcher.searchSlow(P, 10000000);
413  if (-1 != ntesuki_num)
414  {
415  const osl::ntesuki::PdNtesukiTable& table
416  = searcher.getTableSlow(P);
417  const osl::ntesuki::PdNtesukiRecord *record
418  = table.find(key);
419  next_move = record->getBestMove(ntesuki_num).move();
420  }
421  }
422  catch (ntesuki::ReadNodeLimit& e)
423  {
424  }
425  catch (ntesuki::TableFull& e)
426  {
427  }
428  catch (std::runtime_error& e)
429  {
430  std::cerr << e.what() << "\n";
431  }
432  std::cerr << "end ntesuki search\n";
433  *thread_finished = true;
434 }
435 #endif
436 /* ------------------------------------------------------------------------- */
437 // ;;; Local Variables:
438 // ;;; mode:c++
439 // ;;; c-basic-offset:2
440 // ;;; End: