Pawns are complicated pieces and have multiple cases to account for. Additionally, black and white pawns move in opposite ways to each other. We separated out the two colors to help keep the code clearer at a higher level, and to hopefully help with debugging in the future.
This commit is contained in:
@@ -135,6 +135,214 @@ static void push_move(struct Move *out, int *count, int from, int to, uint8_t pi
|
|||||||
(*count)++;
|
(*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.
|
||||||
|
*/
|
||||||
|
static void gen_white_pawn_quiet_pushes(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t occ = b->occ[BOTH];
|
||||||
|
const uint64_t empty = ~occ;
|
||||||
|
|
||||||
|
const uint64_t pawns = b->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, n, from, to, P, 0, MF_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bb = two_step;
|
||||||
|
while (bb) {
|
||||||
|
int to = pop_lsb_index(&bb);
|
||||||
|
int from = to - 16;
|
||||||
|
push_move(out, n, from, to, P, 0, MF_DOUBLE_PUSH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_black_pawn_quiet_pushes(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t occ = b->occ[BOTH];
|
||||||
|
const uint64_t empty = ~occ;
|
||||||
|
|
||||||
|
const uint64_t pawns = b->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, n, from, to, p, 0, MF_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bb = two_step;
|
||||||
|
while (bb) {
|
||||||
|
int to = pop_lsb_index(&bb);
|
||||||
|
int from = to + 16;
|
||||||
|
push_move(out, n, 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.
|
||||||
|
static void gen_white_pawn_push_promotions(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t occ = b->occ[BOTH];
|
||||||
|
const uint64_t empty = ~occ;
|
||||||
|
const uint64_t pawns = b->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, n, from, to, P, Q, MF_PROMO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_black_pawn_push_promotions(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t occ = b->occ[BOTH];
|
||||||
|
const uint64_t empty = ~occ;
|
||||||
|
const uint64_t pawns = b->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, n, from, to, p, q, MF_PROMO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_white_pawn_capture_promotions(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t opp = b->occ[BLACK];
|
||||||
|
// left capture (from white view): +7, mask off file A
|
||||||
|
uint64_t left = ((b->pieces[P] & ~FILE_A) << 7) & opp & RANK_8;
|
||||||
|
while (left) {
|
||||||
|
int to = pop_lsb_index(&left);
|
||||||
|
int from = to - 7;
|
||||||
|
push_move(out, n, from, to, P, Q, MF_CAPTURE | MF_PROMO);
|
||||||
|
}
|
||||||
|
// right capture: +9, mask off file H
|
||||||
|
uint64_t right = ((b->pieces[P] & ~FILE_H) << 9) & opp & RANK_8;
|
||||||
|
while (right) {
|
||||||
|
int to = pop_lsb_index(&right);
|
||||||
|
int from = to - 9;
|
||||||
|
push_move(out, n, from, to, P, Q, MF_CAPTURE | MF_PROMO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_black_pawn_capture_promotions(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t opp = b->occ[WHITE];
|
||||||
|
// from black view, “left” is -7 (mask off file H before shifting)
|
||||||
|
uint64_t left = ((b->pieces[p] & ~FILE_H) >> 7) & opp & RANK_1;
|
||||||
|
while (left) {
|
||||||
|
int to = pop_lsb_index(&left);
|
||||||
|
int from = to + 7;
|
||||||
|
push_move(out, n, from, to, p, q, MF_CAPTURE | MF_PROMO);
|
||||||
|
}
|
||||||
|
// “right” is -9 (mask off file A)
|
||||||
|
uint64_t right = ((b->pieces[p] & ~FILE_A) >> 9) & opp & RANK_1;
|
||||||
|
while (right) {
|
||||||
|
int to = pop_lsb_index(&right);
|
||||||
|
int from = to + 9;
|
||||||
|
push_move(out, n, from, to, p, q, MF_CAPTURE | MF_PROMO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_white_pawn_captures(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t pawns = b->pieces[P];
|
||||||
|
const uint64_t opp = b->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, n, from, to, P, 0, MF_CAPTURE);
|
||||||
|
}
|
||||||
|
while (right_caps) {
|
||||||
|
int to = pop_lsb_index(&right_caps);
|
||||||
|
int from = to - 9;
|
||||||
|
push_move(out, n, from, to, P, 0, MF_CAPTURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// En passant (destination is ep_square)
|
||||||
|
if (b->ep_square >= 0) {
|
||||||
|
uint64_t ep = 1ULL << b->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 = b->ep_square;
|
||||||
|
// EP never promotes, so no promo piece; still a capture
|
||||||
|
push_move(out, n, from, to, P, 0, MF_CAPTURE | MF_ENPASSANT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_black_pawn_captures(const struct Board *b, struct Move *out, int *n) {
|
||||||
|
const uint64_t pawns = b->pieces[p];
|
||||||
|
const uint64_t opp = b->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, n, from, to, p, 0, MF_CAPTURE);
|
||||||
|
}
|
||||||
|
while (right_caps) {
|
||||||
|
int to = pop_lsb_index(&right_caps);
|
||||||
|
int from = to + 9;
|
||||||
|
push_move(out, n, from, to, p, 0, MF_CAPTURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// En passant
|
||||||
|
if (b->ep_square >= 0) {
|
||||||
|
uint64_t ep = 1ULL << b->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 = b->ep_square;
|
||||||
|
push_move(out, n, from, to, p, 0, MF_CAPTURE | MF_ENPASSANT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user