使用位字段和掩码编写象棋游戏

使用位字段和掩码是组合数据而不使用结构的常用方法。
75 位读者喜欢这个。
7 open source Android apps for chess players

David Lapetina。由 Opensource.com 修改。CC BY-SA 3.0。

假设您正在用 C 语言编写一个象棋游戏。跟踪棋盘上棋子的一种方法是定义一个结构,该结构定义棋盘上每个可能的棋子及其颜色,这样每个方格都包含该结构的一个元素。例如,您可能有一个看起来像这样的结构

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。值 BLACK (8,或二进制 00001000)和值 ROOK (2,或二进制 00000010)的二进制 OR 是二进制 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] 处的数组元素。那里有什么棋子,是黑色还是白色?要识别象棋棋子,请使用二进制 AND 将元素的值与 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

分离棋子的颜色只是使用二进制 AND 与值和 BLACK 位字段的问题。例如,您可以将其编写为名为 is_black 的函数,以确定棋子是黑色还是白色

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

这之所以有效,是因为值 BLACK 为 8,或二进制 00001000。在 C 编程语言中,任何非零值都被视为 True,而零始终为 False。因此,如果数组元素 5,3 中的棋子是黑色,则 is_black(board[5][3]) 将返回 True 值 (8),如果它是白色,则返回 False 值 (0)。

位字段

使用位字段和掩码是组合数据而不使用结构的常用方法。它们值得添加到您的程序员“工具包”中。虽然数据结构是您需要跟踪相关数据的有序编程的宝贵工具,但使用单独的元素来跟踪单个 On 或 Off 值(例如象棋棋子的颜色)效率较低。在这些情况下,请考虑使用位字段和掩码更有效地组合您的数据。

接下来阅读什么
标签
photo of Jim Hall
Jim Hall 是一位开源软件倡导者和开发人员,以 GNOME 中的可用性测试以及作为 FreeDOS 的创始人兼项目协调员而闻名。

2 条评论

一篇有趣的读物,很好地利用了 C 语言提供的强大功能。

当然,这是一种很有趣的方式来节省一些内存并以有趣的方式使用位。这里有一个节省更多内存的想法。与其使用通常为 4 个字节的 int,不如 #include <stdint.h> 并改用 uint8_t,它只有 1 个字节,因此在棋盘中没有未使用的字节方面可以大幅节省。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.