diff --git a/src/move-do.c b/src/move-do.c index 60174db..9f414ca 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -52,6 +52,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) color_t us = pos->turn, them = OPPONENT(us); square_t from = move_from(move), to = move_to(move); piece_t piece = pos->board[from]; + piece_t captured = pos->board[to]; piece_type_t ptype = PIECE(piece); color_t pcolor = COLOR(piece); piece_t new_piece = piece; @@ -60,6 +61,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) ++pos->plycount; pos->en_passant = SQUARE_NONE; pos->turn = them; + pos->captured = captured; bug_on(pcolor != us); @@ -68,9 +70,9 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) new_piece = MAKE_PIECE(move_promoted(move), us); } - if (is_capture(move)) { + if (captured != EMPTY) { pos->clock_50 = 0; - pos->captured = pos->board[to]; /* save capture info */ + //pos->captured = pos->board[to]; /* save capture info */ bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them); pos_clr_sq(pos, to); /* clear square */ } else if (is_castle(move)) { /* handle rook move */ @@ -88,7 +90,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) } else if (ptype == PAWN) { /* pawn non capture or e.p. */ pos->clock_50 = 0; if (is_dpush(move)) /* if pawn double push, set e.p. */ - pos->en_passant = pawn_push_up(from, us); + pos->en_passant = from + sq_up(us); else if (is_enpassant(move)) { /* clear grabbed pawn */ square_t grabbed = pawn_push_up(to, them); pos_clr_sq(pos, grabbed); @@ -160,13 +162,13 @@ pos_t *move_undo(pos_t *pos, const move_t move)//, const state_t *state) if (is_promotion(move)) piece = MAKE_PIECE(PAWN, us); - pos_clr_sq(pos, to); /* always clear "to" and set "from" */ - pos_set_sq(pos, from, piece); + pos_clr_sq(pos, to); /* always clear "to" ... */ + pos_set_sq(pos, from, piece); /* ... and set "from" */ if (PIECE(piece) == KING) pos->king[us] = from; - if (is_capture(move)) { + if (pos->captured != EMPTY) { pos_set_sq(pos, to, pos->captured); /* restore captured piece */ } else if (is_castle(move)) { /* make reverse rook move */ square_t rookfrom, rookto; diff --git a/src/move-gen.c b/src/move-gen.c index 19baf43..1b520a5 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -40,25 +40,32 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) square_t from = move_from(move), to = move_to(move); square_t king = pos->king[us]; bitboard_t kingbb = pos->bb[us][KING]; + bitboard_t occ = pos_occ(pos); u64 pinned = mask(from) & pos->blockers; - /* (1) - King - * For castling, we already tested intermediate squares attacks - * in pseudo move generation, so we only care destination square here. - * Attention: We need to exclude king from occupation bitboard ! + /* (1) - Castling & King + * For castling, we need to check intermediate squares attacks only. + * Attention: To test if K is in check after moving, we need to exclude + * king from occupation bitboard (to catch king moving away from checker + * on same line) ! */ + if (is_castle(move)) { + square_t dir = to > from? 1: -1; + if (sq_attackers(pos, occ, from + dir, them)) + return false; + } if (from == king) { - bitboard_t occ = pos_occ(pos) ^ kingbb; - return !sq_attackers(pos, occ, to, them); + return !sq_attackers(pos, occ ^ kingbb, to, them); } /* (2) - King is in check - * Double-check is already handled, as only K moves were generated. + * Double-check is already handled in (1), as only K moves were generated + * by pseudo legal move generator. * Here, allowed dest squares are only on King-checker line, or on checker * square. * attacker. * Special cases: - * e.p., legal if the taken pawn is giving check + * e.p., legal if the grabbed pawn is giving check * pinned piece: always illegal */ if (pos->checkers) { @@ -68,8 +75,9 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) return false; square_t checker = ctz64(pos->checkers); if (is_enpassant(move)) { - return pawn_push_up(pos->en_passant, them) == checker; + return pos->en_passant + sq_up(them) == checker; } + return true; bitboard_t between = bb_between[king][checker] | pos->checkers; return mask(to) & between; } @@ -77,28 +85,25 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) /* (3) - pinned pieces * We verify here that pinned piece P stays on line King-P. */ - //if (mask(from) & pos->blockers & pos->bb[us][ALL_PIECES]) { - if (pinned) { - bitboard_t line = bb_line[from][king]; - return line & mask(to); /* to is not on pin line */ + if (mask(from) & pos->blockers) { + return bb_line[from][king] & mask(to); /* is to on pinner line ? */ } /* (4) - En-passant - * pinned pieces are already handled. - * One case is left: when the two "disappearing" pawns would discover - * a R/Q check. + * pinned pieces are handled in pinned section. + * One case not handled anywhere else: when the two "disappearing" pawns + * would discover a R/Q horizontal check. */ if (is_enpassant(move)) { bitboard_t rank5 = us == WHITE? RANK_5bb: RANK_4bb; if ((pos->bb[us][KING] & rank5)) { - bitboard_t exclude = mask(pos->en_passant + sq_up(them)) | mask(from); + bitboard_t exclude = mask(pos->en_passant - sq_up(us)) | mask(from); bitboard_t rooks = (pos->bb[them][ROOK] | pos->bb[them][QUEEN]) & rank5; - bitboard_t occ = pos_occ(pos) ^ exclude; while (rooks) { square_t rook = bb_next(&rooks); - if (hyperbola_rank_moves(occ, rook) & kingbb) + if (hyperbola_rank_moves(occ ^ exclude, rook) & kingbb) return false; } } @@ -152,6 +157,27 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de return dest; } +/** + * gen_pseudo_king() - generate king pseudo-legal moves. + * @pos: position + * @movelist: &movelist_t array to store pseudo-moves + * + */ +static inline __unused move_t *gen_pseudo_king(move_t *moves, square_t from, + bitboard_t mask) +{ + //color_t us = pos->turn; + //square_t from = pos->king[us]; + //bitboard_t not_my_pieces = ~pos->bb[us][ALL_PIECES]; + bitboard_t to_bb = bb_king_moves(mask, from); + square_t to; + while (moves) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); + } + return moves; +} + /** * pos_gen_check_pseudomoves() - generate position pseudo-legal moves when in check * @pos: position @@ -176,6 +202,48 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de * @Return: The total number of moves. */ +/** + * gen_pseudo_escape() - generate position pseudo-legal moves when in check + * @pos: square_t king position + * @mask: possible destinations to consider + * @moves: &move_t array to store pseudo-moves + * + * Generate all @pos pseudo moves for player-to-move, when in check. + * @movelist is filled with the moves. + * + * If king is doubles-checked, only King moves will be generated. + * Otherwise, only moves where destination in between King or checker, or checker + * square. + * + */ +/* + * static void __always_inline gen_pseudo_escape(pos_t *pos, movelist_t *movelist) + * { + * color_t us = pos->turn; + * color_t them = OPPONENT(us); + * square_t king = pos->king[us]; + * + * gen_pseudo_king(pos, movelist); + * + * } + */ + +/** + * move_make_promotions() - generate all promotions for given pawn and dest. + * @moves: &move_t array where to store moves + * @from: pawn position + * @to: promotion square + * + * Generate all (Q/R/B/N) promotion moves on @to for pawn @from. + * + * @Return: New @moves (incremented by 4). + */ +static inline move_t *move_make_promotions(move_t *moves, square_t from, square_t to) +{ + for (piece_type_t pt = QUEEN; pt >= KNIGHT; --pt) + *moves++ = move_make_promote(from, to, pt); + return moves; +} /** * pos_gen_pseudomoves() - generate position pseudo-legal moves @@ -206,60 +274,88 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist) color_t them = OPPONENT(us); bitboard_t my_pieces = pos->bb[us][ALL_PIECES]; - bitboard_t not_my_pieces = ~my_pieces; bitboard_t enemy_pieces = pos->bb[them][ALL_PIECES]; + bitboard_t dest_squares = ~my_pieces; bitboard_t occ = my_pieces | enemy_pieces; bitboard_t empty = ~occ; - bitboard_t movebits, from_pawns; - bitboard_t tmp1, tmp2; + bitboard_t from_bb, to_bb; + bitboard_t tmp_bb; move_t *moves = movelist->move; - int *nmoves = &movelist->nmoves; - int from, to; + //int *nmoves = &movelist->nmoves; + square_t from, to; + square_t king = pos->king[us]; - *nmoves = 0; + //*nmoves = 0; /* king - MUST BE FIRST (we stop if doubler check) */ - from = pos->king[us]; - movebits = bb_king_moves(not_my_pieces, from); - bit_for_each64(to, tmp1, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp1, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + to_bb = bb_king_moves(dest_squares, king); + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(king, to); } - if (popcount64(pos->checkers) > 1) /* double check, we stop here */ - return *nmoves; + if (bb_multiple(pos->checkers)) /* double check, we stop here */ + goto finish; + if (pos->checkers) { + /* one checker: we limit destination squares to line between + * checker and king + checker square (think: knight). + */ + square_t checker = ctz64(pos->checkers); + dest_squares &= bb_between[king][checker] | pos->checkers; + enemy_pieces &= dest_squares; + } else { + /* no checker: castling moves + * Attention ! Castling flags are assumed correct + */ + bitboard_t rel_rank1 = bb_rel_rank(RANK_1, us); + //square_t from_square[2] = { E1, E8 }; /* verify king is on E1/E8 */ + //bug_on(can_castle(pos->castle, us) && from != from_square[us]); + /* For castle, we check the opponent attacks on squares between from and to. + * To square attack check will be done in gen_is_legal. + */ + if (can_oo(pos->castle, us)) { + bitboard_t occmask = rel_rank1 & (FILE_Fbb | FILE_Gbb); + if (!(occ & occmask)) { + *moves++ = move_make_flags(king, king + 2, M_CASTLE_K); + } + } + if (can_ooo(pos->castle, us)) { + bitboard_t occmask = rel_rank1 & (FILE_Bbb | FILE_Cbb | FILE_Dbb); + if (!(occ & occmask)) { // && + *moves++ = move_make_flags(king, king - 2, M_CASTLE_Q); + } + } + } /* sliding pieces */ - bit_for_each64(from, tmp1, pos->bb[us][BISHOP] | pos->bb[us][QUEEN]) { - movebits = hyperbola_bishop_moves(occ, from) & not_my_pieces; - bit_for_each64(to, tmp2, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp2, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + from_bb = pos->bb[us][BISHOP] | pos->bb[us][QUEEN]; + while (from_bb) { + from = bb_next(&from_bb); + to_bb = hyperbola_bishop_moves(occ, from) & dest_squares; + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); } } - bit_for_each64(from, tmp1, pos->bb[us][ROOK] | pos->bb[us][QUEEN]) { - movebits = hyperbola_rook_moves(occ, from) & not_my_pieces; - bit_for_each64(to, tmp2, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp2, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + from_bb = pos->bb[us][ROOK] | pos->bb[us][QUEEN]; + while (from_bb) { + from = bb_next(&from_bb); + to_bb = hyperbola_rook_moves(occ, from) & dest_squares; + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); } } /* knight */ - bit_for_each64(from, tmp1, pos->bb[us][KNIGHT]) { - movebits = bb_knight_moves(not_my_pieces, from); - bit_for_each64(to, tmp2, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp2, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + from_bb = pos->bb[us][KNIGHT]; + while (from_bb) { + from = bb_next(&from_bb); + to_bb = bb_knight_moves(dest_squares, from); + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); } } @@ -267,91 +363,85 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist) bitboard_t rel_rank8 = bb_rel_rank(RANK_8, us); bitboard_t rel_rank3 = bb_rel_rank(RANK_3, us); - /* pawn: ranks 2-6 push 1 and 2 squares */ - movebits = pawn_shift_up(pos->bb[us][PAWN], us) & empty; - bit_for_each64(to, tmp1, movebits & ~rel_rank8) { - from = pawn_push_up(to, them); /* reverse push */ - moves[(*nmoves)++] = move_make(from, to); + /* pawn: push */ + int shift = sq_up(us); + tmp_bb = bb_shift(pos->bb[us][PAWN], shift) & empty; + + to_bb = tmp_bb & ~rel_rank8 & dest_squares; /* non promotion */ + while(to_bb) { + to = bb_next(&to_bb); + from = to - shift; + *moves++ = move_make(from, to); } - bit_for_each64(to, tmp1, movebits & rel_rank8) { /* promotions */ - from = pawn_push_up(to, them); /* reverse push */ - moves[(*nmoves)++] = move_make_promote(from, to, QUEEN); - moves[(*nmoves)++] = move_make_promote(from, to, ROOK); - moves[(*nmoves)++] = move_make_promote(from, to, BISHOP); - moves[(*nmoves)++] = move_make_promote(from, to, KNIGHT); + to_bb = tmp_bb & rel_rank8 & dest_squares; /* promotions */ + while(to_bb) { + to = bb_next(&to_bb); + from = to - shift; + moves = move_make_promotions(moves, from, to); } /* possible second push */ - movebits = pawn_shift_up(movebits & rel_rank3, us) & empty; - bit_for_each64(to, tmp1, movebits) { - from = pawn_push_up(pawn_push_up(to, them), them); - moves[(*nmoves)++] = move_make_flags(from, to, M_DPUSH); + to_bb = bb_shift(tmp_bb & rel_rank3, shift) & empty & dest_squares; + while(to_bb) { + to = bb_next(&to_bb); + from = to - shift - shift; + *moves++ = move_make_flags(from, to, M_DPUSH); } /* pawn: captures left */ - movebits = pawn_shift_upleft(pos->bb[us][PAWN], us) & enemy_pieces; - bit_for_each64(to, tmp1, movebits & ~rel_rank8) { - from = pawn_push_upleft(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_capture(from, to); + bitboard_t filter = ~bb_rel_file(FILE_A, us); + shift = sq_upleft(us); + tmp_bb = bb_shift(pos->bb[us][PAWN] & filter, shift) & enemy_pieces; + + to_bb = tmp_bb & ~rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + *moves++ = move_make(from, to); } - bit_for_each64(to, tmp1, movebits & rel_rank8) { - from = pawn_push_upleft(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN); - moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK); - moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP); - moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT); + to_bb = tmp_bb & rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + moves = move_make_promotions(moves, from, to); } /* pawn: captures right */ - movebits = pawn_shift_upright(pos->bb[us][PAWN], us) & enemy_pieces; - bit_for_each64(to, tmp1, movebits & ~rel_rank8) { - from = pawn_push_upright(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_capture(from, to); + filter = ~bb_rel_file(FILE_H, us); + shift = sq_upright(us); + tmp_bb = bb_shift(pos->bb[us][PAWN] & filter, shift) & enemy_pieces; + to_bb = tmp_bb & ~rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + *moves++ = move_make(from, to); } - bit_for_each64(to, tmp1, movebits & rel_rank8) { - from = pawn_push_upright(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN); - moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK); - moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP); - moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT); + to_bb = tmp_bb & rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + moves = move_make_promotions(moves, from, to); } - /* pawn: en-passant */ - if ((to = pos->en_passant) != SQUARE_NONE) { - movebits = mask(to); - from_pawns = pos->bb[us][PAWN] - & (pawn_shift_upleft(movebits, them) | pawn_shift_upright(movebits, them)); - bit_for_each64(from, tmp1, from_pawns) { - moves[(*nmoves)++] = move_make_enpassant(from, to); - } - } - - /* castle - Attention ! Castling flags are assumed correct + /* pawn: en-passant + * TODO: special case when in-check here ? */ - if (!pos->checkers) { - bitboard_t rel_rank1 = bb_rel_rank(RANK_1, us); - from = pos->king[us]; - square_t from_square[2] = { E1, E8 }; /* verify king is on E1/E8 */ - bug_on(can_castle(pos->castle, us) && from != from_square[us]); - /* For castle, we check the opponent attacks on squares between from and to. - * To square attack check will be done in gen_is_legal. - */ - if (can_oo(pos->castle, us)) { - bitboard_t occmask = rel_rank1 & (FILE_Fbb | FILE_Gbb); - if (!(occ & occmask) && - !sq_attackers(pos, occ, from+1, them)) { /* f1/f8 */ - moves[(*nmoves)++] = move_make_flags(from, from + 2, M_CASTLE_K); - } - } - if (can_ooo(pos->castle, us)) { - bitboard_t occmask = rel_rank1 & (FILE_Bbb | FILE_Cbb | FILE_Dbb); - if (!(occ & occmask) && - !sq_attackers(pos, occ, from-1, them)) { /* d1/d8 */ - moves[(*nmoves)++] = move_make_flags(from, from - 2, M_CASTLE_Q); - } - } + if ((to = pos->en_passant) != SQUARE_NONE) { + to_bb = mask(to); + /* if e.p not on file H, we may add an e.p move to "up-left" */ + filter = ~bb_rel_file(FILE_A, us); + shift = sq_upleft(us); + if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) + *moves++ = move_make_enpassant(to - shift, to); + + filter = ~bb_rel_file(FILE_H, us); + shift = sq_upright(us); + if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) + *moves++ = move_make_enpassant(to - shift, to); } + /* TODO: add function per piece, and type, for easier debug */ - return *nmoves; +finish: + return movelist->nmoves = moves - movelist->move; }