我过去使用不同的语言编写了一些玩具国际象棋引擎,它们都提供了自己的简单的基于文本的 UI。现在我想编写一个可以与 ChessX 或 Cute Chess 等国际象棋 GUI 一起使用的引擎。我已经阅读并理解了(至少我是这么认为的)通用国际象棋接口(UCI)协议。
为了获得第一个工作版本,我使用 Python(macOS 上的 3.9)作为我的引擎,这里是 通过 STDIN 寻找输入的部分根据要求提供了完整(非)工作示例:
import sys
import os
import time
import re
import select
import fileinput
if __name__ == '__main__':
while True:
if select.select([sys.stdin, ], [], [], 0.0)[0]:
for line in fileinput.input():
tokens = [ x.strip().lower() for x in re.split("\s+", line.strip()) ]
if tokens[0] == "uci":
sys.stdout.write("id name mychess\n")
sys.stdout.write("id author myname\n")
sys.stdout.write("uciok\n")
elif tokens[0] == "isready":
sys.stdout.write("readyok\n")
time.sleep(2)
当我将我的引擎放入其中一个 GUI(或一个简单的 python-chess 应用程序,见下文)时,我的引擎会获取初始的“uci”命令并相应地回答,因为我可以使用一些测试和日志记录代码进行验证,这些代码未在上面的最小示例。
但是,什么也没有发生……直到 GUI 告诉我它们超时了。显然,他们没有从我的引擎那里得到任何答案。使用 python-chess 库,我可以验证引擎的响应没有到达 GUI 进程。
这是我的 python-chess 应用程序中用于连接到我的引擎的代码:
engine = chess.engine.SimpleEngine.popen_uci(r"./engine.py")
如果我将
stockfish
引擎的路径放在那里,我可以看到该引擎的响应以及 python-chess 应用程序如何用“ucinewgame”和许多其他通信进行应答。
那么,为什么 GUI 客户端无法读取我的引擎的响应?
更新: 我怀疑这与我的示例中的
select
代码有关。它的作用是模拟对 STDIN 的非阻塞访问。在一种更简单的方法中,我只是使用阻塞调用来输入并且它有效(这是它的要点):
while True:
line = input()
if line.strip().lower() == "uci":
print("id name mychess")
print("uciok")
问题似乎在于使用
select.select()
和/或 fileinput.input()
。如果我省略这些花哨的技术并简单地从 STDIN 读取,则一切正常并且 cutechess
接受我的引擎。这是 UCI 国际象棋引擎获得认可的基本代码:
import sys
import os
import re
if __name__ == '__main__':
while True:
line = input()
tokens = [ x.strip().lower() for x in re.split("\s+", line.strip()) ]
if tokens[0] == "uci":
sys.stdout.write("id name mychess\n")
sys.stdout.write("id author myname\n")
sys.stdout.write("uciok\n")
elif tokens[0] == "isready":
print("readyok")
当然,对
input()
的阻塞调用现在不允许在我的引擎中进行并发计算,同时立即接受来自 GUI 的输入,但这可能是另一个问题。
我的声誉级别不允许发表评论,因此我被迫输入答案:在其他类似情况下,我经常发现对控制台的 StdOut 流的“刷新”命令允许重定向的控制台输出显示在接收者 GUI 应用程序中。