将计算机编程为下棋时,如何为棋盘建模?

将计算机编程为下棋时,如何为棋盘建模?

How do I model a chessboard when programming a computer to play chess?

您将使用什么数据结构表示计算机国际象棋程序的国际象棋棋盘?


对于严肃的国际象棋引擎,使用位板是在内存中表示国际象棋棋盘的一种有效方法。位板比任何基于数组的表示形式都快,特别是在64位体系结构中,位板可以容纳在单个CPU寄存器中。

位板

位板的基本思想是用64位表示每种棋子类型。在C / C#中,它将为ulong/UInt64。因此,您将维护12个UInt64变量来代表您的国际象棋棋盘:每种棋子类型分别为两个(一个黑色和一个白色),分别为典当,车子,骑士,主教,皇后和国王。 UInt64中的每一位都将对应于棋chess上的一个正方形。通常,最低有效位将是a1平方,下一个b1,然后是c1,依此类推,以行为主。与棋子在棋盘上的位置相对应的位将设置为1,所有其他位都将设置为0。例如,如果在a2和h1上有两个白鸦,则白鸦的位板上将如下所示:

1
0000000000000000000000000000000000000000000000000000000110000000

现在,例如,如果您想在上面的示例中将车从a2移到g2,您要做的就是对位板进行XOR:

1
0000000000000000000000000000000000000000000000000100000100000000

位板在移动生成方面具有性能优势。从位板表示中自然也会产生其他性能优势。例如,您可以使用无锁哈希表,这在并行化搜索算法时具有巨大优势。

进一步阅读

国际象棋引擎开发的最终资源是国际象棋编程维基。我最近写了这个国际象棋引擎,该引擎在C#中实现了位板。更好的开源国际象棋引擎是StockFish,它也实现了位板,但使用C语言。


最初,使用8 * 8整数数组表示国际象棋棋盘。

您可以使用此符号开始编程。给出零件的点值。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
**White**
9 = white queen
5 = white rook
3 = bishop
3 = knight
1 = pawn

**black**
-9 = white queen
-5 = white rook
-3 = bishop
-3 = knight
-1 = pawn

White King: very large positive number
Black King: very large negative number

等(请注意,以上给出的要点是每个棋子的交易能力的近似值)

在开发了应用程序的基本Backbone 并清楚地了解所使用算法的工作原理之后,请尝试通过使用位板来提高性能。

在位板中,使用八个8位字表示板。此表示法每个棋子都需要一块棋盘。在一个位板上,您将存储车的位置,而在另一个位中,您将存储骑士的位置...等等

位板可以极大地提高应用程序的性能,因为使用位板操纵零件非常容易和快速。

正如您指出的那样,

Most chessprograms today, especially
those that run on a 64 bit CPU, use a
bitmapped approach to represent a
chessboard and generate moves. x88 is
an alternate board model for machines
without 64 bit CPUs.


简单的方法是使用8x8整数数组。使用0表示空白方块,并为片段分配值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1 white pawns
2 white knights
3 white bishops
4 white rooks
5 white queens
6 white king

Black pieces use negative values
-1 black pawn
-2 black knight
etc

8| -4 -2 -3 -5 -6 -3 -2 -4
7| -1 -1 -1 -1 -1 -1 -1 -1
6|  0  0  0  0  0  0  0  0
5|  0  0  0  0  0  0  0  0
4|  0  0  0  0  0  0  0  0
3|  0  0  0  0  0  0  0  0
2|  1  1  1  1  1  1  1  1
1|  4  2  3  5  6  3  2  4
  -------------------------
   1  2  3  4  5  6  7  8

可以通过使用数组索引来计算零件移动。例如,白色棋子通过将行索引增加1来移动,如果它是棋子的第一步,则将行索引增加2。因此,[2] [1]上的白色棋子可以移动到[3] [1]或[4] [1]。

但是,这种具有棋盘格的简单8x8数组表示法存在一些问题。最值得注意的是,当您移动"滑动"的棋子(如菜鸟,主教和皇后)时,您需要不断检查索引,以查看棋子是否已移出棋盘。

当今大多数国际象棋程序,特别是那些在64位CPU上运行的象棋程序,都使用位图表示法来表示棋盘并生成动作。 x88是用于没有64位CPU的计算机的备用主板型号。


120个字节的数组。

这是一个8x8的棋盘,周围有前哨正方形(例如255,表示棋子无法移动到该正方形)。前哨的深度为2,因此骑士无法跳过。

向右移动添加1.向左移动添加-1。上10,下-10,上和右对角线11等。正方形A1是索引21。H1是索引29。H8是索引99。

所有设计都是为了简化。但是它永远不会像位板那么快。


创建国际象棋引擎时,我最初采用[8] [8]方法,但是最近我更改了代码,以使用64个项目的数组表示国际象棋棋盘。我发现,至少就我而言,此实现的效率提高了大约1/3。

