What data structures would you use to represent a chessboard for a computer chess program?
问题:
回答1:
Initially, use an 8 * 8 integer array to represent the chess board.
You can start programing using this notation. Give point values for the pieces. For example:
**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
etc. (Note that the points given above are approximations of trading power of each chess piece)
After you develop the basic backbones of your application and clearly understand the working of the algorithms used, try to improve the performance by using bit boards.
In bit boards, you use eight 8 -bit words to represent the boards. This representation needs a board for each chess piece. In one bit board you will be storing the position of the rook while in another you will be storing the position of the knight... etc
Bit boards can improve the performance of your application very much because manipulating the pieces with bit boards are very easy and fast.
As you pointed out,
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.
回答2:
For a serious chess engine, using bitboards is an efficient way to represent a chess board in memory. Bitboards are faster than any array based representation, specially in 64-bit architectures where a bitboard can fit inside a single CPU register.
Bitboards
Basic idea of bitboards is to represent every chess piece type in 64 bits. In C++/C# it will be ulong/UInt64
. So you'll maintain 12 UInt64
variables to represent your chess board: two (one black and one white) for each piece type, namely, pawn, rook, knight, bishop, queen and king. Every bit in a UInt64
will correspond to a square on chessboard. Typically, the least significant bit will be a1 square, the next b1, then c1 and so on in a row-major fashion. The bit corresponding to a piece's location on chess board will be set to 1, all others will be set to 0. For example, if you have two white rooks on a2 and h1 then the white rooks bitboard will look like this:
0000000000000000000000000000000000000000000000000000000110000000
Now for example, if you wanted to move your rook from a2 to g2 in the above example, all you need to do is XOR you bitboard with:
0000000000000000000000000000000000000000000000000100000100000000
Bitboards have a performance advantage when it comes to move generation. There are other performance advantages too that spring naturally from bitboards representation. For example you could use lockless hash tables which are an immense advantage when parallelising your search algorithm.
Further Reading
The ultimate resource for chess engine development is the Chess Programming Wiki. I've recently written this chess engine which implements bitboards in C#. An even better open source chess engine is StockFish which also implements bitboards but in C++.
回答3:
The simple approach is to use an 8x8 integer array. Use 0 for empty squares and assign values for the pieces:
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
Piece moves can be calculated by using the array indexes. For example the white pawns move by increasing the row index by 1, or by 2 if it's the pawn's first move. So the white pawn on [2][1] could move to [3][1] or [4][1].
However this simple 8x8 array representation of has chessboard has several problems. Most notably when you're moving 'sliding' pieces like rooks, bishops and queens you need to constantly be checking the indexes to see if the piece has moved off the board.
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.
回答4:
When creating my chess engine I originally went with the [8][8] approach, however recently I changed my code to represent the chess board using a 64 item array. I found that this implementation was about 1/3 more efficient, at least in my case.
One of the things you want to consider when doing the [8][8] approach is describing positions. For example if you wish to describe a valid move for a chess piece, you will need 2 bytes to do so. While with the [64] item array you can do it with one byte.
To convert from a position on the [64] item board to a [8][8] board you can simply use the following calculations:
Row= (byte)(index / 8)
Col = (byte)(index % 8)
Although I found that you never have to do that during the recursive move searching which is performance sensitive.
For more information on building a chess engine, feel free to visit my blog that describes the process from scratch: www.chessbin.com
Adam Berent
回答5:
Array of 120 bytes.
This is a chessboard of 8x8 surrounded by sentinel squares (e.g. a 255 to indicate that a piece can't move to this square). The sentinels have a depth of two so that a knight can't jump over.
To move right add 1. To move left add -1. Up 10, down -10, up and right diagonal 11 etc. Square A1 is index 21. H1 is index 29. H8 is index 99.
All designed for simplicity. But it's never going to be as fast as bitboards.
回答6:
There are of course a number of different ways to represent a chessboard, and the best way will depend on what is most important to you.
Your two main choices are between speed and code clarity.
If speed is your priority then you must use a 64 bit data type for each set of pieces on the board (e.g. white pawns, black queens, en passant pawns). You can then take advantage of native bitwise operations when generating moves and testing move legality.
If clarity of code is priority then forget bit shuffling and go for nicely abstracted data types like others have already suggested. Just remember that if you go this way you will probably hit a performance ceiling sooner.
To start you off, look at the code for Crafty (C) and SharpChess (C#).
回答7:
Well, not sure if this helps, but Deep Blue used a single 6-bit number to represent a specific position on the board. This helped it save footprint on-chip in comparison to it's competitor, which used a 64-bit bitboard.
This might not be relevant, since chances are, you might have 64 bit registers on your target hardware, already.
回答8:
An alternative to the standard 8x8 board is the 10x12 mailbox (so-called because, uh, I guess it looks like a mailbox). This is a one-dimensional array that includes sentinels around its "borders" to assist with move generation. It looks like this:
-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
You can generate that array like this (in 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));
}
Of course, in a real implementation you'd put actual piece objects where the board labels are. But you'd keep the negative ones (or something equivalent). Those locations make move generation a lot less painful because you can easily tell when you've run off the board by checking for that special value.
Let's first look at generating the legal moves for the knight (a non-sliding piece):
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;
}
We know that the valid Knight moves are a fixed distance from the piece's starting point, so we only needed to check that those locations are valid (i.e. not sentinel squares).
The sliding pieces aren't much harder. Let's look at the bishop:
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;
}
Here are some examples:
knightMoves("h1", generateEmptyBoard()) => ["f2", "g3"]
bishopMoves("a4", generateEmptyBoard()) => ["b3", "c2", "d1", "b5", "c6", "d7", "e8"]
Note that the slide
function is a general implementation. You should be able to model the legal moves of the other sliding pieces fairly easily.
回答9:
Using a bitboard would be an efficient way to represent the state of a chess board. The basic idea is that you use 64bit bitsets to represent each of the squares on the board, where first bit usually represents A1 (the lower left square), and the 64th bit represents H8 (the diagonally opposite square). Each type of piece (pawn, king, etc.) of each player (black, white) gets its own bit board and all 12 of these boards makes up the game state. For more information check out this Wikipedia article.
回答10:
I would use a multidimensional array so that each element in an array is a grid reference to a square on the board.
Thus
board = arrary(A = array (1,2,3,4,5,5,6,7,8),
B = array (12,3,.... etc...
etc...
)
Then board[A][1] is then the board square A1.
In reality you would use numbers not letters to help keep your maths logic for where pieces are allowed to move to simple.
回答11:
int[8][8]
0=no piece
1=king
2=queen
3=rook
4=knight
5=bishop
6=pawn
use positive ints for white and negative ints for black
回答12:
I actually wouldn't model the chess board, I'd just model the position of the pieces. You can have bounds for the chess board then.
Piece.x= x position of piece
Piece.y= y position of piece
回答13:
I know this is a very old post which I have stumbled across a few times when googling chess programming, yet I feel I must mention it is perfectly feasible to model a chessboard with a 1D array e.g. chessboard[64];
I would say this is the simplest approach to chessboard representation...but of course it is a basic approach.
Is a 1D chessboard array structure more efficient than a 2D array (which needs a nested for loop to access and manipulate the indices)?
It is also possible to use a 1D array with more than 64 squares to represent OffBoard squares also e.g. chessboard[120]; (with the array sentinel and board playing squares correctly initialised).
Finally and again for completeness for this post I feel I must mention the 0x88 board array representation. This is quite a popular way to represent a chessboard which also accounts for offboard squares.
回答14:
An array would probably be fine. If you wanted more convenient means of "traversing" the board, you could easily build methods to abstract away the details of the data structure implementation.