1

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

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

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是用于与引擎通信的类(就像预言机用于与众神通信:)),使用UCI 协议。此问题的相关 UCI 命令是:

uci: 进入 uci 模式。
position fen //followed by a FEN:设置要分析的位置。
go depth 1分析一层(“移动”)深度的位置。

这是(同样,非常简化的)StockfishOracle类:

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)将永远持续下去,因此永远不会返回输出。我对流程很陌生,所以我很确定我的代码中存在一些基本错误。如何解决这个问题?

谢谢!

4

2 回答 2

1

似乎stockfish 在他的工作结束时返回“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);
        }
    }
}
于 2018-07-04T19:26:12.590 回答
1

嗯,这对我来说似乎是一个很好的谜语。让我们考虑另一种方法并尝试与国际象棋预言机异步通信:

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 写入输出的每个字符串,直到您看到所需的字符串。

于 2018-07-05T19:23:44.353 回答