在进行[8] [8]方法时,您要考虑的一件事是描述职位。例如,如果您想描述棋子的有效举动,则需要2个字节。当使用[64]项目数组时,您可以用一个字节来完成。

要从[64]板上的位置转换为[8] [8]板上的位置,您只需使用以下计算即可:

行=(字节)(索引/ 8)

Col =(字节)(索引%8)

尽管我发现在性能敏感的递归移动搜索过程中,您不必这样做。

有关构建国际象棋引擎的更多信息,请随时访问我的博客,从头开始描述该过程:www.chessbin.com

亚当·贝伦特


好吧,不确定是否有帮助,但是Deep Blue使用单个6位数字表示板上的特定位置。与使用64位位板的竞争对手相比,这有助于节省芯片上的占用空间。

这可能无关紧要,因为很有可能您的目标硬件上已经有64位寄存器。


当然,代表棋盘的方式有很多种,最好的方式取决于对您来说最重要的东西。

您的两个主要选择是速度和代码清晰度。

如果速度是您的首要任务,那么您必须在板上的每组棋子中使用64位数据类型(例如,白色棋子,黑色皇后棋子,传人棋子)。然后,您可以在生成移动并测试移动合法性时利用本机按位操作。

如果代码的清晰性是优先考虑的事情,那么请忽略位改组,而像其他人已经建议的那样,选择很好的抽象数据类型。请记住,如果采用这种方式,您可能会更快达到性能极限。

