diff --git a/engine/src/bitboard.c b/engine/src/bitboard.c index 8959818..31c77b9 100644 --- a/engine/src/bitboard.c +++ b/engine/src/bitboard.c @@ -135,6 +135,214 @@ static void push_move(struct Move *out, int *count, int from, int to, uint8_t pi (*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); + } + } +} /**