public void Run(object arguments) { string[] args = (string[])arguments; Plug.Write(Utils.engine_info()); Plug.Write(Constants.endl); CheckInfoBroker.init(); EvalInfoBroker.init(); SwapListBroker.init(); MovesSearchedBroker.init(); PositionBroker.init(); StateInfoArrayBroker.init(); MListBroker.init(); LoopStackBroker.init(); MovePickerBroker.init(); StateInfoBroker.init(); Utils.init(); Book.init(); Position.init(); KPKPosition.init(); Endgame.init(); Search.init(); Evaluate.init(); Threads.init(); // .Net warmup sequence Plug.IsWarmup = true; Position pos = new Position(Uci.StartFEN, false, Threads.main_thread()); Stack <string> stack = Utils.CreateStack("go depth 7"); Uci.go(pos, stack); Threads.wait_for_search_finished(); Plug.IsWarmup = false; StringBuilder sb = new StringBuilder(); for (int i = 1; i < args.Length; i++) { sb.Append(args[i]).Append(" "); } Uci.uci_loop(sb.ToString()); Threads.exit(); }
internal Thread(ThreadLoopType lt, ManualResetEvent initEvent) { is_searching = do_exit = false; maxPly = splitPointsCnt = 0; curSplitPoint = null; loopType = lt; idx = Threads.size(); do_sleep = loopType != ThreadLoopType.Main; // Avoid a race with start_searching() for (int j = 0; j < Constants.MAX_SPLITPOINTS_PER_THREAD; j++) { splitPoints[j] = new SplitPoint(); } ThreadPool.QueueUserWorkItem(this.StartThread, initEvent); }
internal Thread(ThreadLoopType lt, ManualResetEvent initEvent) { is_searching = do_exit = false; maxPly = splitPointsCnt = 0; curSplitPoint = null; loopType = lt; idx = Threads.size(); do_sleep = loopType != ThreadLoopType.Main; // Avoid a race with start_searching() for (int j = 0; j < Constants.MAX_SPLITPOINTS_PER_THREAD; j++) { splitPoints[j] = new SplitPoint(); } #if WINDOWS_RT Windows.Foundation.IAsyncAction action = Windows.System.Threading.ThreadPool.RunAsync(delegate { StartThread(initEvent); }, WorkItemPriority.Normal); #else ThreadPool.QueueUserWorkItem(this.StartThread, initEvent); #endif }
// set_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"). internal static void set_position(Position pos, Stack <string> stack) { Move m; string token, fen = string.Empty; token = stack.Pop(); if (token == "startpos") { fen = StartFEN; if (stack.Count > 0) { token = stack.Pop(); } // Consume "moves" token if any } else if (token == "fen") { while ((stack.Count > 0) && (token = stack.Pop()) != "moves") { fen += token + " "; } } else { return; } pos.from_fen(fen, bool.Parse(OptionMap.Instance["UCI_Chess960"].v), Threads.main_thread()); // Parse move list (if any) while ((stack.Count > 0) && (m = Utils.move_from_uci(pos, token = stack.Pop())) != MoveC.MOVE_NONE) { pos.do_move(m, StateRingBuf[SetupStatePos]); // Increment pointer to StateRingBuf circular buffer SetupStatePos = (SetupStatePos + 1) % 102; } }
internal static void on_threads(UCIOption opt) { Threads.read_uci_options(null); }
internal static int SetupStatePos = 0; // *SetupState = StateRingBuf; /// 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. internal static void uci_loop(string args) { for (int i = 0; i < 102; i++) { StateRingBuf[i] = new StateInfo(); } Position pos = new Position(StartFEN, false, Threads.main_thread()); // The root position string cmd, token = string.Empty; while (token != "quit") { if (args.Length > 0) { cmd = args; } else if (String.IsNullOrEmpty(cmd = Plug.ReadLine())) // Block here waiting for input { cmd = "quit"; } Stack <string> stack = Utils.CreateStack(cmd); token = stack.Pop(); if (token == "quit" || token == "stop") { Search.SignalsStop = true; Threads.wait_for_search_finished(); // Cannot quit while threads are running } else if (token == "ponderhit") { // The opponent has played the expected move. GUI sends "ponderhit" if // we were told to ponder on the same move the opponent has played. We // should continue searching but switching from pondering to normal search. Search.Limits.ponder = false; if (Search.SignalsStopOnPonderhit) { Search.SignalsStop = true; Threads.main_thread().wake_up(); // Could be sleeping } } else if (token == "go") { go(pos, stack); } else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } else if (token == "isready") { Plug.Write("readyok"); Plug.Write(Constants.endl); } else if (token == "position") { set_position(pos, stack); } else if (token == "setoption") { set_option(stack); } else if (token == "d") { pos.print(0); } else if (token == "flip") { pos.flip(); } else if (token == "eval") { Plug.Write(Evaluate.trace(pos)); Plug.Write(Constants.endl); } else if (token == "bench") { Benchmark.benchmark(pos, stack); } else if (token == "key") { Plug.Write("key: "); Plug.Write(String.Format("{0:X}", pos.key())); Plug.Write("\nmaterial key: "); Plug.Write(pos.material_key().ToString()); Plug.Write("\npawn key: "); Plug.Write(pos.pawn_key().ToString()); Plug.Write(Constants.endl); } else if (token == "uci") { Plug.Write("id name "); Plug.Write(Utils.engine_info(true)); Plug.Write("\n"); Plug.Write(OptionMap.Instance.ToString()); Plug.Write("\nuciok"); Plug.Write(Constants.endl); } else if (token == "perft") { token = stack.Pop(); // Read depth Stack <string> ss = Utils.CreateStack( string.Format("{0} {1} {2} current perft", OptionMap.Instance["Hash"].v, OptionMap.Instance["Threads"].v, token) ); Benchmark.benchmark(pos, ss); } else { Plug.Write("Unknown command: "); Plug.Write(cmd); Plug.Write(Constants.endl); } if (args.Length > 0) // Command line arguments have one-shot behaviour { Threads.wait_for_search_finished(); break; } } }
// go() is called when engine receives the "go" UCI command. The function sets // the thinking time and other parameters from the input string, and then starts // the main searching thread. internal static void go(Position pos, Stack <string> stack) { string token = string.Empty; LimitsType limits = new LimitsType(); List <Move> searchMoves = new List <Phase>(); while (stack.Count > 0) { token = stack.Pop(); if (token == "wtime") { limits.time[ColorC.WHITE] = int.Parse(stack.Pop()); } else if (token == "btime") { limits.time[ColorC.BLACK] = int.Parse(stack.Pop()); } else if (token == "winc") { limits.inc[ColorC.WHITE] = int.Parse(stack.Pop()); } else if (token == "binc") { limits.inc[ColorC.BLACK] = int.Parse(stack.Pop()); } else if (token == "movestogo") { limits.movesToGo = int.Parse(stack.Pop()); } else if (token == "depth") { limits.depth = int.Parse(stack.Pop()); } else if (token == "nodes") { limits.nodes = int.Parse(stack.Pop()); } else if (token == "movetime") { limits.movetime = int.Parse(stack.Pop()); } else if (token == "infinite") { limits.infinite = 1; } else if (token == "ponder") { limits.ponder = true; } else if (token == "searchmoves") { while ((token = stack.Pop()) != null) { searchMoves.Add(Utils.move_from_uci(pos, token)); } } } Threads.start_searching(pos, limits, searchMoves); }
/// Thread::idle_loop() is where the thread is parked when it has no work to do. /// The parameter 'master_sp', if non-NULL, is a pointer to an active SplitPoint /// object for which the thread is the master. internal void idle_loop(SplitPoint sp_master, ManualResetEvent initEvent) { if (initEvent != null) { // Signal done initEvent.Set(); } bool use_sleeping_threads = Threads.useSleepingThreads; // If this thread is the master of a split point and all slaves have // finished their work at this split point, return from the idle loop. while ((sp_master == null) || (sp_master.slavesMask != 0)) { // If we are not searching, wait for a condition to be signaled // instead of wasting CPU time polling for work. while (do_sleep || do_exit || (!is_searching && use_sleeping_threads)) { if (do_exit) { Debug.Assert(sp_master == null); return; } // Grab the lock to avoid races with Thread::wake_up() ThreadHelper.lock_grab(sleepLock); // If we are master and all slaves have finished don't go to sleep if ((sp_master != null) && (sp_master.slavesMask == 0)) { ThreadHelper.lock_release(sleepLock); break; } // Do sleep after retesting sleep conditions under lock protection, in // particular we need to avoid a deadlock in case a master thread has, // in the meanwhile, allocated us and sent the wake_up() call before we // had the chance to grab the lock. if (do_sleep || !is_searching) { ThreadHelper.cond_wait(sleepCond, sleepLock); } ThreadHelper.lock_release(sleepLock); } // If this thread has been assigned work, launch a search if (is_searching) { Debug.Assert(!do_sleep && !do_exit); ThreadHelper.lock_grab(Threads.splitLock); Debug.Assert(is_searching); SplitPoint sp = curSplitPoint; ThreadHelper.lock_release(Threads.splitLock); LoopStack ls = LoopStackBroker.GetObject(); Stack[] ss = ls.ss; int ssPos = 0; Position pos = PositionBroker.GetObject(); pos.copy(sp.pos, this); Array.Copy(sp.ss, sp.ssPos - 1, ss, ssPos, 4); ss[ssPos + 1].sp = sp; ThreadHelper.lock_grab(sp.Lock); if (sp.nodeType == NodeTypeC.Root) { Search.search(NodeTypeC.SplitPointRoot, pos, ss, ssPos + 1, sp.alpha, sp.beta, sp.depth); } else if (sp.nodeType == NodeTypeC.PV) { Search.search(NodeTypeC.SplitPointPV, pos, ss, ssPos + 1, sp.alpha, sp.beta, sp.depth); } else if (sp.nodeType == NodeTypeC.NonPV) { Search.search(NodeTypeC.SplitPointNonPV, pos, ss, ssPos + 1, sp.alpha, sp.beta, sp.depth); } else { Debug.Assert(false); } Debug.Assert(is_searching); is_searching = false; #if ACTIVE_REPARENT sp.allSlavesRunning = false; #endif sp.slavesMask &= ~(1UL << idx); sp.nodes += pos.nodes; // Wake up master thread so to allow it to return from the idle loop in // case we are the last slave of the split point. if (use_sleeping_threads && this != sp.master && !sp.master.is_searching) { sp.master.wake_up(); } // After releasing the lock we cannot access anymore any SplitPoint // related data in a safe way becuase it could have been released under // our feet by the sp master. Also accessing other Thread objects is // unsafe because if we are exiting there is a chance are already freed. ThreadHelper.lock_release(sp.Lock); #if ACTIVE_REPARENT // Try to reparent to the first split point, with still all slaves // running, where we are available as a possible slave. for (int i = 0; i < Threads.size(); i++) { Thread th = Threads.threads[i]; int spCnt = th.splitPointsCnt; SplitPoint latest = th.splitPoints[spCnt != 0 ? spCnt - 1 : 0]; if (this.is_available_to(th) && spCnt > 0 && !th.cutoff_occurred() && latest.allSlavesRunning && Utils.more_than_one(latest.slavesMask)) { ThreadHelper.lock_grab(latest.Lock); ThreadHelper.lock_grab(Threads.splitLock); // Retest all under lock protection, we are in the middle // of a race storm here ! if (this.is_available_to(th) && spCnt == th.splitPointsCnt && !th.cutoff_occurred() && latest.allSlavesRunning && Utils.more_than_one(latest.slavesMask)) { latest.slavesMask |= 1UL << idx; curSplitPoint = latest; is_searching = true; } ThreadHelper.lock_release(Threads.splitLock); ThreadHelper.lock_release(latest.Lock); break; // Exit anyhow, only one try (enough in 99% of cases) } } #endif pos.startState = null; pos.st = null; PositionBroker.Free(); LoopStackBroker.Free(ls); } } }
/// 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 12), 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 secs or number of nodes. internal static void benchmark(Position current, Stack <string> stack) { List <string> fens = new List <string>(); LimitsType limits = new LimitsType(); Int64 nodes = 0; Int64 nodesAll = 0; long e = 0; long eAll = 0; // Assign default values to missing arguments string ttSize = (stack.Count > 0) ? (stack.Pop()) : "128"; string threads = (stack.Count > 0) ? (stack.Pop()) : "1"; string limit = (stack.Count > 0) ? (stack.Pop()) : "12"; string fenFile = (stack.Count > 0) ? (stack.Pop()) : "default"; string limitType = (stack.Count > 0) ? (stack.Pop()) : "depth"; OptionMap.Instance["Hash"].v = ttSize; OptionMap.Instance["Threads"].v = threads; TT.clear(); if (limitType == "time") { limits.movetime = 1000 * int.Parse(limit); // maxTime is in ms } else if (limitType == "nodes") { limits.nodes = int.Parse(limit); } else { limits.depth = int.Parse(limit); } if (fenFile == "default") { fens.AddRange(Defaults); } else if (fenFile == "current") { fens.Add(current.to_fen()); } else { #if PORTABLE throw new Exception("File cannot be read."); #else System.IO.StreamReader sr = new System.IO.StreamReader(fenFile, true); string fensFromFile = sr.ReadToEnd(); sr.Close(); sr.Dispose(); string[] split = fensFromFile.Replace("\r", "").Split('\n'); foreach (string fen in split) { if (fen.Trim().Length > 0) { fens.Add(fen.Trim()); } } #endif } Stopwatch time = new Stopwatch(); long[] res = new long[fens.Count]; for (int i = 0; i < fens.Count; i++) { time.Reset(); time.Start(); Position pos = new Position(fens[i], bool.Parse(OptionMap.Instance["UCI_Chess960"].v), Threads.main_thread()); Plug.Write("\nPosition: "); Plug.Write((i + 1).ToString()); Plug.Write("/"); Plug.Write(fens.Count.ToString()); Plug.Write(Constants.endl); if (limitType == "perft") { Int64 cnt = Search.perft(pos, limits.depth * DepthC.ONE_PLY); Plug.Write("\nPerft "); Plug.Write(limits.depth.ToString()); Plug.Write(" leaf nodes: "); Plug.Write(cnt.ToString()); Plug.Write(Constants.endl); nodes = cnt; } else { Threads.start_searching(pos, limits, new List <Move>()); Threads.wait_for_search_finished(); nodes = Search.RootPosition.nodes; res[i] = nodes; } e = time.ElapsedMilliseconds; nodesAll += nodes; eAll += e; Plug.Write("\n==========================="); Plug.Write("\nTotal time (ms) : "); Plug.Write(e.ToString()); Plug.Write("\nNodes searched : "); Plug.Write(nodes.ToString()); Plug.Write("\nNodes/second : "); Plug.Write(((int)(nodes / (e / 1000.0))).ToString()); Plug.Write(Constants.endl); } Plug.Write("\n==========================="); Plug.Write("\nTotal time (ms) : "); Plug.Write(eAll.ToString()); Plug.Write("\nNodes searched : "); Plug.Write(nodesAll.ToString()); Plug.Write("\nNodes/second : "); Plug.Write(((int)(nodesAll / (eAll / 1000.0))).ToString()); Plug.Write(Constants.endl); //for (int i = 0; i < res.Length; i++) //{ // Plug.Write(string.Format("{0}: {1}", i, res[i])); // Plug.Write(Constants.endl); //} }