我正在开发一个使用国际象棋引擎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 protocol与引擎进行通信的类(就像oracle与众神进行通信:)。此问题的相关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)
一直存在,因此永远不会返回输出。我对流程还很陌生,因此我可以肯定代码中存在一些基本错误。该如何解决?
谢谢!
答案 0 :(得分:1)
工作鱼似乎在工作结束时返回了“ 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);
}
}
}
答案 1 :(得分:1)
好吧,这对我来说是个很好的谜语。 让我们考虑另一种方法,并尝试与国际象棋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写入输出的每个字符串,直到看到所需的字符串为止。