diff options
Diffstat (limited to 'src/engine')
| -rw-r--r-- | src/engine/moves.c | 39 | ||||
| -rw-r--r-- | src/engine/moves/attack.c | 92 | ||||
| -rw-r--r-- | src/engine/moves/bishop.c | 49 | ||||
| -rw-r--r-- | src/engine/moves/magic.c | 204 | ||||
| -rw-r--r-- | src/engine/moves/queen.c | 27 | ||||
| -rw-r--r-- | src/engine/moves/rook.c | 50 |
6 files changed, 271 insertions, 190 deletions
diff --git a/src/engine/moves.c b/src/engine/moves.c index beaf8a6..3aff11e 100644 --- a/src/engine/moves.c +++ b/src/engine/moves.c @@ -2,6 +2,7 @@ #include "bitboard.h" #include "moves/vec.c" +#include "moves/magic.c" #include "moves/knight.c" #include "moves/pawn.c" #include "moves/rook.c" @@ -258,12 +259,38 @@ bool test_perft_kiwipete_position() { "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" ).position; int d; - d = count_positions(position, 1); printf("d0: %d\n", d); if (d != 48) return false; - d = count_positions(position, 2); printf("d1: %d\n", d); if (d != 2039) return false; - d = count_positions(position, 3); printf("d2: %d\n", d); if (d != 97862) return false; - d = count_positions(position, 4); printf("d3: %d\n", d); if (d != 4085603) return false; - d = count_positions(position, 5); printf("d4: %d\n", d); if (d != 193690690) return false; - d = count_positions(position, 6); printf("d5: %d\n", d); if (d != 8031647685) return false; + d = count_positions(position, 1); printf("d1: %d (expected 48)\n", d); if (d != 48) return false; + printf("--- Kiwipete d2 divide ---\n"); perft_divide(position, 2); + d = count_positions(position, 2); printf("d2: %d (expected 2039)\n", d); if (d != 2039) return false; + + printf("\n--- Debug: investigate e5g6 ---\n"); + { + moves_t moves; + moves_init(&moves); + get_legal_moves(&moves, position); + for (int i = 0; i < moves.length; i++) { + if (moves.moves[i].from == 36 && moves.moves[i].to == 46) { + position_t copy = position; + position_make_move(©, moves.moves[i]); + printf("After Nxe5g6, turn=%d, is e8 attacked by white? %d\n", + copy.turn, square_attacked(copy, 60, WHITE_TURN)); + moves_t black_moves; + moves_init(&black_moves); + get_legal_moves(&black_moves, copy); + printf("Black has %d legal moves:\n", black_moves.length); + for (int j = 0; j < black_moves.length; j++) { + printf(" %c%d%c%d flags=%d\n", + 'a' + (black_moves.moves[j].from % 8), 1 + (black_moves.moves[j].from / 8), + 'a' + (black_moves.moves[j].to % 8), 1 + (black_moves.moves[j].to / 8), + black_moves.moves[j].flags); + } + moves_deinit(black_moves); + break; + } + } + moves_deinit(moves); + } + return true; } diff --git a/src/engine/moves/attack.c b/src/engine/moves/attack.c index e4923ea..2786ad3 100644 --- a/src/engine/moves/attack.c +++ b/src/engine/moves/attack.c @@ -51,95 +51,19 @@ static bool sliding_attacks_square( bool check_bishop ) { bitboard_t all_occupied = whites(position) | blacks(position); - int rank = square / 8; - int file = square % 8; - - bitboard_t enemy_rooks = (by_color == WHITE_TURN) ? position.bitboards[WHITE_ROOK] : position.bitboards[BLACK_ROOK]; - bitboard_t enemy_bishops = (by_color == WHITE_TURN) ? position.bitboards[WHITE_BISHOP] : position.bitboards[BLACK_BISHOP]; - bitboard_t enemy_queens = (by_color == WHITE_TURN) ? position.bitboards[WHITE_QUEEN] : position.bitboards[BLACK_QUEEN]; if (check_rook) { - bitboard_t rook_like = enemy_rooks | enemy_queens; - - int r, idx; - r = rank + 1; - while (r < 8) { - idx = r * 8 + file; - if ((all_occupied >> idx) & 1) { - if ((rook_like >> idx) & 1) return true; - break; - } - r++; - } - r = rank - 1; - while (r >= 0) { - idx = r * 8 + file; - if ((all_occupied >> idx) & 1) { - if ((rook_like >> idx) & 1) return true; - break; - } - r--; - } - int f = file + 1; - while (f < 8) { - idx = rank * 8 + f; - if ((all_occupied >> idx) & 1) { - if ((rook_like >> idx) & 1) return true; - break; - } - f++; - } - f = file - 1; - while (f >= 0) { - idx = rank * 8 + f; - if ((all_occupied >> idx) & 1) { - if ((rook_like >> idx) & 1) return true; - break; - } - f--; - } + bitboard_t rook_like = (by_color == WHITE_TURN) + ? (position.bitboards[WHITE_ROOK] | position.bitboards[WHITE_QUEEN]) + : (position.bitboards[BLACK_ROOK] | position.bitboards[BLACK_QUEEN]); + if (get_rook_attacks(square, all_occupied) & rook_like) return true; } if (check_bishop) { - bitboard_t bishop_like = enemy_bishops | enemy_queens; - - int r, f, idx; - r = rank + 1; f = file + 1; - while (r < 8 && f < 8) { - idx = r * 8 + f; - if ((all_occupied >> idx) & 1) { - if ((bishop_like >> idx) & 1) return true; - break; - } - r++; f++; - } - r = rank - 1; f = file - 1; - while (r >= 0 && f >= 0) { - idx = r * 8 + f; - if ((all_occupied >> idx) & 1) { - if ((bishop_like >> idx) & 1) return true; - break; - } - r--; f--; - } - r = rank - 1; f = file + 1; - while (r >= 0 && f < 8) { - idx = r * 8 + f; - if ((all_occupied >> idx) & 1) { - if ((bishop_like >> idx) & 1) return true; - break; - } - r--; f++; - } - r = rank + 1; f = file - 1; - while (r < 8 && f >= 0) { - idx = r * 8 + f; - if ((all_occupied >> idx) & 1) { - if ((bishop_like >> idx) & 1) return true; - break; - } - r++; f--; - } + bitboard_t bishop_like = (by_color == WHITE_TURN) + ? (position.bitboards[WHITE_BISHOP] | position.bitboards[WHITE_QUEEN]) + : (position.bitboards[BLACK_BISHOP] | position.bitboards[BLACK_QUEEN]); + if (get_bishop_attacks(square, all_occupied) & bishop_like) return true; } return false; diff --git a/src/engine/moves/bishop.c b/src/engine/moves/bishop.c index 0bc47f8..a7b2812 100644 --- a/src/engine/moves/bishop.c +++ b/src/engine/moves/bishop.c @@ -7,52 +7,15 @@ void __forloop_bishop_moves_gen( bitboard_t friendly_pieces, bitboard_t enemy_pieces ) { + bitboard_t all_occupied = friendly_pieces | enemy_pieces; while (friendly_type) { int from = __builtin_ctzll(friendly_type); friendly_type &= friendly_type - 1; - - int og_rank = from / 8; // range: [0, 7] - int og_file = from % 8; // range: [0, 7] - int rank = og_rank + 1, file = og_file + 1, index = rank * 8 + file; - - bitboard_t occupied; - while ((rank < 8 && file < 8) && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = ++rank * 8 + ++file; - } - - rank = og_rank - 1, file = og_file - 1, index = rank * 8 + file; - while ((rank >= 0 && file >= 0) && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = --rank * 8 + --file; - } - - rank = og_rank - 1, file = og_file + 1, index = rank * 8 + file; - while ((rank >= 0 && file < 8) && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = --rank * 8 + ++file; - } - - rank = og_rank + 1, file = og_file - 1, index = rank * 8 + file; - while ((rank < 8 && file >= 0) && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = ++rank * 8 + --file; + bitboard_t attacks = get_bishop_attacks(from, all_occupied) & ~friendly_pieces; + while (attacks) { + int to = __builtin_ctzll(attacks); + attacks &= attacks - 1; + add_move(moves, from, to, .flags = ((enemy_pieces >> to) & 1) ? MOVE_CAPTURE : 0); } } } diff --git a/src/engine/moves/magic.c b/src/engine/moves/magic.c new file mode 100644 index 0000000..43f4a32 --- /dev/null +++ b/src/engine/moves/magic.c @@ -0,0 +1,204 @@ +#include "engine/magic.h" +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +static magic_t rook_table[64]; +static magic_t bishop_table[64]; +static bitboard_t *rook_attacks_flat; +static bitboard_t *bishop_attacks_flat; + +static u64 rng_state = 1070399; + +static u64 random_u64(void) { + rng_state ^= rng_state << 13; + rng_state ^= rng_state >> 7; + rng_state ^= rng_state << 17; + return rng_state; +} + +static u64 random_magic(void) { + return random_u64() & random_u64() & random_u64(); +} + +static bitboard_t compute_rook_mask(int sq) { + bitboard_t mask = 0; + int rank = sq / 8, file = sq % 8; + for (int r = rank + 1; r < 7; r++) mask |= (bitboard_t)1 << (r * 8 + file); + for (int r = rank - 1; r > 0; r--) mask |= (bitboard_t)1 << (r * 8 + file); + for (int f = file + 1; f < 7; f++) mask |= (bitboard_t)1 << (rank * 8 + f); + for (int f = file - 1; f > 0; f--) mask |= (bitboard_t)1 << (rank * 8 + f); + return mask; +} + +static bitboard_t compute_bishop_mask(int sq) { + bitboard_t mask = 0; + int rank = sq / 8, file = sq % 8; + for (int r = rank + 1, f = file + 1; r < 7 && f < 7; r++, f++) + mask |= (bitboard_t)1 << (r * 8 + f); + for (int r = rank - 1, f = file - 1; r > 0 && f > 0; r--, f--) + mask |= (bitboard_t)1 << (r * 8 + f); + for (int r = rank - 1, f = file + 1; r > 0 && f < 7; r--, f++) + mask |= (bitboard_t)1 << (r * 8 + f); + for (int r = rank + 1, f = file - 1; r < 7 && f > 0; r++, f--) + mask |= (bitboard_t)1 << (r * 8 + f); + return mask; +} + +static bitboard_t compute_rook_attacks_ref(int sq, bitboard_t occ) { + bitboard_t attacks = 0; + int rank = sq / 8, file = sq % 8; + for (int r = rank + 1; r < 8; r++) { + bitboard_t b = (bitboard_t)1 << (r * 8 + file); + attacks |= b; + if (occ & b) break; + } + for (int r = rank - 1; r >= 0; r--) { + bitboard_t b = (bitboard_t)1 << (r * 8 + file); + attacks |= b; + if (occ & b) break; + } + for (int f = file + 1; f < 8; f++) { + bitboard_t b = (bitboard_t)1 << (rank * 8 + f); + attacks |= b; + if (occ & b) break; + } + for (int f = file - 1; f >= 0; f--) { + bitboard_t b = (bitboard_t)1 << (rank * 8 + f); + attacks |= b; + if (occ & b) break; + } + return attacks; +} + +static bitboard_t compute_bishop_attacks_ref(int sq, bitboard_t occ) { + bitboard_t attacks = 0; + int rank = sq / 8, file = sq % 8; + for (int r = rank + 1, f = file + 1; r < 8 && f < 8; r++, f++) { + bitboard_t b = (bitboard_t)1 << (r * 8 + f); + attacks |= b; + if (occ & b) break; + } + for (int r = rank - 1, f = file - 1; r >= 0 && f >= 0; r--, f--) { + bitboard_t b = (bitboard_t)1 << (r * 8 + f); + attacks |= b; + if (occ & b) break; + } + for (int r = rank - 1, f = file + 1; r >= 0 && f < 8; r--, f++) { + bitboard_t b = (bitboard_t)1 << (r * 8 + f); + attacks |= b; + if (occ & b) break; + } + for (int r = rank + 1, f = file - 1; r < 8 && f >= 0; r++, f--) { + bitboard_t b = (bitboard_t)1 << (r * 8 + f); + attacks |= b; + if (occ & b) break; + } + return attacks; +} + +static void init_magic_for_square( + int sq, magic_t *entry, bool is_bishop, + bitboard_t *occs, bitboard_t *atts, bitboard_t *used +) { + int bits = 64 - entry->shift; + int count = 1 << bits; + bitboard_t mask = entry->mask; + + for (int i = 0; i < count; i++) { + occs[i] = 0; + bitboard_t m = mask; + for (int j = 0; m; j++) { + if (i & (1 << j)) { + occs[i] |= (bitboard_t)1 << __builtin_ctzll(m); + } + m &= m - 1; + } + atts[i] = is_bishop + ? compute_bishop_attacks_ref(sq, occs[i]) + : compute_rook_attacks_ref(sq, occs[i]); + } + + for (int tries = 0; tries < 100000000; tries++) { + bitboard_t magic = random_magic(); + if (__builtin_popcountll((magic * mask) & 0xFF00000000000000ULL) < 6) + continue; + + memset(used, 0, count * sizeof(bitboard_t)); + bool fail = false; + + for (int i = 0; i < count && !fail; i++) { + int idx = (int)(((occs[i] * magic) >> (64 - bits)) & (count - 1)); + if (used[idx] == 0) { + used[idx] = atts[i]; + } else if (used[idx] != atts[i]) { + fail = true; + } + } + + if (!fail) { + entry->magic = magic; + for (int i = 0; i < count; i++) { + int idx = (int)(((occs[i] * magic) >> (64 - bits)) & (count - 1)); + entry->attacks[idx] = atts[i]; + } + return; + } + } + + assert(0 && "Failed to find magic number"); +} + +void magic_init(void) { + for (int sq = 0; sq < 64; sq++) { + rook_table[sq].mask = compute_rook_mask(sq); + rook_table[sq].shift = 64 - __builtin_popcountll(rook_table[sq].mask); + bishop_table[sq].mask = compute_bishop_mask(sq); + bishop_table[sq].shift = 64 - __builtin_popcountll(bishop_table[sq].mask); + } + + int rook_total = 0, bishop_total = 0; + for (int sq = 0; sq < 64; sq++) { + rook_total += 1 << (64 - rook_table[sq].shift); + bishop_total += 1 << (64 - bishop_table[sq].shift); + } + + rook_attacks_flat = (bitboard_t*)malloc(rook_total * sizeof(bitboard_t)); + bishop_attacks_flat = (bitboard_t*)malloc(bishop_total * sizeof(bitboard_t)); + assert(rook_attacks_flat && bishop_attacks_flat); + + int rook_off = 0, bishop_off = 0; + for (int sq = 0; sq < 64; sq++) { + rook_table[sq].attacks = rook_attacks_flat + rook_off; + rook_off += 1 << (64 - rook_table[sq].shift); + bishop_table[sq].attacks = bishop_attacks_flat + bishop_off; + bishop_off += 1 << (64 - bishop_table[sq].shift); + } + + int max_count = 1 << 12; + bitboard_t *occs = (bitboard_t*)malloc(max_count * sizeof(bitboard_t)); + bitboard_t *atts = (bitboard_t*)malloc(max_count * sizeof(bitboard_t)); + bitboard_t *used = (bitboard_t*)malloc(max_count * sizeof(bitboard_t)); + assert(occs && atts && used); + + for (int sq = 0; sq < 64; sq++) { + init_magic_for_square(sq, &rook_table[sq], false, occs, atts, used); + } + for (int sq = 0; sq < 64; sq++) { + init_magic_for_square(sq, &bishop_table[sq], true, occs, atts, used); + } + + free(occs); + free(atts); + free(used); +} + +bitboard_t get_rook_attacks(int sq, bitboard_t occ) { + magic_t *m = &rook_table[sq]; + return m->attacks[((occ & m->mask) * m->magic) >> m->shift]; +} + +bitboard_t get_bishop_attacks(int sq, bitboard_t occ) { + magic_t *m = &bishop_table[sq]; + return m->attacks[((occ & m->mask) * m->magic) >> m->shift]; +} diff --git a/src/engine/moves/queen.c b/src/engine/moves/queen.c index 3e0b8ef..bd3f564 100644 --- a/src/engine/moves/queen.c +++ b/src/engine/moves/queen.c @@ -16,17 +16,18 @@ void get_queen_moves(moves_t* moves, position_t position) { enemy_pieces = whites(position); friendly_queens = position.bitboards[BLACK_QUEEN]; } - - __forloop_rook_moves_gen( - moves, - friendly_queens, - friendly_pieces, - enemy_pieces - ); - __forloop_bishop_moves_gen( - moves, - friendly_queens, - friendly_pieces, - enemy_pieces - ); + + bitboard_t all_occupied = friendly_pieces | enemy_pieces; + bitboard_t queens = friendly_queens; + while (queens) { + int from = __builtin_ctzll(queens); + queens &= queens - 1; + bitboard_t attacks = (get_rook_attacks(from, all_occupied) | + get_bishop_attacks(from, all_occupied)) & ~friendly_pieces; + while (attacks) { + int to = __builtin_ctzll(attacks); + attacks &= attacks - 1; + add_move(moves, from, to, .flags = ((enemy_pieces >> to) & 1) ? MOVE_CAPTURE : 0); + } + } } diff --git a/src/engine/moves/rook.c b/src/engine/moves/rook.c index abbc784..0938de9 100644 --- a/src/engine/moves/rook.c +++ b/src/engine/moves/rook.c @@ -7,53 +7,15 @@ void __forloop_rook_moves_gen( bitboard_t friendly_pieces, bitboard_t enemy_pieces ) { + bitboard_t all_occupied = friendly_pieces | enemy_pieces; while (friendly_type) { int from = __builtin_ctzll(friendly_type); friendly_type &= friendly_type - 1; - - int og_rank = from / 8; // range: [0, 7] - int og_file = from % 8; // range: [0, 7] - int rank = og_rank + 1, file = og_file, index = rank * 8 + file; - - - bitboard_t occupied; - while (rank < 8 && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = ++rank * 8 + file; - } - - rank = og_rank - 1, file = og_file, index = rank * 8 + file; - while (rank >= 0 && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = --rank * 8 + file; - } - - rank = og_rank, file = og_file + 1, index = rank * 8 + file; - while (file < 8 && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = rank * 8 + ++file; - } - - rank = og_rank, file = og_file - 1, index = rank * 8 + file; - while (file >= 0 && !occupied_by(friendly_pieces, index)) { - occupied = occupied_by(enemy_pieces, index); - add_move(moves, from, index, .flags = occupied ? MOVE_CAPTURE : 0); - if (occupied) { - break; - } - index = rank * 8 + --file; + bitboard_t attacks = get_rook_attacks(from, all_occupied) & ~friendly_pieces; + while (attacks) { + int to = __builtin_ctzll(attacks); + attacks &= attacks - 1; + add_move(moves, from, to, .flags = ((enemy_pieces >> to) & 1) ? MOVE_CAPTURE : 0); } } } |
