国际象棋通用引擎协议
一、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 <x> | author <x>}
这是uci指令的反馈信息，显示引擎的版本号和作者。
3. option name <id> type <t> [default <x>] [min <x>] [max <x>] [var <x> [var <x> [...]]]
也是uci指令的反馈信息，表示引擎所支持的选项，<id>指选项的名称(后面会介绍)，<t>指选项的类型，可以是以下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 <id> [value <x>]
设置引擎参数，这些参数必须是option反馈信息所列出的。
6. isready
检测引擎是否处于“就绪”状态，如果引擎发送回readyok信息，则说明引擎已经就绪，可以界面可以向引擎发出其他指令。
7. readyok
这是isready的反馈信息，仅仅表示引擎可以接收指令了。即使引擎在思考，接收到isready指令后也会返回readyok。
8. position {fen <fenstring> | startpos } [moves <move1> .... <moven>]
设置“内置棋盘”的局面，用fen来指定FEN格式串，或用startpos来指定起始局面，它等价于
  fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1。
一般来说，界面发送给引擎的<fenstring>是最近一次吃子或进兵后的局面(称为“不可逆局面”)，该局面到当前局面的一系列着法则跟在moves后，目的是让引擎掌握重复检测的策略。FEN格式串的写法参阅《国际象棋译文苑》文摘——关于PGN和FEN记谱规范(下)一文。
9. go ...
让引擎根据内置棋盘的设置和设定的搜索方式来思考，有以下搜索方式可供选择(可以多选，直接跟在go后面)：
(1) searchmoves <move1> .... <moven>，只让引擎在这几步中选择一步；
(2) wtime <x>，白方剩余时间(单位是毫秒)；
btime <x>，黑方剩余时间；
winc <x>，白方每步增加的时间(适用于Fischer制)；
binc <x>，黑方每步增加的时间；
movestogo <x>，还有多少回合进入下一时段(适用于时段制)；
这些选项用来设定时钟，它决定了引擎的思考时间；
(3) ponder，让引擎进行后台思考(即对手在用时，引擎的时钟不起作用)；
(4) depth <x>，指定搜索深度；
(5) nodes <x>，指定搜索的节点数(即分析的局面数，一般它和时间成正比)；
(6) mate <x>，在指定步数内只搜索杀棋；
(7) movetime <x>，只花规定的时间搜索；
(8) infinite，无限制搜索，直到杀棋。
10. info ...
显示引擎思考信息，信息有以下内容(可以是很多信息，都跟在info后面)：
(1) depth <x>，当前搜索到的深度；
(2) seldepth <x>，选择性搜索(不完全搜索)达到的深度，通常会跟在depth后面；
(3) pv <move1> ... <moven>，已经搜索到的最佳路线；
(4) multipv <num>，这只会出现在设定了MultiPV选项以后，紧跟在pv后面，说明它是最佳的路线中的第几条(排名第几)；
(5) time <x>，已经搜索的时间，它往往跟在pv后面；
(6) score {pv <x> | mate <y>} [lowerbound | upperbound]，引擎对当前局面的评价，pv <x>指当前局面的评分，单位是“百分兵值”，mate <y>指在多少步之内会形成杀棋，lowerbound指该评价是最低估计(为白方估计)，upperbound指该评价是最高估计(为黑方估计)；
(7) currmovenumber <x>，当前搜索着法的序号；
(8) currmove <move>，当前搜索的着法，它往往跟在currmovenumber <x>的后面；
(9) nodes <x>，已经搜索的节点数(即分析的局面数，它会隔一定时间显示出来；
(10) hashfull <x>，Hash表的占用率(单位是千分之一)，它也会隔一定时间显示出来；
(11) nps <x>，引擎速度，用每秒搜索的节点数，它也会隔一定时间显示出来；
(12) tbhits <x>，在残局库中找到局面的数目；
(13) cpuload <x>，处理器的占用率(单位是千分之一)；
(14) string <str>，其他信息，通常是调试信息。
11. stop
中断引擎的思考；
12. ponderhit
在后台思考(go ponder)还没有输出结果(bestmove <x>)时，告诉引擎后台思考命中(正在后台思考的着法正好是对手的着法)，随后引擎就自动转入正常思考(时钟开始有效)。如果后台思考没有命中，就必须用stop中止思考，重新设置局面让引擎思考。
13. bestmove <move1> [ponder <move2>]
引擎思考结束后(不管是否被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 <fenstring>的形式给出，因为当前局面是不可逆局面)；
  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。