void MainThread::think() { Move bestMove; // --------------------- // 合法手がないならここで投了 // --------------------- if (rootMoves.size() == 0) { bestMove = MOVE_RESIGN; Signals.stop = true; goto ID_END; } // --------------------- // 定跡の選択部 // --------------------- { static PRNG prng; auto it = book.find(rootPos.sfen()); if (it != book.end()) { // 定跡にhitした。逆順で出力しないと将棋所だと逆順にならないという問題があるので逆順で出力する。 const auto& move_list = it->second; for (auto it = move_list.rbegin(); it != move_list.rend();it++ ) sync_cout << "info pv " << it->bestMove << " " << it->nextMove << " (" << fixed << setprecision(2) << (100* it->prob) << "%)" // 採択確率 << " score cp " << it->value << " depth " << it->depth << sync_endl; // このなかの一つをランダムに選択 // 無難な指し手が選びたければ、採択回数が一番多い、最初の指し手(move_list[0])を選ぶべし。 bestMove = move_list[prng.rand(move_list.size())].bestMove; Signals.stop = true; goto ID_END; } } // --------------------- // 通常の思考処理 // --------------------- { rootDepth = 0; Value alpha, beta; StateInfo si; auto& pos = rootPos; // --- 置換表のTTEntryの世代を進める。 TT.new_search(); // --------------------- // 思考の終了条件 // --------------------- std::thread* timerThread = nullptr; // 探索深さ、ノード数、詰み手数が指定されていない == 探索時間による制限 if (!(Limits.depth || Limits.nodes || Limits.mate)) { // 時間制限があるのでそれに従うために今回の思考時間を計算する。 // 今回に用いる思考時間 = 残り時間の1/60 + 秒読み時間 auto us = pos.side_to_move(); // 2秒未満は2秒として問題ない。(CSAルールにおいて) auto availableTime = std::max(2000, Limits.time[us] / 60 + Limits.byoyomi[us]); // 思考時間は秒単位で繰り上げ availableTime = (availableTime / 1000) * 1000; // 50msより小さいと思考自体不可能なので下限を50msに。 availableTime = std::max(50, availableTime - Options["NetworkDelay"]); auto endTime = Limits.startTime + availableTime; // タイマースレッドを起こして、終了時間を監視させる。 timerThread = new std::thread([&] { while (now() < endTime && !Signals.stop) sleep(10); Signals.stop = true; }); } // --------------------- // 反復深化のループ // --------------------- while (++rootDepth < MAX_PLY && !Signals.stop && (!Limits.depth || rootDepth <= Limits.depth)) { // 本当はもっと探索窓を絞ったほうが効率がいいのだが…。 alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; PVIdx = 0; // MultiPVではないのでPVは1つで良い。 YaneuraOuNano::search<Root>(rootPos, alpha, beta, rootDepth*ONE_PLY); // それぞれの指し手に対するスコアリングが終わったので並べ替えおく。 std::stable_sort(rootMoves.begin(), rootMoves.end()); // 読み筋を出力しておく。 sync_cout << USI::pv(pos, rootDepth, alpha, beta) << sync_endl; } bestMove = rootMoves.at(0).pv[0]; // --------------------- // タイマースレッド終了 // --------------------- Signals.stop = true; if (timerThread != nullptr) { timerThread->join(); delete timerThread; } } ID_END:; // 反復深化の終了。 // --------------------- // 指し手をGUIに返す // --------------------- // ponder中であるならgoコマンドか何かが送られてきてからのほうがいいのだが、とりあえずponderの処理は後回しで…。 sync_cout << "bestmove " << bestMove << sync_endl; }
void MainThread::think() { MoveList<LEGAL_ALL> ml(rootPos); Move m = (ml.size() == 0) ? MOVE_RESIGN : ml.at(size_t(my_rand.rand(ml.size()))).move; sync_cout << "bestmove " << m << sync_endl; }