使用位域和掩码编写国际象棋游戏的教程

191 阅读5分钟

追踪棋盘上的棋子的一种方法是定义一个结构,定义棋盘上每个可能的棋子,以及它的颜色,所以每个方格都包含该结构的一个元素。例如,你可能有一个这样的结构,它看起来像这样。

struct chess_pc {
   int piece;
   int is_black;
}

有了这种编程结构,你的程序就会知道每个格子里是什么棋子以及它的颜色。你可以快速识别该棋子是卒、车、马、象、皇后还是国王--以及该棋子是黑色还是白色。但有一种更直接的方法来跟踪相同的信息,同时使用更少的数据和内存。与其为棋盘上的每个方块存储两个int 值的结构,我们可以存储一个int 值,并使用二进制的位域掩码来识别每个方块中的棋子和颜色。

位和二进制

当使用比特字段来表示数据时,有助于像计算机一样思考。让我们从列出可能的棋子开始,给每个棋子分配一个数字。我将通过以二进制形式表示数字,即计算机跟踪它的方式来帮助我们进行下一步。请记住,二进制数字是由比特组成的,这些比特要么是零,要么是一。

  • **00000000**:空 (0)
  • **00000001**:卒 (1)
  • **00000010**:车 (2)
  • **00000011**:马 (3)
  • **00000100**:象 (4)
  • **00000101**:王后 (5)
  • **00000110**:王 (6)

要列出棋盘上的所有棋子,我们只需要代表(从右到左)1、2、4这三个比特的数值。例如,数字6是二进制110 。6的二进制表示中的所有其他位都是零。

通过一点小聪明,我们可以使用其中一个额外的总是零的比特来追踪一个棋子是黑还是白。我们可以用数字8(二进制00001000 )来表示一个棋子是否是黑色的。如果这个位是1,它就是黑棋;如果是0,它就是白棋。这就是所谓的位域,我们可以在以后用二进制掩码把它拉出来。

用位域存储数据

要写一个使用位域和掩码的国际象棋程序,我们可以从这些定义开始。

/* game pieces */
#define EMPTY 0
#define PAWN 1
#define ROOK 2
#define KNIGHT 3
#define BISHOP 4
#define QUEEN 5
#define KING 6
/* piece color (bit-field) */
#define BLACK 8
#define WHITE 0
/* piece only (mask) */
#define PIECE 7

当你给一个方块赋值时,比如初始化棋盘时,你可以给一个单一的int ,以跟踪棋子和它的颜色。例如,要在数组的0,0位置存储一个黑色的车,你可以使用这个代码。

  int board[8][8];
..
  board[0][0] = BLACK | ROOK;

| 是一个二进制的OR,这意味着计算机将结合两个数字的位。对于每个比特位置,如果任何一个数字的该比特是1,那么该比特位置的结果也是1。二进制OR的值BLACK (8,或二进制00001000 )和值ROOK (2,或二进制00000010 )是二进制00001010 ,或10。

    00001000 = 8
 OR 00000010 = 2
    ________
    00001010 = 10

同样,要在数组的6,0位置存储一个白棋,你可以用这个。

  board[6][0] = WHITE | PAWN;

这存储的值是1,因为WHITE (0)和PAWN (1)的二进制OR只是1。

    00000000 = 0
 OR 00000001 = 1
    ________
    00000001 = 1

用掩码获取数据

在国际象棋游戏中,程序需要知道哪个棋子在一个方格中以及它的颜色。我们可以用二进制掩码将棋子分开。

例如,在棋局中,程序可能需要知道棋盘上某一特定方格的内容,例如位于board[5][3] 的阵列元素。那里有什么棋子,是黑棋还是白棋?为了识别这颗棋子,用二进制和运算符将元素的值与PIECE 掩码结合起来。

  int board[8][8];
  int piece;
..
  piece = board[5][3] & PIECE;

二进制AND运算符(&)将两个二进制数值结合起来,这样对于任何位的位置,如果两个数字中的那个位是1,那么结果也是1。例如,如果board[5][3] 的值是11(二进制00001011 ),那么11和掩码PIECE(7,或二进制00000111 )的二进制AND是二进制00000011 ,或3。这是一个骑士,它的数值也是3。

    00001011 = 11
AND 00000111 = 7
    ________
    00000011 = 3

分离棋子的颜色是一个简单的问题,即使用二进制和值以及BLACK 位域。例如,你可以把它写成一个叫做is_black 的函数,以确定一个棋子是黑色还是白色。

int
is_black(int piece)
{
  return (piece & BLACK);
}

这样做的原因是:BLACK 的值是8,或二进制00001000 。在C语言中,任何非零值都被视为真,而零总是假的。因此,如果数组元素5,3 中的棋子是黑色的,is_black(board[5][3]) 将返回一个真值(8),如果是白色的,将返回一个假值(0)。

位域

使用位域和掩码是一种常见的方法,无需使用结构就可以组合数据。它们值得添加到你的程序员的 "工具包 "中。虽然数据结构对于需要跟踪相关数据的有序编程来说是一个有价值的工具,但使用单独的元素来跟踪单一的开或关值(如国际象棋的颜色)则效率较低。在这些情况下,考虑使用位域和掩码来更有效地组合你的数据。