ZirconiumX wrote:I have been trying to make a bitboard knight attack generator to fill an array of knight attacks.
Code: Select all
Bitboard KnightMoves(int Square)
{
Bitboard b = 1 << Square, Targets = 0;
Targets |= (b & 0x7F7F7F7F7F7F7F7FULL) << 17;
Targets |= (b & 0x3F3F3F3F3F3F3F3FULL) << 10;
Targets |= (b & 0x3F3F3F3F3F3F3F3FULL) >> 6;
Targets |= (b & 0x7F7F7F7F7F7F7F7FULL) >> 15;
Targets |= (b & 0xFEFEFEFEFEFEFEFEULL) << 15;
Targets |= (b & 0xFCFCFCFCFCFCFCFCULL) << 6;
Targets |= (b & 0xFCFCFCFCFCFCFCFCULL) >> 10;
Targets |= (b & 0xFEFEFEFEFEFEFEFEULL) >> 17;
return Targets;
}
When I try and generate the array I get some strange figures given, e.g. for square a1 I get 0x132096...
Something at the back of my mind says that I'm suffering from wrap-around issues.
Matthew:out
OK I rewrote my code with the same idea. This code is 100% correct and generates King, Knight, and Pawn attacks:
Code: Select all
static uint64_t calc_KAttacks(int sq)
{
const uint64_t b = Bit[sq];
return ((b & ~File[FileA]) << 7)
| ((b & ~File[FileA]) >> 1)
| ((b & ~File[FileA]) >> 9)
| ((b & ~File[FileH]) << 9)
| ((b & ~File[FileH]) << 1)
| ((b & ~File[FileH]) >> 7)
| (b << 8)
| (b >> 8);
}
static uint64_t calc_NAttacks(int sq)
{
const uint64_t b = Bit[sq];
return ((b & ~File[FileA]) << 15)
| ((b & ~File[FileH]) << 17)
| ((b & ~File[FileA]) >> 17)
| ((b & ~File[FileH]) >> 15)
| ((b & ~(File[FileA] | File[FileB])) << 6)
| ((b & ~(File[FileA] | File[FileB])) >> 10)
| ((b & ~(File[FileG] | File[FileH])) << 10)
| ((b & ~(File[FileG] | File[FileH])) >> 6);
}
static uint64_t calc_PAttacks(int color, int sq)
{
const uint64_t b = Bit[sq];
return shift_bit(b & ~File[FileA], color ? -9 : +7)
| shift_bit(b & ~File[FileH], color ? -7 : +9);
}
static void init_KNP_attacks()
{
for (int sq = A1; sq <= H8; sq++) {
KAttacks[sq] = calc_KAttacks(sq);
NAttacks[sq] = calc_NAttacks(sq);
for (int color = White; color <= Black; color++)
PAttacks[color][sq] = calc_PAttacks(color, sq);
}
}
When I do non-functional changes like this, I *always* check the equality of node counts on a benchmark test suite. That's a guideline I got from Marco Costalba, and he was definitely right about that. No non functional commits are accepted before the node count test is passed
Note that my code is far more readable because I avoid using hardcoded hexadecimal numbers, as much as possible. Replacing the bitboard "filters" by their values makes the code illegible for no good reason:
1/ this code is not performance critical, so there's no need to do that
2/ even if it was, you could define constants for the bitboards File(A...H) and have them evaluated at compile time.
The function shift_bit allows me to do positive (<<) or negative (>>) shifts the same way,. This is particularly nice to make the code more condensed and more readable in areas that are not performance critical.
Although the above code is longer (in number of lines) than my previous code, I find it much more pleasant to read. My previous code used way too many imbricated loops:
Code: Select all
static void init_KNP_attacks()
{
const int K_dir[8][2] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
const int N_dir[8][2] = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
const int P_dir[NB_COLOR][2][2] = { {{1,-1},{1,1}}, {{-1,-1},{-1,1}} };
for (int sq = A1; sq <= H8; sq++) {
int r = rank(sq), f = file(sq);
KAttacks[sq] = NAttacks[sq] = 0ULL;
for (int i = 0; i < 8; i++) {
int dr = K_dir[i][0], df = K_dir[i][1];
if (rank_file_ok(r+dr,f+df))
set_bit(&KAttacks[sq], square(r+dr,f+df));
dr = N_dir[i][0], df = N_dir[i][1];
if (rank_file_ok(r+dr,f+df))
set_bit(&NAttacks[sq], square(r+dr,f+df));
}
PAttacks[White][sq] = PAttacks[Black][sq] = 0ULL;
for (int i = 0; i < 2; i++)
for (int c = White; c <= Black; c++) {
int dr = P_dir[c][i][0], df = P_dir[c][i][1];
if (rank_file_ok(r+dr,f+df))
set_bit(&PAttacks[c][sq], square(r+dr,f+df));
}
}
}