/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate /// function. Also intercepts EOF from stdin to ensure gracefully exiting if the /// GUI dies unexpectedly. When called with some command line arguments, e.g. to /// run 'bench', once the command is executed the function returns immediately. /// In addition to the UCI ones, also some additional debug commands are supported. internal static void loop(string args) { var pos = new Position(StartFEN, false, ThreadPool.main()); // The root position string token = string.Empty; do { try { string cmd; if (args.Length > 0) { cmd = args; } else if (null == (cmd = Console.ReadLine())) // Block here waiting for input { cmd = "quit"; } var stack = Position.CreateStack(cmd); if (stack.Count == 0) { continue; } token = stack.Pop(); // The 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. if (token == "quit" || token == "stop" || (token == "ponderhit") && Search.Signals.stopOnPonderhit) { Search.Signals.stop = true; ThreadPool.main().notify_one(); // Could be sleeping } else if (token == "ponderhit") { Search.Limits.ponder = false; // Switch to normal search } else if (token == "uci") { Output.Write("id name "); Output.Write(Utils.engine_info(true)); Output.Write("\n"); Output.Write(OptionMap.Instance.ToString()); Output.WriteLine("\nuciok"); } else if (token == "ucinewgame") { Search.reset(); TimeManagement.availableNodes = 0; } else if (token == "isready") { Output.WriteLine("readyok"); } else if (token == "go") { go(pos, stack); } else if (token == "position") { position(pos, stack); } else if (token == "setoption") { setoption(stack); } // Additional custom non-UCI commands, useful for debugging else if (token == "flip") { pos.flip(); } else if (token == "eval") { Output.WriteLine(Eval.trace(pos)); } else if (token == "bench") { Benchmark.benchmark(pos, stack); } else if (token == "d") { Output.Write(pos.displayString()); } else if (token == "perft") { token = stack.Pop(); // Read depth var ss = Position.CreateStack( $"{OptionMap.Instance["Hash"].v} {OptionMap.Instance["Threads"].v} {token} current perft"); Benchmark.benchmark(pos, ss); } else { Output.Write("Unknown command: "); Output.WriteLine(cmd); } } catch (Exception ex) { Console.Error.WriteLine($"An error occurred: {ex}"); } } while (token != "quit" && args.Length == 0); // Passed args have one-shot behaviour ThreadPool.main().join(); // Cannot quit whilst the search is running }