Цикл не достигает конца потока - PullRequest
0 голосов
/ 04 июля 2018

Я разрабатываю программу для анализа шахматных задач - особенно проблем с эндшпилем - с использованием .exe версии шахматного движка с открытым исходным кодом Stockfish 9 .

Вот (очень упрощенный) 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();
    }
}

При вызове метода AnalyzeByFEN с помощью FEN, вывод на консоль не выводится. Тщательное расследование привело к наблюдению, что цикл while (!process.StandardOutput.EndOfStream) работает вечно, поэтому вывод никогда не возвращается. Я довольно новичок в процессах, поэтому я уверен, что в моем коде есть некоторые основные ошибки. Как это исправить?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Ну, это оказалось хорошей загадкой для меня. Давайте рассмотрим другой подход и попробуем асинхронно общаться с шахматным оракулом:

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);
    }
}

Насколько я понял, вы ищете прогноз лучшего хода. Теперь вы можете подождать, пока оно не появится в выводе. Также используя тот же обработчик событий, вы можете анализировать каждую строку, которую оракул записывает в вывод, пока не увидите нужную.

0 голосов
/ 04 июля 2018

Похоже, что в конце своей работы стокфиш возвращает "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);
        }
    }
}
...