今回はUCI制御部について解説します。
UCIというのは、Chess用のプロトコルでして、UCIのCがChessの頭文字です。将棋の場合、ご存知の通り、USIプロトコルであり、USIのSがShogiの頭文字です。
UCIプロトコルは標準入出力経由でやりとりするだけなのでかなりシンプルなソースコードとなっています。USIプロトコルの実装の参考用にLesserkaiのソースコードをご覧になった方も多いかとは思いますが、あのソースコードよりはかなり洗練された、美しい作りとなっています。
Seleneの西海枝さんは「Stockfish、探索部はあまり見てないですが、UCIのやりとりする部分はかなり参考になりました」とおっしゃっていました。そういう意味では、この部分だけでも参考にする価値は大いにあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #ifndef UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED #include #include namespace UCI { class Option; /// Custom comparator because UCI options should be case insensitive // カスタム比較オペレーター。なぜなら、UCIオプションは大文字小文字を区別しないから。 struct CaseInsensitiveLess { bool operator() (const std::string&, const std::string&) const; }; /// Our options container is actually a std::map // 我々のオプション設定を保持するコンテナは実際はstd::mapである。 typedef std::map /// Option class implements an option as defined by UCI protocol // UCIプロトコルで定義されているオプションを実装するOptionクラス。 // ※ 項目ひとつ分の設定を保持する。 class Option { // この項目が変更されたときに呼び出されるハンドラ typedef void (Fn)(const Option&); public: // --- コンストラクタ // Fn = この項目が変更されたときに呼び出されるハンドラ // デフォルトコンストラクタ Option(Fn* = nullptr); // bool型用 // v = デフォルト値かつ現在の値 Option(bool v, Fn* = nullptr); // string型用 // v = デフォルト値かつ現在の値 Option(const char* v, Fn* = nullptr); // int型用 // v = デフォルト値 , min = 最小値 , max = 最大値 , この項目が変更されたときに呼び出されるハンドラ Option(int v, int min, int max, Fn* = nullptr); // 代入用。UCIプロトコルで渡された文字列をvに入れるとコンストラクタで格納していた型に変換して格納される。 // また、このクラスのコンストラクタでFnとして渡されていたハンドラがnullptrでなければそれを呼び出す。 Option& operator=(const std::string& v); // int型への変換子。currentValueの文字列がint化されて返る。 operator int() const; // string型への変換子。currentValueの文字列がそのまま返る。 operator std::string() const; private: // coutにoperator << でオプション項目一式をUCIプロトコルで出力するためのもの。 friend std::ostream& operator<<(std::ostream&, const OptionsMap&); // このOption項目(がint型のとき)のデフォルト値、現在の値 // typeは、UCIプロトコルのoptionコマンドで通知するこのOption項目の型。 // 例) int型ならspin(スピンコントロールを表示するためにこれを指定する) std::string defaultValue, currentValue, type; // このOption項目(がint型のとき)の最小値、最大値 int min, max; // このOption項目の通しナンバー size_t idx; // このOption項目がUCIプロトコルで変更されたときに呼び出されるハンドラ Fn* on_change; }; /// init() initializes the UCI options to their hard coded default values // init()は引数で渡されたUCI option設定をhard codeされたデフォルト値で初期化する。 void init(OptionsMap&); // 応答用ループ。quitコマンドが与えられるまで抜けない。 // コマンドラインからの文字列を渡すと、それをコマンドとして実行してくれる。 // uci.cppのほうに実装がある。 void loop(const std::string&); } // namespace UCI extern UCI::OptionsMap Options; #endif // #ifndef UCIOPTION_H_INCLUDED |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #include #include #include #include "evaluate.h" #include "misc.h" #include "thread.h" #include "tt.h" #include "ucioption.h" using std::string; // UCI経由で設定されたoption UCI::OptionsMap Options; // Global object namespace UCI { /// 'On change' actions, triggered by an option's value change // --- optionの値が変更されたことをトリガーとして、呼び出される'On change'ハンドラ。 // デバッグログの記録を行うためのハンドラ // "Write Debug Log" optionに応じて。 void on_logger(const Option& o) { start_logger(o); } // 評価関数の初期化を行うためのハンドラ void on_eval(const Option&) { Eval::init(); } // Thread数が変更されたときに呼び出されるハンドラ // スレッド数の設定は、splitの設定等に関係するのでここで、専用の設定関数を呼び出す。 void on_threads(const Option&) { Threads.read_uci_options(); } // ハッシュサイズが変更されたときに呼び出されるハンドラ void on_hash_size(const Option& o) { TT.set_size(o); } // 置換表クリアのためのハンドラ void on_clear_hash(const Option&) { TT.clear(); } /// Our case insensitive less() function as required by UCI protocol // UCIプロトコルで必要とされる大文字小文字を区別しない less()関数。 bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), [](char c1, char c2) { return tolower(c1) < tolower(c2); }); } /// init() initializes the UCI options to their hard coded default values // init()は引数で渡されたUCI option設定をhard codeされたデフォルト値で初期化する。 void init(OptionsMap& o) { // デバッグログの書き出しコマンド o["Write Debug Log"] = Option(false, on_logger); // 探索ログを書き出すかどうかのフラグ o["Write Search Log"] = Option(false); // 探索ログを書き出すときの、そのファイル名 o["Search Log Filename"] = Option("SearchLog.txt"); // Options["Book File"] = 定跡ファイルのファイル名 o["Book File"] = Option("book.bin"); // Options["Best Book Move"] = これがtrueだと定跡DBのその局面のなかで最大のスコアのものを選択。 // さもなくば、定跡DB上の指し手からランダムに1つ選ぶ。 o["Best Book Move"] = Option(false); // 引き分けをどう扱うか。 // Contempt≒恥辱 // 引き分けをOptions["Contempt Factor"]は歩の何枚分の値だと評価するか。 // プラスの値にすると自分がやや損でも千日手を打開する。 o["Contempt Factor"] = Option(0, -50, 50); o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); o["Space"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Cowardice"] = Option(100, 0, 200, on_eval); // 最小split深さ。 o["Min Split Depth"] = Option(0, 0, 12, on_threads); // split point当たりの最大スレッド数 // この値はThreadPool::maxThreadsPerSplitPointに反映される。 o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); // スレッド数の設定 o["Threads"] = Option(1, 1, MAX_THREADS, on_threads); // Threadがidle_loop()のなかでidle中にsleep(0)で待機するのかどうかのフラグ。 // このときCPU負荷は高くなるがレスポンスは良くなる。 o["Idle Threads Sleep"] = Option(false); // 置換表のサイズ、MB単位。 // 32MB , 1~8192MB、変更時に呼び出されるハンドラ = on_hash_size o["Hash"] = Option(32, 1, 8192, on_hash_size); // Clear Hashコマンドが送れられてきた場合、置換表をクリアする。 o["Clear Hash"] = Option(on_clear_hash); // ponder(相手の思考中に思考するのか)の設定。 // →これは将棋所からだとGUI側から渡される。 o["Ponder"] = Option(true); // Options["OwnBook"]は定跡ファイルを持っているかのフラグ。 // "go infinite"のときは定跡を使わない思考(のようだ) o["OwnBook"] = Option(false); // Search::PVSizeに反映される。 // PVをRootMoveに対して何個目まで求めるのか(ハンディキャップ戦用) o["MultiPV"] = Option(1, 1, 500); // Search::Skillに反映される。 // ハンディキャップ戦用のパラメーター(小さいほどハンディキャップが大きい) // default == 20で、20だとハンディキャップなし。 o["Skill Level"] = Option(20, 0, 20); // 持ち時間制御のための仕組み o["Emergency Move Horizon"] = Option(40, 0, 50); o["Emergency Base Time"] = Option(60, 0, 30000); o["Emergency Move Time"] = Option(30, 0, 5000); o["Minimum Thinking Time"] = Option(20, 0, 5000); // 思考時間において序盤重視のパラメーターを100分率で。 // 100 = 通常、200 = 序盤重視、みたいな感じか。 // default = 70のようだが…。 o["Slow Mover"] = Option(70, 10, 1000); // Chess960というチェスのルールの一部のようだ。局面初期化のときにこのフラグを用いてある。 o["UCI_Chess960"] = Option(false); // 評価関数のクリアを行うためのコマンド o["UCI_AnalyseMode"] = Option(false, on_eval); } /// operator<<() is used to print all the options default values in chronological /// insertion order (the idx field) and in the format defined by the UCI protocol. // operator<<()は、すべてのオプション項目のデフォルト値を時系列順(idxメンバ変数)に、 // UCIプロトコルで定義されているフォーマットにおいて出力するのに用いる。 std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { // OptionMapは単なるstd::mapなのでforで回してもidx順とは限らないので、om.size()回だけループしながら // 該当idxを持つOptionを探し、それを表示させていく。 for (size_t idx = 0; idx < om.size(); ++idx) { auto it = std::find_if(om.begin(), om.end(), [idx](const OptionsMap::value_type& p) { return p.second.idx == idx; }); const Option& o = it->second; os << "\noption name " << it->first << " type " << o.type; if (o.type != "button") os << " default " << o.defaultValue; if (o.type == "spin") os << " min " << o.min << " max " << o.max; } return os; } /// Option c'tors and conversion operators // Optionクラスのコンストラクターと変換子。 // ※ コンストラクターでは、ハンドラは呼び出されない。 // そのため、置換表、スレッド数などに関しては別のところでハンドラを呼び出す必要がある。 // 置換表の確保はmain()で行っており、スレッドの確保は、 // main()からThread::init()が呼び出され、そのなかでread_uci_options()が呼び出されておこなわれる。 // これらはあまりいい実装ではない気がするのだが…。 Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) { defaultValue = currentValue = v; } Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) { defaultValue = currentValue = (v ? "true" : "false"); } Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f) {} Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f) { defaultValue = currentValue = std::to_string(v); } Option::operator int() const { assert(type == "check" || type == "spin"); return (type == "spin" ? stoi(currentValue) : currentValue == "true"); } Option::operator std::string() const { assert(type == "string"); return currentValue; } /// operator=() updates currentValue and triggers on_change() action. It's up to /// the GUI to check for option's limits, but we could receive the new value from /// the user by console window, so let's check the bounds anyway. // operator=()は、currentValueの値を更新し、on_change()ハンドラを呼び出す。(コンストラクタでFnとして渡されていれば) // optionの範囲のチェックすることは、GUI側の責任であるが、しかし、コンソールウィンドウから // ユーザーによる新しい値を受け取ることもありうるため、範囲チェックはいずれにせよ行おう。 Option& Option::operator=(const string& v) { // 左辺の変数で格納している型が右辺の変数で格納している型を保存できる型でなければならない。 // そのため、左辺で格納している型が空の場合は、このコピーコンストラクタは使えない。 assert(!type.empty()); // 格納できる型でなければ、何もせずにreturn。 if ( (type != "button" && v.empty()) || (type == "check" && v != "true" && v != "false") || (type == "spin" && (stoi(v) < min || stoi(v) > max))) return *this; // check,spin,stringならば現在の値をコピー。buttonは、ただの押しボタンなので現在の値というのはない。 if (type != "button") currentValue = v; // 値がセットされたのでハンドラを呼び出しておる。 if (on_change) (*on_change)(*this); return *this; } } // namespace UCI |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #include #include #include #include #include "evaluate.h" #include "notation.h" #include "position.h" #include "search.h" #include "thread.h" #include "ucioption.h" using namespace std; extern void benchmark(const Position& pos, istream& is); namespace { // FEN string of the initial position, normal chess // チェスの平手の開始局面をFEN表記で表現したもの。 const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // Keep track of position keys along the setup moves (from start position to the // position just before to start searching). Needed by repetition draw detection. // セットアップされた局面(UCIのpositionコマンドで渡された開始局面から探索を開始する直前の局面)までの // 局面のキーの追跡をする。 千日手の検出に必要とされる。 // ※ globalに確保してあり、実体はStateInfoのstackである。 // これ、search.cppのほうにも同名の変数がある。 // goコマンドのときに使われるが、thread.cppのなかのstart_thinking()で、このSetupStatesはmoveしてしまうので // 結局のところ要らないようなのだが…。 Search::StateStackPtr SetupStates; // UCIコマンドの"setoption"コマンドに対して呼び出されるハンドラ void setoption(istringstream& up); // UCIの"position"コマンドに対して呼び出されるハンドラ。 void position(Position& pos, istringstream& up); // 思考時間等を受け取り、探索を開始する。GUIから"go"コマンドを受け取ったときに呼び出される。 void go(const Position& pos, istringstream& up); } /// Wait for a command from the user, parse this text string as an UCI command, /// and call the appropriate functions. Also intercepts EOF from stdin to ensure /// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI /// commands, the function also supports a few debug commands. // ユーザーからのコマンドを受付、UCIコマンドとしてのテキスト文字列をパース(解析)し、 // 適切な関数を呼び出す。GUIが予期せず死亡しても、丁寧に終了するのを確実にするため、 // stdinからのEOFの横取りも行う。 // それに加えて、いくつかのデバッグコマンドをサポートする機能を持つUCIコマンドも。 void UCI::loop(const string& args) { // StartFENは開始局面のFEN文字列。 Position pos(StartFEN, false, Threads.main()); // The root position string token, cmd = args; do { // getlineはEOFに遭遇するとfalseになる。 // ここで入力があるまでブロックしている。 // また、コマンドライン引数があれば、標準入力からの入力を受け付けずにそれを解釈する。 if (args.empty() && !getline(cin, cmd)) // Block here waiting for input cmd = "quit"; // 引数がないか、getlineが失敗したならquit扱い。 // スペース区切りで文字列がUCI文字列がやってくるのでその解析用。 istringstream is(cmd); // skipwsはスペースなどをスキップするモードに移行するためのもの。 // 入力がないとtokenが前回の内容のままになるので、token = ""とか事前にしたほうがいいのでは。 is >> skipws >> token; if (token == "quit" || token == "stop" || token == "ponderhit") { // GUI sends 'ponderhit' to tell us to ponder on the same move the // opponent has played. In case Signals.stopOnPonderhit is set we are // waiting for 'ponderhit' to stop the search (for instance because we // already ran out of time), otherwise we should continue searching but // switching from pondering to normal search. // GUIは対戦相手が予想手を指したことを我々に知らせるために'ponderhit'を送る。 // Signals.stopOnPonderhitがセットされている場合、探索を停止させるために我々は'ponderhit' // を待っている(例えば、我々はすでに探索時間を使いきっている)、さもなくば、 // 我々は探索し続けるが、ponderingから通常の探索モードに切り替える必要がある。 if (token != "ponderhit" || Search::Signals.stopOnPonderhit) { // 1) 'quit','stop' である // 2) 'ponderhit' で Search::Signals.stopOnPoderhit状態である。 // Search::Signals.stopOnPoderhitは、今回のための探索時間を使いきっているの意味。 Search::Signals.stop = true; // mainスレッドが寝ているかも知れないのでnotify_one()で起こしてやる。 Threads.main()->notify_one(); // Could be sleeping } else // 'ponderhit'でかつ、まだ思考時間を使いきっていないなら、 // ponder解除して探索継続 Search::Limits.ponder = false; } else if (token == "perft" && (is >> token)) // Read perft depth { // パフォーマンステストモードらしい // perft = Perft, (performance test, move path enumeration) // cf. http://chessprogramming.wikispaces.com/perft stringstream ss; ss << Options["Hash"] << " " << Options["Threads"] << " " << token << " current perft"; benchmark(pos, ss); } else if (token == "key") // 局面のハッシュキーなどを出力する。(デバッグ用) sync_cout << hex << uppercase << setfill('0') << "position key: " << setw(16) << pos.key() << "\nmaterial key: " << setw(16) << pos.material_key() << "\npawn key: " << setw(16) << pos.pawn_key() << dec << sync_endl; else if (token == "uci") sync_cout << "id name " << engine_info(true) << "\n" << Options << "\nuciok" << sync_endl; else if (token == "eval") { // 評価値を返す。(デバッグ用) Search::RootColor = pos.side_to_move(); // Ensure it is set sync_cout << Eval::trace(pos) << sync_endl; } // "ucinewgame"は無視する。解読できないコマンド扱いして"Unknown command"を返してはまずいので。 // ※ このループでは、ucinewgame中の状態であるかどうかは問題としていない。 else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } // 思考開始 else if (token == "go") go(pos, is); // 局面の設定 else if (token == "position") position(pos, is); // optionの設定 else if (token == "setoption") setoption(is); // 盤面を180度回転させる。評価関数の対称性があるかどうかのバグがないか調べるときなどに使う。 else if (token == "flip") pos.flip(); // ベンチマーク用のコマンド else if (token == "bench") benchmark(pos, is); // 局面のASCII文字による綺麗な盤面の出力。(デバッグ用) else if (token == "d") sync_cout << pos.pretty() << sync_endl; // "isready"に対しては"readyok"を返すだけ。何か思考エンジンのために時間のかかる // 初期化が必要ならばここで行うべき。 else if (token == "isready") sync_cout << "readyok" << sync_endl; // それら以外ならば未知のコマンドであることを出力。(ユーザーの手入力によるものだろうから) else sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && args.empty()); // Args have one-shot behaviour // ループ条件) 'quit'ではなく、argsが空でなければならない。 // Argsは一度っきりのふるまい(ベンチマークの起動など)なので、 // これが設定されていたのならばループせずに終了する。 // 探索中は終了できないので終了を待つ。 Threads.wait_for_think_finished(); // Cannot quit while search is running } namespace { // position() is called when engine receives the "position" UCI command. // The function sets up the position described in the given fen string ("fen") // or the starting position ("startpos") and then makes the moves given in the // following move list ("moves"). // position()は、思考エンジンがUCIコマンドとして"position"を受け取ったときに呼び出される。 // この関数は、与えられたfen文字列("fen")もしくは開始局面("startpos")で書かれた局面をセットアップして、 // 後続の指し手リスト("moves")で与えられる指し手を進める。 void position(Position& pos, istringstream& is) { Move m; string token, fen; is >> token; if (token == "startpos") { // 初期局面として初期局面のFEN形式の入力が与えられたとみなして処理する。 fen = StartFEN; is >> token; // Consume "moves" token if any // もしあるなら"moves"トークンを消費する。 } // 局面がfen形式で指定されているなら、その局面を読み込む。 // USI(将棋用プロトコル)だとここの文字列は"fen"ではなく"sfen" else if (token == "fen") while (is >> token && token != "moves") fen += token + " "; else return; pos.set(fen, Options["UCI_Chess960"], Threads.main()); SetupStates = Search::StateStackPtr(new std::stack // Parse move list (if any) // 指し手のリストをパースする(あるなら) while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE) { // 1手進めるごとにStateInfoが積まれていく。これは千日手の検出のために必要。 SetupStates->push(StateInfo()); pos.do_move(m, SetupStates->top()); } } // setoption() is called when engine receives the "setoption" UCI command. The // function updates the UCI option ("name") to the given value ("value"). // setoption() は、思考エンジンが"setoption" UCIコマンドを受信したときに呼び出される。 // この関数はUCI option("name")を与えられた値("value")に更新する。 void setoption(istringstream& is) { string token, name, value; is >> token; // Consume "name" token // Read option name (can contain spaces) while (is >> token && token != "value") name += string(" ", !name.empty()) + token; // Read option value (can contain spaces) while (is >> token) value += string(" ", !value.empty()) + token; if (Options.count(name)) Options[name] = value; else sync_cout << "No such option: " << name << sync_endl; } // go() is called when engine receives the "go" UCI command. The function sets // the thinking time and other parameters from the input string, and starts // the search. // go()は、思考エンジンがUCIコマンドの"go"を受け取ったときに呼び出される。 // この関数は、入力文字列から思考時間とその他のパラメーターをセットし、探索を開始する。 void go(const Position& pos, istringstream& is) { Search::LimitsType limits; // 探索開始局面で特定の指し手だけ探索する場合の指し手集合。 vector string token; // cf. // UCI protcol : http://www.glaurungchess.com/shogi/usi.html while (is >> token) { // 探索すべき指し手。(探索開始局面から特定の初手だけ探索させるとき) if (token == "searchmoves") // 残りの指し手すべてをsearchMovesに突っ込む。 while (is >> token) searchMoves.push_back(move_from_uci(pos, token)); // 先手、後手の残り時間。[ms] else if (token == "wtime") is >> limits.time[WHITE]; else if (token == "btime") is >> limits.time[BLACK]; // 1手ごとの増加時間。[ms] // 秒読みに似た概念だが、持ち時間が切れていなくともこれが加算されるところが異なる。 else if (token == "winc") is >> limits.inc[WHITE]; else if (token == "binc") is >> limits.inc[BLACK]; else if (token == "movestogo") is >> limits.movestogo; // この探索深さで探索を打ち切る else if (token == "depth") is >> limits.depth; // この探索ノード数で探索を打ち切る else if (token == "nodes") is >> limits.nodes; // 次の時間制御までx手あるという意味らしい。 // これが指定されておらず、"wtime"と"btime"を受信したのならば切れ負けの意味。 else if (token == "movetime") is >> limits.movetime; // 詰み探索。このあとには手数が入っており、その手数以内に詰むかどうかを判定する。 // USIプロトコルでは、ここは探索のための時間制限に変更となっている。 else if (token == "mate") is >> limits.mate; // 時間無制限。 else if (token == "infinite") limits.infinite = true; // ponderモードでの思考。 else if (token == "ponder") limits.ponder = true; } Threads.start_thinking(pos, limits, searchMoves, SetupStates); } } |