3.2 扩展数组表示
虽然10×9的矩阵可以完全表示棋盘和棋子,但为了运算方便,我们将矩阵扩展成16× 16的形式。这是在算法设计中,常常采用的技巧:“空间换取时间”。多出了166个整数空间对现在的计算机简直就不值一提。
3.2.1 棋盘表示
为什么采用16×16矩阵呢?当然是为了加快运算速度。
16 = 24,可以使二维坐标与一维坐标方便的转换。如:二维下标[i][j],对应一维下标[k]。
k = i * 16 +j i = k /16(整除) j = k %16
由于16 = 24,所以上述乘除运算可以通过位运算来实现,时间可以大幅缩短。
k = i <<4 +j
16×16矩阵还有的好处:
可以快速判断兵是否过河;
方便生成棋子位置关于河界的对称位置,这在计算兵的走法时很有用。
16×16的棋盘矩阵远比真正棋盘大,那棋盘放在矩阵的什么位置呢?为了运算的方便,将真正棋盘放在虚拟棋盘的中间,如图3-2所示。
为什么有效棋盘要放在虚拟棋盘的中间呢?
这样保证了有效棋盘的边界外至少都有3个格子,右边一列有4个多余格子。这样做的目的是加快走法生成,判断棋子是否走出边界。如,当计算象的走法时,象沿着“田”字行走,当走到虚拟边框上时就知道这是一个不合法的走法,不用太多复杂的if判断语句。
如chess[10][9],判断一个位置[i][j]是否合法,常用如下判断:
if ( i >= 0 && i <= 9 && j >=0 && j<=8)
图3-2 16×16矩阵棋盘示意图
要想快速判断棋子位置的合法性,还要用一个辅助数组LegalPosition [256]。
const char LegalPosition[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
要判断位置k是否合法,只需要看LegalPosition[k]的值就行了。LegalPosition[k]=1,则在棋盘上,LegalPosition[k]=0,则在棋盘外了,非法位置。
3.2.2 棋子表示
棋子的表示也要做一些调整,红黑双方各为16个棋子:
红棋子:帅一个,车、马、炮、相、仕各两个,兵五个。
黑棋子:将一个,车、马、炮、象、士各两个,卒五个。
我们为每一个棋子都用一个整数来表示,如表3-1所示。
表3-1 每一个棋子的整数表示
棋子这样表示有什么特别的呢?
红黑双方棋子的表示,可以达到快速棋子判断的目的,如表3-2所示。
表3-2 棋子的十进制与二进制表示
通过表3-1,可以看出红黑棋子的特点:
红方棋子 & 16 =1按位与运算
黑方棋子 & 32 =1按位与运算
假设当前走棋方side,随便为0或1,判断位置上的棋子是本方棋子:
Sidetag = side *16 +16;
棋子 & Sidetag ==1为本方棋子
由于0用来表示无棋子,所以不能用0~15来表示红方或者黑方的棋子。当然,也可以用64~79表示某方棋子。
16×16矩阵棋盘初始局面,如图3-3所示。
图3-3 16×16矩阵棋盘初始局面示意图
3.2.3 二维数组与一维数组
用二维数组表示16×16矩阵,是最自然不过的事情。但实际上很多博弈程序更喜欢用一维数组来表示board[256]。二维数组表示一个位置要用两个变量,一个走法包含起点和终点则就需要四个变量。当一个位置变换成另一个位置时,也常常需要分别对x、y变量做加减运算,如马二进三,则进行两个操作:x-2,y-1。这样会影响走法生成、运算的效率。
本书示例程序采用一维数组来表示16×16矩阵。
数组的类型,虽然棋子都是整数,但我们可以用字符类型表示。常用的是unsigned char类型。一是可以节省存储空间,二是可以加快运算,一字节字符运算要快于四字节的整数运算。