首先,请查看Crafty(C)和SharpChess(C#)的代码。


10x12邮箱是标准8x8电路板的替代品(之所以这么称呼,是因为嗯,我想它看起来像一个邮箱)。这是一维数组,在其"边界"周围包括哨兵,以协助进行移动生成。看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1,"a8","b8","c8","d8","e8","f8","g8","h8", -1,
-1,"a7","b7","c7","d7","e7","f7","g7","h7", -1,
-1,"a6","b6","c6","d6","e6","f6","g6","h6", -1,
-1,"a5","b5","c5","d5","e5","f5","g5","h5", -1,
-1,"a4","b4","c4","d4","e4","f4","g4","h4", -1,
-1,"a3","b3","c3","d3","e3","f3","g3","h3", -1,
-1,"a2","b2","c2","d2","e2","f2","g2","h2", -1,
-1,"a1","b1","c1","d1","e1","f1","g1","h1", -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1

您可以这样生成数组(在JavaScript中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function generateEmptyBoard() {
    var row = [];
    for(var i = 0; i < 120; i++) {
        row.push((i < 20 || i > 100 || !(i % 10) || i % 10 == 9)
            ? -1
            : i2an(i));
    }
    return row;
}

// converts an index in the mailbox into its corresponding value in algebraic notation
function i2an(i) {
    return"abcdefgh"[(i % 10) - 1] + (10 - Math.floor(i / 10));
}

当然,在实际的实现中,您会将实际的块对象放置在板标签所在的位置。但是您要保留负面的(或类似的东西)。这些位置使生成移动的痛苦减轻了很多,因为您可以通过检查该特殊值来轻松判断何时出局。

让我们首先看看为骑士(防滑的棋子)生成合法动作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function knightMoves(square, board) {
    var i = an2i(square);
    var moves = [];
    [8, 12, 19, 21].forEach(function(offset) {
        [i + offset, i - offset].forEach(function(pos) {
            // make sure we're on the board
            if (board[pos] != -1) {
                // in a real implementation you'd also check whether
                // the squares you encounter are occupied
                moves.push(board[pos]);
            }
        });
    });
    return moves;
}

// converts a position in algebraic notation into its location in the mailbox
function an2i(square) {
    return"abcdefgh".indexOf(square[0]) + 1 + (10 - square[1]) * 10;
}

我们知道有效的Knight移动距离棋子的起点有固定的距离,因此我们只需要检查这些位置是否有效(即不是前哨正方形)即可。

滑动件并不难。让我们看一下主教:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function bishopMoves(square, board) {
    var oSlide = function(direction) {
        return slide(square, direction, board);
    }
    return [].concat(oSlide(11), oSlide(-11), oSlide(9), oSlide(-9));
}

function slide(square, direction, board) {
    var moves = [];
    for(var pos = direction + an2i(square); board[pos] != -1; pos += direction) {
        // in a real implementation you'd also check whether
        // the squares you encounter are occupied
        moves.push(board[pos]);
    }
    return moves;
}

以下是一些示例:

1
2
knightMoves("h1", generateEmptyBoard()) => ["f2","g3"]
bishopMoves("a4", generateEmptyBoard()) => ["b3","c2","d1","b5","c6","d7","e8"]

请注意,slide函数是常规实现。您应该能够相当容易地为其他滑动件的合法移动建模。


使用位板是表示棋盘状态的有效方法。基本思想是,您使用64位位集表示板上的每个正方形,其中第一个位通常代表A1(左下角的正方形),而第64个位代表H8(对角相对的正方形)。每个玩家(黑,白)的每种棋子(棋子,国王等)都有自己的位板,所有这12个板都构成游戏状态。有关更多信息,请查看此Wikipedia文章。


我将使用多维数组,以便数组中的每个元素都是对板上正方形的网格参考。

因此

1
2
3
4
board = arrary(A = array (1,2,3,4,5,5,6,7,8),
               B = array (12,3,.... etc...
               etc...          
               )

然后板子[A] [1]是板子正方形A1。

实际上,您将使用数字而不是字母来帮助保持您的数学逻辑,以允许将片段移动到简单的地方。


我知道这是一个非常古老的帖子,在使用国际象棋编程时偶然发现了几次,但是我觉得我必须提到用一维数组(例如1D数组)对国际象棋棋盘建模是完全可行的。棋盘[64];

我会说这是最简单的棋盘表示方法……但是,这当然是一种基本方法。

一维棋盘阵列结构是否比二维数组(需要嵌套的for循环访问和操作索引)效率更高?

还可以使用具有超过64个正方形的1D数组来表示OffBoard正方形,例如棋盘[120]; (正确初始化了数组哨兵和棋盘游戏方块)。

最后,为了完整起见,我觉得我必须提到0x88电路板阵列表示形式。这是表示棋盘的一种很流行的方式,该棋盘也占了外侧的正方形。


我实际上不会为国际象棋棋盘建模,我只是为棋子的位置建模。
然后,您可以为国际象棋棋盘划界。

1
2
Piece.x= x position of piece
Piece.y= y position of piece

1
2
3
4
5
6
7
8
9
int[8][8]

0=no piece
1=king
2=queen
3=rook
4=knight
5=bishop
6=pawn

对白色使用正整数,对黑色使用负整数


数组可能很好。如果您想使用更方便的"遍历"电路板的方法,则可以轻松地构建一些方法来抽象出数据结构实现的细节。


推荐阅读

    linux的查看内存命令?

    linux的查看内存命令?,情况,系统,信息,电脑,状态,工具,内存,命令,数据,总量,L

    linux下内存调整命令?

    linux下内存调整命令?,系统,情况,地址,工具,总量,总额,管理,内存,机制,内核,

    linux内存大小命令?

    linux内存大小命令?,系统,情况,电脑,信息,工具,状态,命令,内存,环境,分析,Lin

    linux内存命令大全?

    linux内存命令大全?,地址,系统,信息,工作,命令,情况,代码,分析,数据,工具,[Li

    linux命令监控内存?

    linux命令监控内存?,情况,系统,工具,信息,环境,分析,命令,工作,实时,电脑,lin

    linux内存监控命令?

    linux内存监控命令?,情况,系统,信息,电脑,总量,内存,命令,单位,环境,第一,lin

    linux命令查找内存?

    linux命令查找内存?,情况,系统,信息,工具,状态,分析,数字,内存,命令,适当,怎

    linux常用命令查内存?

    linux常用命令查内存?,系统,情况,实时,分析,工具,信息,内存,总量,状态,命令,

    linux检测内存命令?

    linux检测内存命令?,情况,系统,工具,分析,信息,实时,状态,检测,内存,电脑,怎

    查看内存的命令linux?

    查看内存的命令linux?,系统,情况,信息,工具,数据,内存,实时,分析,电脑,单位,l

    内存回收linux命令?

    内存回收linux命令?,系统,地址,技术,管理,不了,数据,名称,发展,环境,管理系

    linux命令行查询内存?

    linux命令行查询内存?,情况,系统,信息,单位,工具,内存,状态,分析,命令,电脑,l

    linux好用的内存命令?

    linux好用的内存命令?,系统,情况,信息,数据,内存,命令,总量,存量,方法,终端,l

    linux命令剩余内存?

    linux命令剩余内存?,情况,系统,信息,工具,内存,分析,命令,单位,实时,电脑,Lin

    linux命令查看没内存?

    linux命令查看没内存?,情况,系统,信息,工具,内存,单位,分析,命令,总量,数据,

    linux磁盘总内存命令?

    linux磁盘总内存命令?,情况,系统,信息,服务,命令,单位,数据,管理,地址,实时,l

    linux关于内存命令?

    linux关于内存命令?,情况,系统,信息,命令,电脑,总额,工具,代码,位置,暂停,Lin

    linux内存管理命令?

    linux内存管理命令?,系统,等级,工作,信息,命令,基础,地址,情况,管理,标准,Lin

    内存泄漏linux命令?

    内存泄漏linux命令?,工具,检测,信息,服务,系统,分析,中心,报告,交易,生产,Lin

    命令查看linux内存?

    命令查看linux内存?,情况,系统,信息,工具,电脑,命令,内存,环境,数据,总量,查