国际象棋通用引擎协议
黄晨 * 2004年9月初稿,2006年2月修订
( * 联系地址:复旦大学化学系表面化学实验室,eMail:
)
一、UCI协议的特点 UCI协议,全称是国际象棋通用引擎协议
(Universal Chess Interface,直译作通用象棋接口
)。它是开放的象棋引擎协议,所谓“开放的”引擎协议,指的是:
(1) 协议内容是公开的,并且可以免费使用;
(2) 你可以根据该协议自己编写象棋引擎,凡是支持该协议的界面,都可以使用你编写的引擎;
(3) 你可以根据该协议自己编写象棋界面,凡是支持该协议的引擎,都可以被你编写的界面调用。
UCI协议的前身是象棋引擎
SOS和
Shredder使用的引擎协议,在他们的作者
Rudolf Huber和
Stefan Meyer-Kahlen对该协议的改进下,
2000年
11月
28曰
UCI协议问世了。
UCI协议具有以下特点:
(1) 引擎程序是可执行文件,它同界面程序之间通过“标准输入”和“标准输出”
(即
C语言中的
stdin和
stdout)来通讯。
(2) 输入和输出是以“行方式”来完成的,界面发给引擎的每条指令都必须以“回车”
(即
C语言中的
'\n')结束,界面接收引擎的反馈也一样。 注:引擎不能跨平台使用,如果引擎从一个平台
(如
Windows)移植到另一平台
(如
Unix),需要重新编译源代码,或使用跨平台接口。引擎的平台通常由三类:
A. DOS平台,但是由于
DOS平台过于陈旧,现在很难找到这样的引擎;
B. Windows平台,需要在
Windows下用
Console方式编译引擎源代码,
WinBoard只能使用这类引擎;
C. Unix/Linux平台;需要在
Unix或
Linux下编译源代码
(也用
Console方式
),
XBoard能够使用
Linux的引擎。
(3) 引擎启动时,必须用“
uci”指令让引擎进入
UCI协议状态。当然,引擎也可以保留不使用
UCI协议的权利,只要它接受的第一条有效指令允许不是“
uci”。例如,很多
UCI引擎允许第一条有效指令是“
xboard”,这样引擎就转而进入
WinBoard协议状态。
(4) 无论引擎是否在思考,都必须随时接收指令,这样界面程序就可以随时中断引擎的工作或改变引擎的思考方式了。每条指令都以特定的关键字开头,关键字和参数之间必须用空格隔开,这样可以简化引擎识别指令的过程。
(5) 界面必须随时接收引擎的反馈信息,每条反馈信息也都以特定的关键字开头
(空格后面才是信息的实质内容
),这样可以让界面更方便地识别每一条反馈信息。
(6) 引擎在搜索一个局面前,先要让界面把局面的位置告诉引擎,作为“内置局面”;
(7) 引擎必须接收到
"go"指令后才开始思考
(搜索
);
(8) 如果对局是计时的,那么每次思考时都必须设定时钟,引擎仅仅根据时钟来决定思考策略,时钟的改变需要界面来完成;
(9) 当引擎完成一个局面的搜索,得到一步最佳着法后,并不改变“内置局面”,只是把这个着法反馈给界面,界面来完成这一步
(当然,界面也可以让引擎走别的着法
),再把走完这一步后的局面告诉引擎;
(10) 开局库通常由界面来指定给引擎,即通常引擎是不自带开局库的,当然引擎也可以自带开局库,此时界面可以让引擎根据引擎自己的开局库来完成开局的某步。 以上
10个特点中,前
5点和
WinBoard协议是类似的,而后
5点
WinBoard协议和
UCI协议则完全相反,因此
WinBoard协议和
UCI协议的指令也截然不同。
二、走棋格式 UCI协议用的走棋格式是“长代数格式”,即走动子的起始位置和到达位置,这里有几点需要注意:
(1) 不要标明“到达记号”、“吃子记号”、“将军记号”以及其他评注记号,例如第一步走
e4,则记作
e2e4,而不是
e2-e4;
(2) 不要标明所走的子,例如走
Nf6,则记作
g8f6,而不是
Ng8f6;
(3) 王车易位时只标明王的路线,例如走
O-O,则记作
e1g1;
(4) 兵升变时在最后注明只用一个字母注明升变的子,例如走
e8=Q,则记作
e7e8q。
三、输入和输出协议 输入协议就是界面向引擎发送指令的协议,协议内容通常由一系列指令集组成
(用红色表示
),输出协议则规定了引擎反馈给界面的信息,以及这些信息具体的含义
(用蓝色表示
)。
1. uci 这是引擎启动后,界面需要给引擎发送的第一条指令,通知引擎现在使用的是
UCI协议。
2. id {name | author } 这是
uci指令的反馈信息,显示引擎的版本号和作者。
3. option name type [default ] [min ] [max ] [var [var [...]]] 也是
uci指令的反馈信息,表示引擎所支持的选项,
指选项的名称
(后面会介绍
),
指选项的类型,可以是以下
5种:
(1) check,检查框,取值只能是
true或
false;
(2) spin,旋钮,取值是整数,可以用
min和
max来限定范围;
(3) combo,列表框,取值是由
var来指定;
(4) button,按钮,没有取值,仅仅用来触发某个事件;
(5) string,字符串,取值可以是任何字符串。 通常的
UCI引擎支持以下选项:
(1) Hash(spin),以
MB为单位规定
Hash表的大小;
(2) NalimovPath(string),指定
Nalimov残局库的路径,可以设置多个路径,用
';'隔开;
(3) NalimovCache(spin),以
MB为单位规定
Nalimov残局库的缓冲区;
(4) Ponder(check),指定引擎是否后台思考
(Ponder),设定该参数的目的仅仅是让引擎改变时间分配策略,后台思考仍然需要界面发出指令;
(5) OwnBook(check),指定引擎是否要使用引擎自带的开局库;
(6) MultiPV(spin),引擎给出多少步最佳着法,
Alpha-Beta搜索通常只给出一步,增加这个数值会降低引擎的运行效率,但扩大了界面对引擎提供着法的选择空间;
4. uciok 这是
uci指令的最后一条反馈信息,表示引擎已经进入用
uci协议通讯的状态。
5. setoption name [value ] 设置引擎参数,这些参数必须是option反馈信息所列出的。
6. isready 检测引擎是否处于“就绪”状态,如果引擎发送回
readyok信息,则说明引擎已经就绪,可以界面可以向引擎发出其他指令。
7. readyok 这是
isready的反馈信息,仅仅表示引擎可以接收指令了。即使引擎在思考,接收到
isready指令后也会返回
readyok。
8. position {fen | startpos } [moves .... ] 设置“内置棋盘”的局面,用
fen来指定
FEN格式串,或用
startpos来指定起始局面,它等价于
fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1。
一般来说,界面发送给引擎的
是最近一次吃子或进兵后的局面
(称为“不可逆局面”
),该局面到当前局面的一系列着法则跟在
moves后,目的是让引擎掌握重复检测的策略。
FEN格式串的写法参阅《
国际象棋译文苑》文摘——
关于PGN和FEN记谱规范(下)一文。
9. go ... 让引擎根据内置棋盘的设置和设定的搜索方式来思考,有以下搜索方式可供选择
(可以多选,直接跟在
go后面
):
(1) searchmoves .... ,只让引擎在这几步中选择一步;
(2) wtime ,白方剩余时间
(单位是毫秒
);
btime ,黑方剩余时间;
winc ,白方每步增加的时间
(适用于
Fischer制
);
binc ,黑方每步增加的时间;
movestogo ,还有多少回合进入下一时段
(适用于时段制
); 这些选项用来设定时钟,它决定了引擎的思考时间;
(3) ponder,让引擎进行后台思考
(即对手在用时,引擎的时钟不起作用
);
(4) depth ,指定搜索深度;
(5) nodes ,指定搜索的节点数
(即分析的局面数,一般它和时间成正比
);
(6) mate ,在指定步数内只搜索杀棋;
(7) movetime ,只花规定的时间搜索;
(8) infinite,无限制搜索,直到杀棋。
10. info ... 显示引擎思考信息,信息有以下内容
(可以是很多信息,都跟在
info后面
):
(1) depth ,当前搜索到的深度;
(2) seldepth ,选择性搜索
(不完全搜索
)达到的深度,通常会跟在
depth后面;
(3) pv ... ,已经搜索到的最佳路线;
(4) multipv ,这只会出现在设定了
MultiPV选项以后,紧跟在
pv后面,说明它是最佳的路线中的第几条
(排名第几
);
(5) time ,已经搜索的时间,它往往跟在
pv后面;
(6) score {pv | mate } [lowerbound | upperbound],引擎对当前局面的评价,
pv 指当前局面的评分,单位是“百分兵值”,
mate 指在多少步之内会形成杀棋,
lowerbound指该评价是最低估计
(为白方估计
),
upperbound指该评价是最高估计
(为黑方估计
);
(7) currmovenumber ,当前搜索着法的序号;
(8) currmove ,当前搜索的着法,它往往跟在
currmovenumber 的后面;
(9) nodes ,已经搜索的节点数
(即分析的局面数,它会隔一定时间显示出来;
(10) hashfull ,
Hash表的占用率
(单位是千分之一
),它也会隔一定时间显示出来;
(11) nps ,引擎速度,用每秒搜索的节点数,它也会隔一定时间显示出来;
(12) tbhits ,在残局库中找到局面的数目;
(13) cpuload ,处理器的占用率
(单位是千分之一
);
(14) string ,其他信息,通常是调试信息。
11. stop 中断引擎的思考;
12. ponderhit 在后台思考
(go ponder)还没有输出结果
(bestmove )时,告诉引擎后台思考命中
(正在后台思考的着法正好是对手的着法
),随后引擎就自动转入正常思考
(时钟开始有效
)。如果后台思考没有命中,就必须用
stop中止思考,重新设置局面让引擎思考。
13. bestmove [ponder ] 引擎思考结束后
(不管是否被
stop中断
)所显示的结果,
ponder指为对手思考的结果,通常引擎会提供此信息。
14. quit 让引擎退出运转,无论引擎思考与否。
四、举例 在下面的例子中,输入部分
(从界面到引擎
)用红色表示,输出部分
(从引擎到界面
)用蓝色表示。
uci,告诉引擎使用
UCI协议;
id name Shredder 5 id author Stefan MK,引擎显示版本信息。
option name Hash type spin default 1 min 1 max 128,
Hash表的上限是
128MB;
option name NalimovPath type string default c:\,说明引擎支持
Nalimov残局库,注意在
C语言中
'\'要打印成
'\\';
option name NalimovCache type spin default 1 min 1 max 32 option name Nullmove type check default true option name Style type combo default Normal var Solid var Normal var Risky,说明引擎有三种风格
(保守、均衡和冒进,缺省是均衡
);
uciok,所有可设置的参数已经显示出来,此时引擎可以接收指令;
setoption name Hash value 32,把
Hash表增加到
32MB;
setoption name NalimovCache value 1,把残局库缓冲区设置为
1MB(这句话没有必要,因为缺省值就是
1);
setoption name NalimovPath value d:\tb;c\tb,设置残局库的路径;
isready,等待引擎初始化;
readyok,引擎初始化完成,现在可以让引擎思考了;
position startpos moves e2e4 e7e5,给定开局
e4 e5(在这里,最好直接将当前局面用
position fen 的形式给出,因为当前局面是不可逆局面
);
go infinite,让引擎无限制思考,到时候用
stop中断;
info depth 1 seldepth 0 info score cp 13 depth 1 nodes 13 time 15 pv f1b5 info depth 2 seldepth 2 info nps 15937 info score cp 14 depth 2 nodes 255 time 15 pv f1c4 f8c5 info depth 2 seldepth 7 nodes 255 info depth 3 seldepth 7 info nps 26437 info score cp 20 depth 3 nodes 423 time 15 pv f1c4 g8f6 b1c3 info nps 41562 ...,以后还有很多信息,这里省略;
stop,用户不再等待,让引擎中止思考;
bestmove g1f3 ponder d8f6,引擎输出白方最佳着法
Nf3,黑方的应对是
Bf6(由于思考被用户中止,所以这步多少有些问题
),如果用户要继续进行思考,可再输入
go。 资料来源:
www.uciengines.de,
www.shredderchess.com,
www.chessbase.com。