象棋小巫师有一个很好的教程,一共分为6个步骤。
其中第1、2部分为基础,说明如何表示象棋、走棋
从第3部分开始说明如何实现电脑搜索,第3-6部分每一部分都包含了很多内容,深入调试会发现很多问题,每一部分大约都要2-4天时间来消化。
第3部分已经有很多内容,包括局面评价、alpha-beta搜索、杀棋分数、历史记录、迭代加深,已经可以走出不错的棋,但是如果跟踪下电脑思考路线就会发现很多问题,这些问题会在后面有解决
跟踪电脑搜索路径,如果玩家第一步走炮二平五,电脑思考路线如下:
时间(ms) 深度 评价 路线
0 1 81 炮2进7
0 2 -7 马2进3 炮五进四
0 3 4 马2进3 马八进七 车1平2
0 4 -7 马2进3 马八进七 马8进7 炮五进四
47 5 8 车9进1 马八进九 车9平6 仕四进五 马8进7
453 6 -14 马2进3 马二进三 炮8平5 车一平二 马8进7 炮五进四
2141 7 14 车9进1 马八进七 车9平6 炮五进四 马8进7 炮五平一 车6进8
28843 8 -17 炮8平5 马二进三 马8进7 车一平二 车9进1 马八进七 马2进3 炮五进四
120875 9 15 车9进1 马八进七 车9平6 炮五进四 马8进7 炮五平一 炮8平9 炮一退一 车6进8
1700422 10 -19 马8进9 马二进三 炮8平7 车一平二 车9进1 马八进七 车9平6 炮五进四 马2进3 车九平八
问题1. 水平线问题,这是最明显的问题
问题2. 每次走棋固定,可以通过加入随机来解决
问题3. 搜索路径奇数跟偶数有很大区别,即搜索的不稳定性
问题4. 搜索速度太慢
问题5. 没有任何智慧,完全靠搜索,完全无视对方放空头炮
对上面电脑思考做一个分析:
如果深度是1,则电脑选择自己最好的选择,就是炮打马
如果深度是2,则电脑选择以后要考虑人会选择最好的走法
炮2进7 车九平八 则电脑得分 -25
车1进2 炮八进七 则电脑得分 -87
马2进1 炮五进四 -8
马2进3 炮五进四 -7
如果深度是3,则不再那么直观,但是对于黑方每一种走法,根据搜索算法都会得到唯一的系列搜索结果
黑方从其中选择最好一个即可
有2个问题需要思考:
第一个比较简单,偶数搜索步数以后红方最后一步都是炮五进四,这个容易理解,通过解决水平线问题可以解决这个问题
第二个问题比较有意思,5、7、9的深度搜索的第一步都是车9进1,原因是类似的,考虑5步搜索,主要考虑下面两个结果
4 马2进3 马二进三 马8进7 马八进七 车1平2
8 车9进1 马八进九 车9平4 仕六进五 马8进7
车9进1 马八进七 炮8进5 马2进3 炮五进四
显然走车9进1还是因为水平线效应
第4部分解决了水平线问题、重复局面判断
同样玩家第一步走炮二平五,电脑思考结果如下
0 1 1 马2进3
0 2 -2 马2进3 马八进七
0 3 4 马8进7 马八进七 车9平8
16 4 -2 马8进7 马二进三 车9平8 车一平二
16 5 1 马8进7 马二进三 马2进3 车一平二 车9平8
187 6 -2 马2进3 马八进七 马8进7 马二进三 车9平8 车一平二
1485 7 -2 马8进7 马二进三 车9平8 车一平二 马2进3 马八进七 车1进1
5812 8 -3 马2进3 马二进三 马8进7 车一平二 车9平8 马八进七 车1进1 车二进六
19766 9 2 马8进7 马二进三 车9平8 车一平二 马2进3 马八进七 车1进1 车二进六 车1平4
295375 10 -2 马2进3 马二进三 马8进7 车一平二 车9平8 马八进七 车1进1 车九进一 车1平4 车九平四
速度有了一定的提升,关键是水平线问题解决以后,走法相对比较合理了,当然现在没法处理走完最后一步棋以后捉双的问题
棋盘局面重复判断采用Zobrist键值,对每一个棋子的每一个位置赋予一个值,对每一方走棋也赋予一个值,没走一步或交换对手,都做一个xor操作
并记录在历史移动记录中。
水平线问题则是在搜索到叶子节点的时候继续搜索,如果被将军则搜索所有走法,否则搜索所有下一步可能吃子的走法
空步裁剪是加快搜索速度的重要方法,在不走棋的情况下都产生beta截断,那么则不再进行完全搜索
空步裁剪的思想是如果不走棋都能产生beta截断,那么走一步肯定会更好,更会产生beta截断,但是在很多残棋中是会利用等着的,所以残棋不应该采用
这一部分通过增加两种启发式搜索方式加快搜索速度,差不多是第4部分速度的2-3倍,置换表与杀手走法,这两个名字感觉都不是很好
第一个是置换表,置换表记录了一个局面历史搜索结果,包括走法(上次得到这个局面以后最佳走法),估价值(采用最佳走法估价值),估价值类型(准确值、alpha值、beta值),搜索深度,以及校验值(用来确定是这个局面,记录所有可能的局面需要14^90,这不大可能,因此hash值需要校验,虽然校验以后仍然可能有冲突,但概率大大降低了)。如果在某次搜索中找到了相应的置换表,并且表中深度大于等于当前要搜索深度,则可以直接用这个项
第二个叫杀手走法,这个名字可以别管,我觉得更合适的叫法应该是最深最好走法,因为这个表示记录了每次搜索完以后距离搜索根节点每一个距离的2个最好的走法(象棋小巫师教程中是2个,可以是1个或3个或其它个),这个方法可行的原因是采用保存的这些走法容易产生alpha或beta阶段,需要注意的是要判断这些走法是否能走,因为同一搜索深度保存的这些走法不一定能走
开局库:让电脑开局选择官招中的一个
主要变例搜索(PVS):这种算法假设在一次搜索中,如果找到一个主要变例:结果在alpha和beta之间的评价走法,那么以后搜索中产生alpha截断的可能性很大,那么采用一个较小的窗口alpha,alpha+1取代alpha,beta,可以更快产生截断。
根节点处理:对根节点评价做一定调整,可以让电脑不再每次走同样招法