您将使用哪些数据结构来表示计算机国际象棋程序的棋盘?
答案 0 :(得分:12)
最初,使用 8 * 8整数数组来代表国际象棋棋盘。
您可以使用此表示法开始编程。给出碎片的点值。例如:
**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
等。 (注意,上面给出的点是每个棋子交易能力的近似值)
在开发应用程序的基本主干并清楚地了解所用算法的工作原理后,尝试使用位板来提高性能。
在位板中,您使用8个8位字来表示电路板。这种表示需要每个棋子的棋盘。在一个比特板中你将存储车的位置,而在另一个比特板中你将存储骑士的位置......等等
位板可以极大地提高应用程序的性能,因为使用位板操作这些部件非常简单快捷。
正如你所指出的那样,
今天的大多数国际象棋图,特别是 那些在64位CPU上运行的,使用一个 位图方法来表示一个 棋盘和生成动作。 x88是 机器的备用板模型 没有64位CPU。
答案 1 :(得分:12)
对于严肃的国际象棋引擎,使用位板是在内存中表示国际象棋棋盘的有效方式。位板比任何基于阵列的表示都要快,特别是在64位架构中,位板可以放在单个CPU寄存器中。
<强>位棋盘强>
位板的基本思想是以64位表示每个棋子类型。在C ++ / C#中它将是ulong/UInt64
。因此,您将保留12个UInt64
变量来代表您的棋盘:每个棋子类型有两个(一个黑色和一个白色),即典当,车,骑士,主教,女王和国王。 UInt64
中的每一位都对应于棋盘上的方块。通常,最低有效位将是a1平方,下一个b1,然后是c1,依此类推行。与棋盘上棋子位置对应的位将设置为1,其他所有位置都设置为0.例如,如果你在a2和h1上有两个白车,那么白车的位置将如下所示:
0000000000000000000000000000000000000000000000000000000110000000
现在举例来说,如果你想在上面的例子中将你的车从a2移动到g2,你需要做的只是对你的位置进行异或:
0000000000000000000000000000000000000000000000000100000100000000
在移动生成方面,Bitboard具有性能优势。还有其他性能优势,弹簧自然地来自位板表示。例如,您可以使用lockless hash tables,这在并行化搜索算法时具有巨大的优势。
进一步阅读
国际象棋引擎开发的最终资源是Chess Programming Wiki。我最近写了this chess engine,用C#实现了位板。一个更好的开源象棋引擎是StockFish,它也在C ++中实现了位板。
答案 2 :(得分:9)
简单的方法是使用8x8整数数组。将0用于空方块并为各个部分指定值:
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的机器的备用板模型。
答案 3 :(得分:4)
当然,有许多不同的方式来代表棋盘,最好的方法取决于对你最重要的东西。
您的两个主要选择是在速度和代码清晰度之间。
如果速度是您的首选,那么您必须为棋盘上的每组棋子使用64位数据类型(例如白棋子,黑棋子,传球棋子)。然后,您可以在生成移动和测试移动合法性时利用本机按位操作。
如果代码的清晰度是优先级,那么请忘记位改组,并像其他人已经建议的那样去寻找抽象的数据类型。请记住,如果你这样走,你可能会很快达到性能上限。
首先,请查看Crafty(C)和SharpChess(C#)的代码。
答案 4 :(得分:4)
在创建我的国际象棋引擎时,我最初使用[8] [8]方法,但最近我改变了我的代码以使用64项阵列代表国际象棋棋盘。我发现这个实现效率大约提高了1/3,至少在我的情况下如此。
在[8] [8]方法中你要考虑的一件事是描述位置。例如,如果您想描述棋子的有效移动,则需要2个字节才能完成。使用[64]项目数组时,您可以使用一个字节进行操作。
要从[64]项目板上的位置转换为[8] [8]板,您只需使用以下计算:
Row =(byte)(index / 8)
Col =(byte)(索引%8)
虽然我发现在递归搜索过程中你永远不必这样做,这是性能敏感的。
有关构建国际象棋引擎的更多信息,请随时访问我的博客,从头开始描述该过程:www.chessbin.com
Adam Berent
答案 5 :(得分:4)
120字节的数组。
这是一个由哨兵方块包围的8x8棋盘(例如255表示一块棋子不能移动到这个方格)。哨兵的深度为2,因此骑士无法跳过。
向右移动添加1.向左移动添加-1。向上10,向下-10,向上和向右对角线11等。方形A1是指数21.H1是指数29.H8是指数99.
所有设计都是为了简单。但它永远不会像位板那么快。
答案 6 :(得分:3)
嗯,不确定这是否有帮助,但Deep Blue使用一个6位数来表示电路板上的特定位置。与使用64位位板的竞争对手相比,这有助于节省芯片上的占用空间。
这可能不相关,因为很可能,您的目标硬件上可能已有64位寄存器。
答案 7 :(得分:3)
标准8x8 board的替代方法是10x12邮箱(所谓的,因为,呃,我猜它看起来像邮箱)。这是一个一维数组,在其“边界”周围包含sentinels,以协助移动生成。它看起来像这样:
-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中):
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));
}
当然,在实际实现中,您将实际的棋子对象放在棋盘标签所在的位置。但你要保留负面的(或类似的东西)。这些位置使得移动生成不那么痛苦,因为通过检查特殊值,您可以轻松地判断您何时离开了。
让我们首先看看为骑士制作合法动作(非滑动动作):
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;
}
我们知道有效的骑士移动距离作品的起点是固定的距离,因此我们只需要检查这些位置是否有效(即不是哨兵方格)。
滑动件不太难。让我们来看看主教:
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;
}
以下是一些例子:
knightMoves("h1", generateEmptyBoard()) => ["f2", "g3"]
bishopMoves("a4", generateEmptyBoard()) => ["b3", "c2", "d1", "b5", "c6", "d7", "e8"]
请注意,slide
函数是一般实现。您应该能够非常轻松地模拟其他滑动件的合法移动。
答案 8 :(得分:2)
使用位板将是表示国际象棋棋盘状态的有效方式。基本思想是使用64位位组来表示电路板上的每个方块,其中第一位通常表示A1(左下方),第64位表示H8(对角方形对角线)。每个玩家(黑色,白色)的每种类型的棋子(棋子,王等)都有自己的位板,所有12个棋盘都构成游戏状态。有关更多信息,请查看此维基百科article。
答案 9 :(得分:1)
我会使用多维数组,以便数组中的每个元素都是对电路板上正方形的网格引用。
因此
board = arrary(A = array (1,2,3,4,5,5,6,7,8),
B = array (12,3,.... etc...
etc...
)
然后板[A] [1] 就是板方A1。
实际上,你会使用数字而不是字母来帮助保持你的数学逻辑,以便允许片段变得简单。
答案 10 :(得分:0)
int[8][8]
0=no piece
1=king
2=queen
3=rook
4=knight
5=bishop
6=pawn
对白色使用正整数,对黑色使用负投注
答案 11 :(得分:0)
我实际上不会对棋盘进行建模,我只是模拟棋子的位置。 那么你可以为国际象棋棋盘设置界限。
Piece.x= x position of piece
Piece.y= y position of piece
答案 12 :(得分:0)
我知道这是一个非常古老的帖子,我在谷歌国际象棋编程时偶然发现了几次,但我觉得我必须提到用一维数组模拟棋盘是完全可行的,例如棋盘[64];
我想说这是棋盘代表的最简单方法......但当然这是一种基本方法。
1D棋盘阵列结构是否比2D数组更有效(它需要嵌套的for循环来访问和操作索引)?
也可以使用具有多于64个正方形的1D阵列来表示OffBoard正方形。棋盘[120]; (正确初始化阵列哨兵和棋盘正方形)。
对于这篇文章的完整性,我觉得我必须提到0x88板阵列表示。这是一种非常流行的方式来表示棋盘,这也是角落的正方形。
答案 13 :(得分:-3)
一个数组可能没问题。如果您想要更方便的“遍历”电路板,您可以轻松地构建方法来抽象出数据结构实现的细节。