C/C++中国象棋程序入门与提高
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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类型。一是可以节省存储空间,二是可以加快运算,一字节字符运算要快于四字节的整数运算。