This is a very deep subject, but here's a few basic thoughts that might get you going.
A bitboard consists of 64 bits. If you designate one corner of the board as the most-significant bit and the opposite corner as the least-significant bit, you can store a bitboard in a single 64-bit integer.
For example, suppose A1 is the LSB and H8 is the MSB. We will fill up the 64-bit integer moving left-to-right (A-to-H) and then down-to-up (1-to-8). Your sample turns into:
0000000000000000000000000100000000100000000000000010000001000000
and this 64-bit integer represents the possible "landing" locations of a knight located at A6. In C this could be stored like:
long long knight_a6 = 0x4020002040;
We can do this for every possible location of the knight, in the same order that we filled out the bitboard (left-to-right, down-to-up). In other words:
A1: 0000000000100000010000000000000000000000000000000000000000000000
A2: <bitboard for a knight at A2>
...
B1: <bitboard for a knight at B1>
...
H8: <bitboard for a knight at H8>
This will take up 64 entries * 64 bits each = 512 bytes of space. Now we can determine the possible landing locations of a knight at any location, just by looking at the corresponding entry in the table. In C it could look something like this:
long long knight[64] = { 0x0020400000000000,
<bitboard for a knight at A2>,
... };
long long knight_a6 = knight[40]; /* A6 = index 40 */
These are all static bitboards: they're just there for your convenience when programming. But you'll probably also want some active bitboards that represent the state of the game. For example, you'll probably want a bitboard that represents where all the pieces are. At the start of the game ranks 1, 2, 7, and 8 are full, so the bitboard looks like this:
1111111111111111000000000000000000000000000000001111111111111111
Or in C:
long long game = 0xFFFF00000000FFFF;
So what can you do with bitboards? Well, for starters you can determine if a piece (say, a knight at A6) is attacking another piece. To do this you perform a logical AND between the game bitboard and the static bitboard for the attacking piece. Let's say the game looks like this:
00000000
00k00000
N0000000
00000000
00000000
00000000
00000000
000000K0
The white king is on G1, the white knight is on A6, and the black king is on C7. The game bitboard would look like this:
0000001000000000000000000000000000000000100000000010000000000000
Let's see if the white knight is putting the black king in check. First, we look up the white knight's "landing" bitboard as described earlier.
0000000000000000000000000100000000100000000000000010000001000000
Now we AND that bitboard together with our game bitboard.
0000001000000000000000000000000000000000100000000010000000000000
AND 0000000000000000000000000100000000100000000000000010000001000000
----------------------------------------------------------------
0000000000000000000000000000000000000000000000000010000000000000
The result is not all-zeroes, so the selected piece is attacking something. Here's it is in C:
long long game = 0x200000000802000;
long long attacker = knight[40]; /* A6 = index 40 */
if (game & attacker != 0)
{
/* The knight at A6 is attacking something! */
}
The next step is to see what he's attacking. There are multiple ways to do this: you could see where the 1 appeared in the ANDed bitboard and find the corresponding square, or you could keep a bitboard for every different type of piece. Let's leave it at that for now though.
Next let's see how pieces can be moved. We'll remove the black king from the board for simplicity.
00000000
00000000
N0000000
00000000
00000000
00000000
00000000
000000K0
Bitboard: 0000001000000000000000000000000000000000100000000000000000000000
Looking at the knight's landing bitboard we can see there are four one's, so four possible moves. Let's take the first one - so we'll erase all the other zeroes in the bitboard for now.
0000000000000000000000000100000000000000000000000000000000000000
To move the knight, we erase him from the game bitboard, and then AND the game bitboard with the knight's move bitboard.
0000001000000000000000000000000000000000100000000000000000000000
ERASE 0000000000000000000000000000000000000000100000000000000000000000
----------------------------------------------------------------
0000001000000000000000000000000000000000000000000000000000000000
OR 0000000000000000000000000100000000000000000000000000000000000000
----------------------------------------------------------------
0000001000000000000000000100000000000000000000000000000000000000
The new gameboard looks like this:
00000000
00000000
00000000
00000000
0N000000
00000000
00000000
000000K0
Now, there are a lot of things brushed over in this answer:
- How do you track what kind of piece is at each location?
- How do you distinguish between white and black?
- How do you distinguish between captures and normal moves?
- How do you determine which squares are "shielded" behind another piece?
- How do you handle pawns, which move in a different way than they capture?
- How do you handle the initial pawn move and capture en passant?
Not all bitboards are the same, either. What if we filled out the bitboard in a fancier order than just going left-to-right, bottom-to-top? Maybe there's some other way to assign squares to bits that makes certain checks or moves faster. The way we've written it is just the simplest to picture.
Again, this is a very deep subject, but hopefully this at least explained some of the basics about how bitboards are used.