| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <cassert> |
| #include <cmath> |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
|
|
| #include "evaluate.h" |
| #include "movegen.h" |
| #include "position.h" |
| #include "search.h" |
| #include "thread.h" |
| #include "timeman.h" |
| #include "tt.h" |
| #include "uci.h" |
| #include "syzygy/tbprobe.h" |
|
|
| using namespace std; |
|
|
| namespace Stockfish { |
|
|
| extern vector<string> setup_bench(const Position&, istream&); |
|
|
| namespace { |
|
|
| |
| const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; |
|
|
|
|
| |
| |
| |
| |
|
|
| void position(Position& pos, istringstream& is, StateListPtr& states) { |
|
|
| Move m; |
| string token, fen; |
|
|
| is >> token; |
|
|
| if (token == "startpos") |
| { |
| fen = StartFEN; |
| is >> token; |
| } |
| else if (token == "fen") |
| while (is >> token && token != "moves") |
| fen += token + " "; |
| else |
| return; |
|
|
| states = StateListPtr(new std::deque<StateInfo>(1)); |
| pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); |
|
|
| |
| while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) |
| { |
| states->emplace_back(); |
| pos.do_move(m, states->back()); |
| } |
| } |
|
|
| |
| |
|
|
| void trace_eval(Position& pos) { |
|
|
| StateListPtr states(new std::deque<StateInfo>(1)); |
| Position p; |
| p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); |
|
|
| Eval::NNUE::verify(); |
|
|
| sync_cout << "\n" << Eval::trace(p) << sync_endl; |
| } |
|
|
|
|
| |
| |
|
|
| void setoption(istringstream& is) { |
|
|
| string token, name, value; |
|
|
| is >> token; |
|
|
| |
| while (is >> token && token != "value") |
| name += (name.empty() ? "" : " ") + token; |
|
|
| |
| while (is >> token) |
| value += (value.empty() ? "" : " ") + token; |
|
|
| if (Options.count(name)) |
| Options[name] = value; |
| else |
| sync_cout << "No such option: " << name << sync_endl; |
| } |
|
|
|
|
| |
| |
| |
|
|
| void go(Position& pos, istringstream& is, StateListPtr& states) { |
|
|
| Search::LimitsType limits; |
| string token; |
| bool ponderMode = false; |
|
|
| limits.startTime = now(); |
|
|
| while (is >> token) |
| if (token == "searchmoves") |
| while (is >> token) |
| limits.searchmoves.push_back(UCI::to_move(pos, token)); |
|
|
| else if (token == "wtime") is >> limits.time[WHITE]; |
| else if (token == "btime") is >> limits.time[BLACK]; |
| 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; |
| else if (token == "movetime") is >> limits.movetime; |
| else if (token == "mate") is >> limits.mate; |
| else if (token == "perft") is >> limits.perft; |
| else if (token == "infinite") limits.infinite = 1; |
| else if (token == "ponder") ponderMode = true; |
|
|
| Threads.start_thinking(pos, states, limits, ponderMode); |
| } |
|
|
|
|
| |
| |
| |
|
|
| void bench(Position& pos, istream& args, StateListPtr& states) { |
|
|
| string token; |
| uint64_t num, nodes = 0, cnt = 1; |
|
|
| vector<string> list = setup_bench(pos, args); |
| num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; }); |
|
|
| TimePoint elapsed = now(); |
|
|
| for (const auto& cmd : list) |
| { |
| istringstream is(cmd); |
| is >> skipws >> token; |
|
|
| if (token == "go" || token == "eval") |
| { |
| cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl; |
| if (token == "go") |
| { |
| go(pos, is, states); |
| Threads.main()->wait_for_search_finished(); |
| nodes += Threads.nodes_searched(); |
| } |
| else |
| trace_eval(pos); |
| } |
| else if (token == "setoption") setoption(is); |
| else if (token == "position") position(pos, is, states); |
| else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } |
| } |
|
|
| elapsed = now() - elapsed + 1; |
|
|
| dbg_print(); |
|
|
| cerr << "\n===========================" |
| << "\nTotal time (ms) : " << elapsed |
| << "\nNodes searched : " << nodes |
| << "\nNodes/second : " << 1000 * nodes / elapsed << endl; |
| } |
|
|
| |
| |
| int win_rate_model(Value v, int ply) { |
|
|
| |
| double m = std::min(240, ply) / 64.0; |
|
|
| |
| |
| |
| constexpr double as[] = { -0.58270499, 2.68512549, 15.24638015, 344.49745382}; |
| constexpr double bs[] = { -2.65734562, 15.96509799, -20.69040836, 73.61029937 }; |
|
|
| |
| static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); |
|
|
| double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; |
| double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; |
|
|
| |
| double x = std::clamp(double(v), -4000.0, 4000.0); |
|
|
| |
| return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); |
| } |
|
|
| } |
|
|
|
|
| |
| |
| |
| |
| |
|
|
| void UCI::loop(int argc, char* argv[]) { |
|
|
| Position pos; |
| string token, cmd; |
| StateListPtr states(new std::deque<StateInfo>(1)); |
|
|
| pos.set(StartFEN, false, &states->back(), Threads.main()); |
|
|
| for (int i = 1; i < argc; ++i) |
| cmd += std::string(argv[i]) + " "; |
|
|
| do { |
| if (argc == 1 && !getline(cin, cmd)) |
| cmd = "quit"; |
|
|
| istringstream is(cmd); |
|
|
| token.clear(); |
| is >> skipws >> token; |
|
|
| if ( token == "quit" |
| || token == "stop") |
| Threads.stop = true; |
|
|
| |
| |
| |
| |
| else if (token == "ponderhit") |
| Threads.main()->ponder = false; |
|
|
| else if (token == "uci") |
| sync_cout << "id name " << engine_info(true) |
| << "\n" << Options |
| << "\nuciok" << sync_endl; |
|
|
| else if (token == "setoption") setoption(is); |
| else if (token == "go") go(pos, is, states); |
| else if (token == "position") position(pos, is, states); |
| else if (token == "ucinewgame") Search::clear(); |
| else if (token == "isready") sync_cout << "readyok" << sync_endl; |
|
|
| |
| |
| else if (token == "flip") pos.flip(); |
| else if (token == "bench") bench(pos, is, states); |
| else if (token == "d") sync_cout << pos << sync_endl; |
| else if (token == "eval") trace_eval(pos); |
| else if (token == "compiler") sync_cout << compiler_info() << sync_endl; |
| else if (token == "export_net") |
| { |
| std::optional<std::string> filename; |
| std::string f; |
| if (is >> skipws >> f) |
| filename = f; |
| Eval::NNUE::save_eval(filename); |
| } |
| else if (token == "--help" || token == "help" || token == "--license" || token == "license") |
| sync_cout << "\nStockfish is a powerful chess engine for playing and analyzing." |
| "\nIt is released as free software licensed under the GNU GPLv3 License." |
| "\nStockfish is normally used with a graphical user interface (GUI) and implements" |
| "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc." |
| "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme" |
| "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" << sync_endl; |
| else if (!token.empty() && token[0] != '#') |
| sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl; |
|
|
| } while (token != "quit" && argc == 1); |
| } |
|
|
|
|
| |
| |
| |
| |
| |
|
|
| string UCI::value(Value v) { |
|
|
| assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); |
|
|
| stringstream ss; |
|
|
| if (abs(v) < VALUE_MATE_IN_MAX_PLY) |
| ss << "cp " << v * 100 / NormalizeToPawnValue; |
| else |
| ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; |
|
|
| return ss.str(); |
| } |
|
|
|
|
| |
| |
|
|
| string UCI::wdl(Value v, int ply) { |
|
|
| stringstream ss; |
|
|
| int wdl_w = win_rate_model( v, ply); |
| int wdl_l = win_rate_model(-v, ply); |
| int wdl_d = 1000 - wdl_w - wdl_l; |
| ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; |
|
|
| return ss.str(); |
| } |
|
|
|
|
| |
|
|
| std::string UCI::square(Square s) { |
| return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; |
| } |
|
|
|
|
| |
| |
| |
| |
|
|
| string UCI::move(Move m, bool chess960) { |
|
|
| Square from = from_sq(m); |
| Square to = to_sq(m); |
|
|
| if (m == MOVE_NONE) |
| return "(none)"; |
|
|
| if (m == MOVE_NULL) |
| return "0000"; |
|
|
| if (type_of(m) == CASTLING && !chess960) |
| to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); |
|
|
| string move = UCI::square(from) + UCI::square(to); |
|
|
| if (type_of(m) == PROMOTION) |
| move += " pnbrqk"[promotion_type(m)]; |
|
|
| return move; |
| } |
|
|
|
|
| |
| |
|
|
| Move UCI::to_move(const Position& pos, string& str) { |
|
|
| if (str.length() == 5) |
| str[4] = char(tolower(str[4])); |
|
|
| for (const auto& m : MoveList<LEGAL>(pos)) |
| if (str == UCI::move(m, pos.is_chess960())) |
| return m; |
|
|
| return MOVE_NONE; |
| } |
|
|
| } |
|
|