Add Pseudo Move Generator #12
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user