循环未到达流的末尾

Loop doesn't reach end of stream

我正在开发一个程序来分析国际象棋问题——尤其是残局问题——使用开源国际象棋引擎的 .exe 版本 Stockfish 9

这是(非常简化的)EndgameAnalyzer class:

class EndgameAnalyzer
{
    private StockfishOracle oracle = new StockfishOracle();

    public void AnalyzeByFEN(string fen)
    {
        var evaluation = oracle.GetEvaluation(fen);
        Console.WriteLine($"{fen}\t{evaluation}");
    }
}

AnalyzeByFEN 方法接收一个 FEN(表示国际象棋位置的字符串)并记下该位置的引擎评估。

StockfishOracle是用来和引擎沟通的class(就像神谕是用来和神沟通的:)),使用UCI protocol。本题相关UCI命令为:

uci: Enter uci mode.
position fen //followed by a FEN: Set a position to analyze.
go depth 1: Analyze the position one ply ("move") deep.

这里是(再次,非常简化)StockfishOracle class:

class StockfishOracle
{
    private Process process = new Process();

    public StockfishOracle()
    {
        process.StartInfo = new ProcessStartInfo()
        {
            FileName = @"C:\stockfish_9_x64.exe",
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            RedirectStandardOutput = true
        };

        process.Start();
        SendUCICommand("uci");
    }
    public string GetEvaluation(string fen)
    {
        SendUCICommand($"position fen {fen}");
        SendUCICommand("go depth 1");

        string result = string.Empty;
        while (!process.StandardOutput.EndOfStream)
        {
            result = process.StandardOutput.ReadLine();
        }
        return result;

    }
    private void SendUCICommand(string command)
    {
        process.StandardInput.WriteLine(command);
        process.StandardInput.Flush();
    }
}

使用 FEN 调用 AnalyzeByFEN 方法时,控制台中未显示任何输出。仔细调查后发现循环 while (!process.StandardOutput.EndOfStream) 将永远持续下去,因此永远不会返回输出。我对流程很陌生,所以我很确定我的代码中存在一些基本错误。如何解决这个问题?

谢谢!

看起来stockfish returns "uciok" 在结束他的工作。 您可以尝试以下代码来确定它何时完成(参见 if (line == "uciok")):

class Program
{
    class StockfishOracle
    {
        private readonly Process process = new Process();

        public StockfishOracle()
        {
            process.StartInfo = new ProcessStartInfo
            {
                FileName = @"D:\stockfish-9-win\Windows\stockfish_9_x64.exe",
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true
            };

            process.Start();
            SendUciCommand("uci");
        }
        public IEnumerable<string> GetEvaluation(string fen)
        {
            SendUciCommand($"position fen {fen}");
            SendUciCommand("go depth 1");

            while (!process.StandardOutput.EndOfStream)
            {
                var line = process.StandardOutput.ReadLine();
                yield return line;

                if (line == "uciok")
                {
                    break;
                }
            }
        }

        private void SendUciCommand(string command)
        {
            process.StandardInput.WriteLine(command);
            process.StandardInput.Flush();
        }
    }

    static void Main(string[] args)
    {
        var s = new StockfishOracle();

        foreach (var @out in s.GetEvaluation(""))
        {
            Console.WriteLine(@out);
        }
    }
}

好吧,这对我来说是个不错的谜语。 让我们考虑另一种方法并尝试与国际象棋 oracle 异步通信:

class Program
{
    class StockfishOracle
    {
        private readonly Process process = new Process();

        public StockfishOracle()
        {
            process.StartInfo = new ProcessStartInfo
            {
                FileName = @"D:\stockfish-9-win\Windows\stockfish_9_x64.exe",
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true
            };

            process.OutputDataReceived += (sender, args) => this.DataReceived.Invoke(sender, args);
        }

        public event DataReceivedEventHandler DataReceived = (sender, args) => {};

        public void Start()
        {
            process.Start();
            process.BeginOutputReadLine();
        }

        public void Wait(int millisecond)
        {
            this.process.WaitForExit(millisecond);
        }

        public void SendUciCommand(string command)
        {
            process.StandardInput.WriteLine(command);
            process.StandardInput.Flush();
        }

    }

    static void Main()
    {
        var oracle = new StockfishOracle();
        // this will contain all the output of the oracle
        var output = new ObservableCollection<string>();
        // in this way we redirect output from oracle to stdout of the main process
        output.CollectionChanged += (sender, eventArgs) => Console.WriteLine(eventArgs.NewItems[0]);
        // in this way collect all the output from oracle
        oracle.DataReceived += (sender, eventArgs) => output.Add(eventArgs.Data);

        oracle.Start();

        oracle.SendUciCommand("position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
        oracle.SendUciCommand("position startpos moves e2e4");
        oracle.SendUciCommand("go depth 20");

        oracle.Wait(5000); // if output does not contain bestmove after given time, you can wait more

        var bestMove = output.Last();

        Console.WriteLine("Oracle says that the best move is: " + bestMove);
    }
}

据我了解,您正在寻找最佳着法的预测。现在您可以等到它出现在输出中。同样使用相同的事件处理程序,您可以分析 oracle 写入输出的每个字符串,直到您看到所需的字符串。