/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and /// inserts the PV back into the TT. This makes sure the old PV moves are searched /// first, even if the old TT entries have been overwritten. internal void insert_pv_in_tt(Position pos) { var st = new StateInfoWrapper(); foreach (var m in pv) { Debug.Assert(new MoveList(GenType.LEGAL, pos).contains(m)); bool ttHit; var tte = TranspositionTable.probe(pos.key(), out ttHit); if (!ttHit || tte.move() != m) // Don't overwrite correct entries { tte.save( pos.key(), Value.VALUE_NONE, Bound.BOUND_NONE, Depth.DEPTH_NONE, m, Value.VALUE_NONE, TranspositionTable.generation()); } var current = st[st.current]; st++; pos.do_move(m, current, pos.gives_check(m, new CheckInfo(pos))); } for (var i = pv.Count; i > 0;) { pos.undo_move(pv[--i]); } }
// ThreadPool::start_thinking() wakes up the main thread sleeping in // MainThread::idle_loop() and starts a new search, then returns immediately. internal static void start_thinking(Position pos, LimitsType limits, StateInfoWrapper states) { main().join(); Search.Signals.stopOnPonderhit = Search.Signals.firstRootMove = false; Search.Signals.stop = Search.Signals.failedLowAtRoot = false; Search.RootMoves.Clear(); Search.RootPos = new Position(pos); Search.Limits = limits; var current = states[states.current]; if (current != null) // If we don't set a new position, preserve current state { Search.SetupStates = states; // Ownership transfer here Debug.Assert(current != null); } var ml = new MoveList(GenType.LEGAL, pos); for (var index = ml.begin(); index < ml.end(); index++) { var m = ml.moveList.table[index]; if (limits.searchmoves.Count == 0 || limits.searchmoves.FindAll(move => move == m.Move).Count > 0) { Search.RootMoves.Add(new RootMove(m)); } } main().thinking = true; main().notify_one(); // Wake up main thread: 'thinking' must be already set }
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// of positions for a given limit each. There are five parameters: the /// transposition table size, the number of search threads that should /// be used, the limit value spent for each position (optional, default is /// depth 13), an optional file name where to look for positions in FEN /// format (defaults are the positions defined above) and the type of the /// limit value: depth (default), time in millisecs or number of nodes. internal static void benchmark(Position current, Stack<string> stack) { var fens = new List<string>(); var limits = new LimitsType(); long nodes = 0; // Assign default values to missing arguments var ttSize = (stack.Count > 0) ? (stack.Pop()) : "16"; var threads = (stack.Count > 0) ? (stack.Pop()) : "1"; #if DEBUG var limit = (stack.Count > 0) ? (stack.Pop()) : "7"; #else var limit = (stack.Count > 0) ? (stack.Pop()) : "13"; #endif var fenFile = (stack.Count > 0) ? (stack.Pop()) : "default"; var limitType = (stack.Count > 0) ? (stack.Pop()) : "depth"; OptionMap.Instance["Hash"].v = ttSize; OptionMap.Instance["Threads"].v = threads; Search.reset(); if (limitType == "time") { limits.movetime = int.Parse(limit); // movetime is in ms } else if (limitType == "nodes") { limits.nodes = ulong.Parse(limit); } else if (limitType == "mate") { limits.mate = int.Parse(limit); } else { limits.depth = int.Parse(limit); } if (fenFile == "default") { fens.AddRange(Defaults); } else if (fenFile == "current") { fens.Add(current.fen()); } else { var sr = new StreamReader(fenFile, true); var fensFromFile = sr.ReadToEnd(); sr.Close(); sr.Dispose(); var split = fensFromFile.Replace("\r", "").Split('\n'); fens.AddRange(from fen in split where fen.Trim().Length > 0 select fen.Trim()); } var time = Stopwatch.StartNew(); for (var i = 0; i < fens.Count; ++i) { var pos = new Position(fens[i], bool.Parse(OptionMap.Instance["UCI_Chess960"].v), ThreadPool.main()); Output.WriteLine($"\nPosition: {i + 1} / {fens.Count}"); if (limitType == "perft") { nodes += Search.perft(true, pos, limits.depth*Depth.ONE_PLY); } else { var st = new StateInfoWrapper(); ThreadPool.start_thinking(pos, limits, st); ThreadPool.main().join(); nodes += Search.RootPos.nodes_searched(); } } var elapsed = time.ElapsedMilliseconds; // Ensure positivity to avoid a 'divide by zero' Output.Write("\n==========================="); Output.Write($"\nTotal time (ms) : {elapsed}"); Output.Write($"\nNodes searched : {nodes}"); Output.WriteLine($"\nNodes/second : {1000*nodes/elapsed}"); }