Add Pseudo Move Generator (#12)
All checks were successful
Python tests (make) / test (push) Successful in 11s
All checks were successful
Python tests (make) / test (push) Successful in 11s
work on #7 Reviewed-on: #12 Co-authored-by: Josh <josh@joshuaschuett.com> Co-committed-by: Josh <josh@joshuaschuett.com>
This commit is contained in:
@@ -61,14 +61,14 @@ void create_king_attack_cache(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static int first_set_index(uint64_t bb) {
|
||||
int first_set_index(uint64_t bb) {
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
if ((bb >> i) & 1ULL) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pop_lsb_index(uint64_t *bb) {
|
||||
int pop_lsb_index(uint64_t *bb) {
|
||||
if (*bb == 0) return -1;
|
||||
int idx = first_set_index(*bb);
|
||||
// Clears bit.
|
||||
@@ -76,8 +76,463 @@ static int pop_lsb_index(uint64_t *bb) {
|
||||
return idx;
|
||||
}
|
||||
|
||||
void print_board(const struct Board *b) {
|
||||
static const char PIECE_CH[12] = {
|
||||
uint64_t ray_attacks(int start_square, int delta_file, int delta_rank, uint64_t occupied)
|
||||
{
|
||||
uint64_t attacks = 0;
|
||||
|
||||
int start_file = start_square % 8;
|
||||
int start_rank = start_square / 8;
|
||||
|
||||
int next_file = start_file + delta_file;
|
||||
int next_rank = start_rank + delta_rank;
|
||||
|
||||
while (next_file >= 0 && next_file < 8 && next_rank >= 0 && next_rank < 8) {
|
||||
int next_square = next_rank * 8 + next_file;
|
||||
uint64_t next_bit = 1ULL << next_square;
|
||||
|
||||
// can attack this square
|
||||
attacks |= next_bit;
|
||||
|
||||
// blocker: include it, then stop
|
||||
if (occupied & next_bit)
|
||||
break;
|
||||
|
||||
next_file += delta_file;
|
||||
next_rank += delta_rank;
|
||||
}
|
||||
|
||||
return attacks;
|
||||
}
|
||||
|
||||
uint64_t rook_attacks(int square, uint64_t occupied) {
|
||||
return ray_attacks(square, +1, 0, occupied) // east
|
||||
| ray_attacks(square, -1, 0, occupied) // west
|
||||
| ray_attacks(square, 0, +1, occupied) // north
|
||||
| ray_attacks(square, 0, -1, occupied); // south
|
||||
}
|
||||
|
||||
uint64_t bishop_attacks(int square, uint64_t occupied) {
|
||||
return ray_attacks(square, +1, +1, occupied) // NE
|
||||
| ray_attacks(square, -1, +1, occupied) // NW
|
||||
| ray_attacks(square, +1, -1, occupied) // SE
|
||||
| ray_attacks(square, -1, -1, occupied); // SW
|
||||
}
|
||||
|
||||
uint64_t queen_attacks(int square, uint64_t occupied) {
|
||||
// Simply combine both types of attacks
|
||||
return rook_attacks(square, occupied) | bishop_attacks(square, occupied);
|
||||
}
|
||||
|
||||
void push_move(struct Move *out, int *count, int from, int to, uint8_t piece, uint8_t promo, uint8_t flags)
|
||||
{
|
||||
out[*count] = (struct Move){
|
||||
.from = (uint16_t)from,
|
||||
.to = (uint16_t)to,
|
||||
.piece = piece,
|
||||
.promo = promo, // 0 for non-promo;
|
||||
.flags = flags
|
||||
};
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
/**
|
||||
PAWN struct Movement Psuedo Moves
|
||||
|
||||
|
||||
Separate the white and black pawn logic. Although the logic is similar,
|
||||
by separating the logic, we can isolate the different cases and have clearer parent functions.
|
||||
These pieces are also the only ones that move in opposite ways, meaning we only need
|
||||
to separate out seemingly related code for this case. Additionally, pawns have the
|
||||
most complicated movement patterns and behaviors in the game. Separating out the logic should
|
||||
hopefully make debugging easier.
|
||||
*/
|
||||
void gen_white_pawn_quiet_pushes(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t occ = board->occ[BOTH];
|
||||
const uint64_t empty = ~occ;
|
||||
|
||||
const uint64_t pawns = board->pieces[P];
|
||||
|
||||
// One-step pushes: shift pawns up by 8 onto empty squares
|
||||
uint64_t one_step = (pawns << 8) & empty;
|
||||
|
||||
// Exclude promotions here (dest on rank 8) — handled by a separate promo-push function
|
||||
uint64_t one_step_no_promo = one_step & ~RANK_8;
|
||||
|
||||
// Two-step pushes: those one-step pawns that landed on rank 3 can go one more if still empty
|
||||
// (means they originally stood on rank 2)
|
||||
uint64_t two_step = ((one_step & RANK_3) << 8) & empty;
|
||||
|
||||
uint64_t bb = one_step_no_promo;
|
||||
while (bb) {
|
||||
int to = pop_lsb_index(&bb);
|
||||
int from = to - 8;
|
||||
push_move(out, count, from, to, P, 0, MF_NONE);
|
||||
}
|
||||
|
||||
bb = two_step;
|
||||
while (bb) {
|
||||
int to = pop_lsb_index(&bb);
|
||||
int from = to - 16;
|
||||
push_move(out, count, from, to, P, 0, MF_DOUBLE_PUSH);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_black_pawn_quiet_pushes(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t occ = board->occ[BOTH];
|
||||
const uint64_t empty = ~occ;
|
||||
|
||||
const uint64_t pawns = board->pieces[p];
|
||||
|
||||
// One-step pushes: shift pawns down by 8 onto empty squares
|
||||
uint64_t one_step = (pawns >> 8) & empty;
|
||||
|
||||
// Exclude promotions here (dest on rank 1) — handle in a promo-push function
|
||||
uint64_t one_step_no_promo = one_step & ~RANK_1;
|
||||
|
||||
uint64_t two_step = ((one_step & RANK_6) >> 8) & empty;
|
||||
|
||||
uint64_t bb = one_step_no_promo;
|
||||
while (bb) {
|
||||
int to = pop_lsb_index(&bb);
|
||||
int from = to + 8;
|
||||
push_move(out, count,from, to, p, 0, MF_NONE);
|
||||
}
|
||||
|
||||
bb = two_step;
|
||||
while (bb) {
|
||||
int to = pop_lsb_index(&bb);
|
||||
int from = to + 16;
|
||||
push_move(out, count,from, to, p, 0, MF_DOUBLE_PUSH);
|
||||
}
|
||||
}
|
||||
|
||||
// We will only allow pawns to promote to queen. Technically, they should be allowed
|
||||
// to promote to any of the following: Rook, Bishop, Knight, Queen.
|
||||
void gen_white_pawn_push_promotions(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t occ = board->occ[BOTH];
|
||||
const uint64_t empty = ~occ;
|
||||
const uint64_t pawns = board->pieces[P];
|
||||
|
||||
// destinations on rank 8 reachable by a single push
|
||||
uint64_t promos = ((pawns << 8) & empty) & RANK_8;
|
||||
|
||||
while (promos) {
|
||||
int to = pop_lsb_index(&promos);
|
||||
int from = to - 8;
|
||||
push_move(out, count, from, to, P, Q, MF_PROMO);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_black_pawn_push_promotions(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t occ = board->occ[BOTH];
|
||||
const uint64_t empty = ~occ;
|
||||
const uint64_t pawns = board->pieces[p];
|
||||
|
||||
// destinations on rank 1 reachable by a single push
|
||||
uint64_t promos = ((pawns >> 8) & empty) & RANK_1;
|
||||
|
||||
while (promos) {
|
||||
int to = pop_lsb_index(&promos);
|
||||
int from = to + 8;
|
||||
push_move(out, count, from, to, p, q, MF_PROMO);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_white_pawn_capture_promotions(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t opp = board->occ[BLACK];
|
||||
// left capture (from white view): +7, mask off file A
|
||||
uint64_t left = ((board->pieces[P] & ~FILE_A) << 7) & opp & RANK_8;
|
||||
while (left) {
|
||||
int to = pop_lsb_index(&left);
|
||||
int from = to - 7;
|
||||
push_move(out, count,from, to, P, Q, MF_CAPTURE | MF_PROMO);
|
||||
}
|
||||
// right capture: +9, mask off file H
|
||||
uint64_t right = ((board->pieces[P] & ~FILE_H) << 9) & opp & RANK_8;
|
||||
while (right) {
|
||||
int to = pop_lsb_index(&right);
|
||||
int from = to - 9;
|
||||
push_move(out, count,from, to, P, Q, MF_CAPTURE | MF_PROMO);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_black_pawn_capture_promotions(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t opp = board->occ[WHITE];
|
||||
// from black view, “left” is -7 (mask off file H before shifting)
|
||||
uint64_t left = ((board->pieces[p] & ~FILE_H) >> 7) & opp & RANK_1;
|
||||
while (left) {
|
||||
int to = pop_lsb_index(&left);
|
||||
int from = to + 7;
|
||||
push_move(out, count,from, to, p, q, MF_CAPTURE | MF_PROMO);
|
||||
}
|
||||
// “right” is -9 (mask off file A)
|
||||
uint64_t right = ((board->pieces[p] & ~FILE_A) >> 9) & opp & RANK_1;
|
||||
while (right) {
|
||||
int to = pop_lsb_index(&right);
|
||||
int from = to + 9;
|
||||
push_move(out, count,from, to, p, q, MF_CAPTURE | MF_PROMO);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_white_pawn_captures(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t pawns = board->pieces[P];
|
||||
const uint64_t opp = board->occ[BLACK];
|
||||
|
||||
// Normal captures (exclude promotion rank)
|
||||
uint64_t left_caps = ((pawns & ~FILE_A) << 7) & opp & ~RANK_8;
|
||||
uint64_t right_caps = ((pawns & ~FILE_H) << 9) & opp & ~RANK_8;
|
||||
|
||||
// Emit normal captures
|
||||
while (left_caps) {
|
||||
int to = pop_lsb_index(&left_caps);
|
||||
int from = to - 7;
|
||||
push_move(out, count,from, to, P, 0, MF_CAPTURE);
|
||||
}
|
||||
while (right_caps) {
|
||||
int to = pop_lsb_index(&right_caps);
|
||||
int from = to - 9;
|
||||
push_move(out, count,from, to, P, 0, MF_CAPTURE);
|
||||
}
|
||||
|
||||
// En passant (destination is ep_square)
|
||||
if (board->ep_square >= 0) {
|
||||
uint64_t ep = 1ULL << board->ep_square;
|
||||
|
||||
// From-squares that can capture onto ep (reverse of +7/+9)
|
||||
uint64_t ep_from = (((ep & ~FILE_H) >> 7) | ((ep & ~FILE_A) >> 9)) & pawns;
|
||||
|
||||
while (ep_from) {
|
||||
int from = pop_lsb_index(&ep_from);
|
||||
int to = board->ep_square;
|
||||
// EP never promotes, so no promo piece; still a capture
|
||||
push_move(out, count,from, to, P, 0, MF_CAPTURE | MF_ENPASSANT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gen_black_pawn_captures(const struct Board *board, struct Move *out, int *count) {
|
||||
const uint64_t pawns = board->pieces[p];
|
||||
const uint64_t opp = board->occ[WHITE];
|
||||
|
||||
// Normal captures (exclude promotion rank)
|
||||
uint64_t left_caps = ((pawns & ~FILE_H) >> 7) & opp & ~RANK_1; // from black POV, "left" is -7
|
||||
uint64_t right_caps = ((pawns & ~FILE_A) >> 9) & opp & ~RANK_1; // "right" is -9
|
||||
|
||||
while (left_caps) {
|
||||
int to = pop_lsb_index(&left_caps);
|
||||
int from = to + 7;
|
||||
push_move(out, count,from, to, p, 0, MF_CAPTURE);
|
||||
}
|
||||
while (right_caps) {
|
||||
int to = pop_lsb_index(&right_caps);
|
||||
int from = to + 9;
|
||||
push_move(out, count,from, to, p, 0, MF_CAPTURE);
|
||||
}
|
||||
|
||||
// En passant
|
||||
if (board->ep_square >= 0) {
|
||||
uint64_t ep = 1ULL << board->ep_square;
|
||||
|
||||
// From-squares that can capture onto ep (reverse of -7/-9)
|
||||
uint64_t ep_from = (((ep & ~FILE_A) << 7) | ((ep & ~FILE_H) << 9)) & pawns;
|
||||
|
||||
while (ep_from) {
|
||||
int from = pop_lsb_index(&ep_from);
|
||||
int to = board->ep_square;
|
||||
push_move(out, count,from, to, p, 0, MF_CAPTURE | MF_ENPASSANT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
|
||||
Pieces that are not PAWNs
|
||||
|
||||
|
||||
*/
|
||||
void gen_knight_moves(const struct Board *board, struct Move *out, int *count, bool captures_only) {
|
||||
enum Color side = board->side_to_move;
|
||||
uint64_t own = board->occ[side];
|
||||
uint64_t opp = board->occ[side ^ 1];
|
||||
uint8_t pid = (side == WHITE) ? N : n;
|
||||
uint64_t bb = (side == WHITE) ? board->pieces[N] : board->pieces[n];
|
||||
|
||||
while (bb) {
|
||||
int from = pop_lsb_index(&bb);
|
||||
uint64_t mask = KNIGHT_ATTACKS[from] & ~own;
|
||||
uint64_t caps = mask & opp;
|
||||
|
||||
if (!captures_only) {
|
||||
uint64_t quiet = mask & ~opp;
|
||||
while (quiet) {
|
||||
int to = pop_lsb_index(&quiet);
|
||||
push_move(out, count, from, to, pid, 0, MF_NONE);
|
||||
}
|
||||
}
|
||||
while (caps) {
|
||||
int to = pop_lsb_index(&caps);
|
||||
push_move(out, count, from, to, pid, 0, MF_CAPTURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gen_bishop_moves(const struct Board *board, struct Move *out, int *count, bool captures_only) {
|
||||
enum Color side = board->side_to_move;
|
||||
uint64_t own = board->occ[side];
|
||||
uint64_t opp = board->occ[side ^ 1];
|
||||
uint64_t occ = board->occ[BOTH];
|
||||
uint8_t pid = (side == WHITE) ? B : b;
|
||||
uint64_t bb = (side == WHITE) ? board->pieces[B] : board->pieces[b];
|
||||
|
||||
while (bb) {
|
||||
int from = pop_lsb_index(&bb);
|
||||
uint64_t mask = bishop_attacks(from, occ) & ~own;
|
||||
uint64_t caps = mask & opp;
|
||||
|
||||
if (!captures_only) {
|
||||
uint64_t quiet = mask & ~opp;
|
||||
while (quiet) {
|
||||
int to = pop_lsb_index(&quiet);
|
||||
push_move(out, count, from, to, pid, 0, MF_NONE);
|
||||
}
|
||||
}
|
||||
while (caps) {
|
||||
int to = pop_lsb_index(&caps);
|
||||
push_move(out, count, from, to, pid, 0, MF_CAPTURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gen_rook_moves(const struct Board *board, struct Move *out, int *count, bool captures_only) {
|
||||
enum Color side = board->side_to_move;
|
||||
uint64_t own = board->occ[side];
|
||||
uint64_t opp = board->occ[side ^ 1];
|
||||
uint64_t occ = board->occ[BOTH];
|
||||
uint8_t pid = (side == WHITE) ? R : r;
|
||||
uint64_t bb = (side == WHITE) ? board->pieces[R] : board->pieces[r];
|
||||
|
||||
while (bb) {
|
||||
int from = pop_lsb_index(&bb);
|
||||
uint64_t mask = rook_attacks(from, occ) & ~own;
|
||||
uint64_t caps = mask & opp;
|
||||
|
||||
if (!captures_only) {
|
||||
uint64_t quiet = mask & ~opp;
|
||||
while (quiet) {
|
||||
int to = pop_lsb_index(&quiet);
|
||||
push_move(out, count,from, to, pid, 0, MF_NONE);
|
||||
}
|
||||
}
|
||||
while (caps) {
|
||||
int to = pop_lsb_index(&caps);
|
||||
push_move(out, count,from, to, pid, 0, MF_CAPTURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gen_queen_moves(const struct Board *board, struct Move *out, int *count, bool captures_only) {
|
||||
enum Color side = board->side_to_move;
|
||||
uint64_t own = board->occ[side];
|
||||
uint64_t opp = board->occ[side ^ 1];
|
||||
uint64_t occ = board->occ[BOTH];
|
||||
uint8_t pid = (side == WHITE) ? Q : q;
|
||||
uint64_t bb = (side == WHITE) ? board->pieces[Q] : board->pieces[q];
|
||||
|
||||
while (bb) {
|
||||
int from = pop_lsb_index(&bb);
|
||||
uint64_t mask = (rook_attacks(from, occ) | bishop_attacks(from, occ)) & ~own;
|
||||
uint64_t caps = mask & opp;
|
||||
|
||||
if (!captures_only) {
|
||||
uint64_t quiet = mask & ~opp;
|
||||
while (quiet) {
|
||||
int to = pop_lsb_index(&quiet);
|
||||
push_move(out, count,from, to, pid, 0, MF_NONE);
|
||||
}
|
||||
}
|
||||
while (caps) {
|
||||
int to = pop_lsb_index(&caps);
|
||||
push_move(out, count,from, to, pid, 0, MF_CAPTURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gen_king_moves(const struct Board *board, struct Move *out, int *count, bool captures_only) {
|
||||
enum Color side = board->side_to_move;
|
||||
uint64_t own = board->occ[side];
|
||||
uint64_t opp = board->occ[side ^ 1];
|
||||
uint8_t pid = (side == WHITE) ? K : k;
|
||||
uint64_t kk = (side == WHITE) ? board->pieces[K] : board->pieces[k];
|
||||
|
||||
if (!kk) return;
|
||||
int from = first_set_index(kk);
|
||||
uint64_t mask = KING_ATTACKS[from] & ~own;
|
||||
uint64_t caps = mask & opp;
|
||||
|
||||
if (!captures_only) {
|
||||
uint64_t quiet = mask & ~opp;
|
||||
while (quiet) {
|
||||
int to = pop_lsb_index(&quiet);
|
||||
push_move(out, count,from, to, pid, 0, MF_NONE);
|
||||
}
|
||||
}
|
||||
while (caps) {
|
||||
int to = pop_lsb_index(&caps);
|
||||
push_move(out, count,from, to, pid, 0, MF_CAPTURE);
|
||||
}
|
||||
|
||||
if (!captures_only) {
|
||||
uint64_t occ = board->occ[BOTH];
|
||||
if (side == WHITE) {
|
||||
if ((board->castling_rights & CASTLE_WK) && !(occ & WK_EMPTY_MASK)) {
|
||||
push_move(out, count,E1, WK_TO, K, 0, MF_CASTLE);
|
||||
}
|
||||
if ((board->castling_rights & CASTLE_WQ) && !(occ & WQ_EMPTY_MASK)) {
|
||||
push_move(out, count,E1, WQ_TO, K, 0, MF_CASTLE);
|
||||
}
|
||||
} else {
|
||||
if ((board->castling_rights & CASTLE_BK) && !(occ & BK_EMPTY_MASK)) {
|
||||
push_move(out, count,E8, BK_TO, k, 0, MF_CASTLE);
|
||||
}
|
||||
if ((board->castling_rights & CASTLE_BQ) && !(occ & BQ_EMPTY_MASK)) {
|
||||
push_move(out, count,E8, BQ_TO, k, 0, MF_CASTLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int gen_pseudo_moves(const struct Board *board, struct Move *moves, bool captures_only) {
|
||||
int count = 0;
|
||||
|
||||
if (board->side_to_move == WHITE) {
|
||||
if (!captures_only) {
|
||||
gen_white_pawn_quiet_pushes(board, moves, &count);
|
||||
gen_white_pawn_push_promotions(board, moves, &count);
|
||||
}
|
||||
gen_white_pawn_captures(board, moves, &count);
|
||||
gen_white_pawn_capture_promotions(board, moves, &count);
|
||||
} else {
|
||||
if (!captures_only) {
|
||||
gen_black_pawn_quiet_pushes(board, moves, &count);
|
||||
gen_black_pawn_push_promotions(board, moves, &count);
|
||||
}
|
||||
gen_black_pawn_captures(board, moves, &count);
|
||||
gen_black_pawn_capture_promotions(board, moves, &count);
|
||||
}
|
||||
|
||||
gen_knight_moves(board, moves, &count, captures_only);
|
||||
gen_king_moves(board, moves, &count, captures_only);
|
||||
gen_rook_moves(board, moves, &count, captures_only);
|
||||
gen_bishop_moves(board, moves, &count, captures_only);
|
||||
gen_queen_moves(board, moves, &count, captures_only);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
void print_board(const struct Board *board) {
|
||||
const char PIECE_CH[12] = {
|
||||
'P','N','B','R','Q','K',
|
||||
'p','n','b','r','q','k'
|
||||
};
|
||||
@@ -86,7 +541,7 @@ void print_board(const struct Board *b) {
|
||||
for (int i = 0; i < 64; ++i) grid[i] = '.';
|
||||
|
||||
for (int p = 0; p < 12; ++p) {
|
||||
uint64_t bb = b->pieces[p];
|
||||
uint64_t bb = board->pieces[p];
|
||||
while (bb) {
|
||||
int sq = pop_lsb_index(&bb);
|
||||
grid[sq] = PIECE_CH[p];
|
||||
|
||||
@@ -1,33 +1,116 @@
|
||||
/**
|
||||
A quick test script to debug and visually inspect functions
|
||||
and operations.
|
||||
*/
|
||||
|
||||
|
||||
// engine/src/test_pawns.c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "bitboard.h"
|
||||
#include "fen.h"
|
||||
|
||||
enum { MAX_MOVES = 256 };
|
||||
|
||||
static int popcount64(uint64_t x){ int c=0; while(x){ x&=x-1; ++c; } return c; }
|
||||
static const char NAME[12] = {'P','N','B','R','Q','K','p','n','b','r','q','k'};
|
||||
void sq_to_coord(int sq, char out[3]) {
|
||||
out[0] = 'a' + (sq % 8);
|
||||
out[1] = '1' + (sq / 8);
|
||||
out[2] = '\0';
|
||||
}
|
||||
|
||||
void print_move(struct Move *m) {
|
||||
char f[3], t[3];
|
||||
sq_to_coord(m->from, f);
|
||||
sq_to_coord(m->to, t);
|
||||
|
||||
printf("%s%s", f, t);
|
||||
if (m->flags) {
|
||||
printf(" [");
|
||||
int first = 1;
|
||||
if (m->flags & MF_CAPTURE) { printf("%sCAP", first?"":"|"); first = 0; }
|
||||
if (m->flags & MF_DOUBLE_PUSH) { printf("%sDP", first?"":"|"); first = 0; }
|
||||
if (m->flags & MF_ENPASSANT) { printf("%sEP", first?"":"|"); first = 0; }
|
||||
if (m->flags & MF_PROMO) { printf("%sPROMO->%c", first?"":"|", (char)(m->promo ? "PNBRQKpnbrqk"[m->promo] : 'Q')); }
|
||||
printf("]");
|
||||
}
|
||||
}
|
||||
|
||||
void dump_moves(const char *title, struct Move *moves, int count) {
|
||||
printf("%s (%d):\n", title, count);
|
||||
for (int i = 0; i < count; ++i) { print_move(&moves[i]); putchar('\n'); }
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char *fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
// change this FEN to target specific cases you want to test
|
||||
//const char *fen_startpos = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
const char *fen_startpos = "8/8/8/8/8/8/8/8 w KQkq - 0 1";
|
||||
const char *fen = fen_startpos;
|
||||
|
||||
struct Board b;
|
||||
memset(&b, 0, sizeof(b));
|
||||
|
||||
int rc = load_fen(&b, fen);
|
||||
|
||||
for (int i=0;i<12;i++) printf("%c: %d\n", NAME[i], popcount64(b.pieces[i]));
|
||||
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "load_fen failed (rc=%d) for FEN:\n%s\n", rc, fen);
|
||||
if (load_fen(&b, fen) != 0) {
|
||||
fprintf(stderr, "load_fen failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_board(&b);
|
||||
struct Move moves[MAX_MOVES];
|
||||
int count;
|
||||
|
||||
/*
|
||||
// --- WHITE pawns: quiet pushes (single + double, no promos) ---
|
||||
count = 0;
|
||||
gen_white_pawn_quiet_pushes(&b, moves, &count);
|
||||
dump_moves("white quiet pushes", moves, count);
|
||||
|
||||
|
||||
// --- BLACK pawns: quiet pushes ---
|
||||
gen_black_pawn_quiet_pushes(&b, moves, &count);
|
||||
dump_moves("black quiet pushes", moves, count);
|
||||
|
||||
printf("COUNT: %d", count);
|
||||
*/
|
||||
|
||||
create_knight_attack_cache();
|
||||
//create_king_attack_cache();
|
||||
//create_pawn_attack_cache();
|
||||
|
||||
int n = gen_pseudo_moves(&b, moves, false);
|
||||
printf("COUNT: %d", n);
|
||||
|
||||
/*
|
||||
// --- WHITE pawns: quiet push promotions (queen-only) ---
|
||||
count = 0;
|
||||
gen_white_pawn_push_promotions_q(&b, moves, &count);
|
||||
dump_moves("white push promotions (Q only)", moves, count);
|
||||
|
||||
// --- WHITE pawns: captures (non-promo) + en passant ---
|
||||
count = 0;
|
||||
gen_white_pawn_captures(&b, moves, &count);
|
||||
dump_moves("white captures (non-promo + EP)", moves, count);
|
||||
|
||||
// --- WHITE pawns: capture promotions (queen-only) ---
|
||||
count = 0;
|
||||
gen_white_pawn_capture_promotions_q(&b, moves, &count);
|
||||
dump_moves("white capture promotions (Q only)", moves, count);
|
||||
|
||||
// Flip side to move if you want to test black without changing FEN side
|
||||
b.side_to_move = BLACK;
|
||||
|
||||
// --- BLACK pawns: quiet pushes ---
|
||||
count = 0;
|
||||
gen_black_pawn_quiet_pushes(&b, moves, &count);
|
||||
dump_moves("black quiet pushes", moves, count);
|
||||
|
||||
// --- BLACK pawns: quiet push promotions (queen-only) ---
|
||||
count = 0;
|
||||
gen_black_pawn_push_promotions_q(&b, moves, &count);
|
||||
dump_moves("black push promotions (q only)", moves, count);
|
||||
|
||||
// --- BLACK pawns: captures (non-promo) + en passant ---
|
||||
count = 0;
|
||||
gen_black_pawn_captures(&b, moves, &count);
|
||||
dump_moves("black captures (non-promo + EP)", moves, count);
|
||||
|
||||
// --- BLACK pawns: capture promotions (queen-only) ---
|
||||
count = 0;
|
||||
gen_black_pawn_capture_promotions_q(&b, moves, &count);
|
||||
dump_moves("black capture promotions (q only)", moves, count);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user