From 0855d93de8cf7c68af616a2d5f384a7632470895 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 3 Nov 2009 12:29:07 +0100 Subject: [PATCH] Rewrite generate_pawn_moves() and simplify evasions Big cleanup and semplification of pawns evasions that now are pseudo-legal as the remaining moves. This allow us to remove a lot of tricky code. Verified against perft: no functional change. Signed-off-by: Marco Costalba --- src/movegen.cpp | 285 ++++++++++++++++++----------------------------- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/position.cpp | 8 -- src/position.h | 1 - src/san.cpp | 8 +- 6 files changed, 118 insertions(+), 188 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 0af95e2d..18fb0943 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -57,8 +57,7 @@ namespace { MoveStack* generate_castle_moves(const Position&, MoveStack*); template - MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard = EmptyBoardBB, - Square = SQ_NONE, Bitboard = EmptyBoardBB); + MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square); // Template generate_piece_moves (captures and non-captures) with specializations and overloads template @@ -68,19 +67,19 @@ namespace { MoveStack* generate_piece_moves(const Position&, MoveStack*, Color, Bitboard); template - inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us) { + inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) { assert(Piece == PAWN); - assert(Type == CAPTURE || Type == NON_CAPTURE); + assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION); - return (us == WHITE ? generate_pawn_moves(p, m) - : generate_pawn_moves(p, m)); + return (us == WHITE ? generate_pawn_moves(p, m, t, SQ_NONE) + : generate_pawn_moves(p, m, t, SQ_NONE)); } // Templates for non-capture checks generation template - MoveStack* generate_discovered_checks(const Position& pos, Square from, MoveStack* mlist); + MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from); template MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square); @@ -91,18 +90,6 @@ namespace { return (us == WHITE ? generate_pawn_moves(p, m, dc, ksq) : generate_pawn_moves(p, m, dc, ksq)); } - - // Template generate_piece_evasions with specializations - template - MoveStack* generate_piece_evasions(const Position&, MoveStack*, Color, Bitboard, Bitboard); - - template<> - inline MoveStack* generate_piece_evasions(const Position& p, MoveStack* m, - Color us, Bitboard t, Bitboard pnd) { - - return (us == WHITE ? generate_pawn_moves(p, m, pnd, SQ_NONE, t) - : generate_pawn_moves(p, m, pnd, SQ_NONE, t)); - } } @@ -126,7 +113,7 @@ MoveStack* generate_captures(const Position& pos, MoveStack* mlist) { mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); - mlist = generate_piece_moves(pos, mlist, us); + mlist = generate_piece_moves(pos, mlist, us, target); return generate_piece_moves(pos, mlist, us, target); } @@ -142,7 +129,7 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) { Color us = pos.side_to_move(); Bitboard target = pos.empty_squares(); - mlist = generate_piece_moves(pos, mlist, us); + mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); @@ -153,7 +140,7 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) { } -/// generate_non_capture_checks() generates all pseudo-legal non-captures and +/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc) { @@ -174,10 +161,10 @@ MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bi switch (pos.type_of_piece_on(from)) { case PAWN: /* Will be generated togheter with pawns direct checks */ break; - case KNIGHT: mlist = generate_discovered_checks(pos, from, mlist); break; - case BISHOP: mlist = generate_discovered_checks(pos, from, mlist); break; - case ROOK: mlist = generate_discovered_checks(pos, from, mlist); break; - case KING: mlist = generate_discovered_checks(pos, from, mlist); break; + case KNIGHT: mlist = generate_discovered_checks(pos, mlist, from); break; + case BISHOP: mlist = generate_discovered_checks(pos, mlist, from); break; + case ROOK: mlist = generate_discovered_checks(pos, mlist, from); break; + case KING: mlist = generate_discovered_checks(pos, mlist, from); break; default: assert(false); break; } } @@ -194,16 +181,15 @@ MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bi /// generate_evasions() generates all pseudo-legal check evasions when /// the side to move is in check. Returns a pointer to the end of the move list. -MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { +MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); assert(pos.is_check()); Bitboard b; - Square from, to, checksq; + Square from, checksq; int checkersCnt = 0; Color us = pos.side_to_move(); - Color them = opposite_color(us); Square ksq = pos.king_square(us); Bitboard checkers = pos.checkers(); Bitboard sliderAttacks = EmptyBoardBB; @@ -220,7 +206,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pin checkersCnt++; checksq = pop_1st_bit(&b); - assert(pos.color_of_piece_on(checksq) == them); + assert(pos.color_of_piece_on(checksq) == opposite_color(us)); switch (pos.type_of_piece_on(checksq)) { @@ -247,62 +233,15 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pin if (checkersCnt > 1) return mlist; - Bitboard target = squares_between(checksq, ksq); - - // Pawn captures - b = pos.attacks_from(checksq, them) & pos.pieces(PAWN, us) & ~pinned; - while (b) - { - from = pop_1st_bit(&b); - if (relative_rank(us, checksq) == RANK_8) - { - (*mlist++).move = make_promotion_move(from, checksq, QUEEN); - (*mlist++).move = make_promotion_move(from, checksq, ROOK); - (*mlist++).move = make_promotion_move(from, checksq, BISHOP); - (*mlist++).move = make_promotion_move(from, checksq, KNIGHT); - } else - (*mlist++).move = make_move(from, checksq); - } - - // Pawn blocking evasions (possible only if the checking piece is a slider) - if (sliderAttacks) - mlist = generate_piece_evasions(pos, mlist, us, target, pinned); - - // Add the checking piece to the target squares - target |= checkers; + // Find squares where a blocking evasion or a capture of the + // checker piece is possible. + Bitboard target = squares_between(checksq, ksq) | checkers; - // Captures and blocking evasions for the other pieces + mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); - mlist = generate_piece_moves(pos, mlist, us, target); - - // Finally, the special case of en passant captures. An en passant - // capture can only be a check evasion if the check is not a discovered - // check. If pos.ep_square() is set, the last move made must have been - // a double pawn push. If, furthermore, the checking piece is a pawn, - // an en passant check evasion may be possible. - if (pos.ep_square() != SQ_NONE && (checkers & pos.pieces(PAWN, them))) - { - to = pos.ep_square(); - b = pos.attacks_from(to, them) & pos.pieces(PAWN, us); - - // The checking pawn cannot be a discovered (bishop) check candidate - // otherwise we were in check also before last double push move. - assert(!bit_is_set(pos.discovered_check_candidates(them), checksq)); - assert(count_1s(b) == 1 || count_1s(b) == 2); - - b &= ~pinned; - while (b) - { - from = pop_1st_bit(&b); - // Move is always legal because checking pawn is not a discovered - // check candidate and our capturing pawn has been already tested - // against pinned pieces. - (*mlist++).move = make_ep_move(from, to); - } - } - return mlist; + return generate_piece_moves(pos, mlist, us, target); } @@ -319,7 +258,7 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega // Generate pseudo-legal moves if (pos.is_check()) - last = generate_evasions(pos, mlist, pinned); + last = generate_evasions(pos, mlist); else { last = generate_captures(pos, mlist); last = generate_noncaptures(pos, last); @@ -348,7 +287,7 @@ bool move_is_legal(const Position& pos, const Move m) { MoveStack* last = generate_moves(pos, mlist, true); for (MoveStack* cur = mlist; cur != last; cur++) if (cur->move == m) - return pos.pl_move_is_legal(m); + return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move())); return false; } @@ -481,24 +420,6 @@ namespace { return mlist; } - template - MoveStack* generate_piece_evasions(const Position& pos, MoveStack* mlist, - Color us, Bitboard target, Bitboard pinned) { - Square from; - Bitboard b; - const Square* ptr = pos.piece_list_begin(us, Piece); - - while ((from = *ptr++) != SQ_NONE) - { - if (pinned && bit_is_set(pinned, from)) - continue; - - b = pos.attacks_from(from) & target; - SERIALIZE_MOVES(b); - } - return mlist; - } - template inline Bitboard move_pawns(Bitboard p) { @@ -512,8 +433,8 @@ namespace { return p; } - template - MoveStack* generate_pawn_diagonal_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces, bool promotion) { + template + inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces, bool possiblePromotion) { // Calculate our parametrized parameters at compile time const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); @@ -527,131 +448,147 @@ namespace { // Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black) Bitboard b1 = move_pawns(pawns) & ~TFileABB & enemyPieces; - // Capturing promotions - if (promotion) + // Capturing promotions and under-promotions + if (possiblePromotion) { Bitboard b2 = b1 & TRank8BB; b1 &= ~TRank8BB; while (b2) { to = pop_1st_bit(&b2); - (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN); + + if (Type == CAPTURE || Type == EVASION) + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN); + + if (Type == NON_CAPTURE || Type == EVASION) + { + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK); + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP); + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT); + } + + // This is the only possible under promotion that can give a check + // not already included in the queen-promotion. It is not sure that + // the promoted knight will give check, but it doesn't worth to verify. + if (Type == CHECK) + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT); } } - // Capturing non-promotions - SERIALIZE_MOVES_D(b1, -TTDELTA_NE); + // Serialize standard captures + if (Type == CAPTURE || Type == EVASION) + SERIALIZE_MOVES_D(b1, -TTDELTA_NE); + return mlist; } template - MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard dcp, - Square ksq, Bitboard blockSquares) { + MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { // Calculate our parametrized parameters at compile time const Color Them = (Us == WHITE ? BLACK : WHITE); const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE); - const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW); const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); - Bitboard b1, b2, dcPawns1, dcPawns2; Square to; - Bitboard pawns = (Type == EVASION ? pos.pieces(PAWN, Us) & ~dcp : pos.pieces(PAWN, Us)); + Bitboard b1, b2, enemyPieces, emptySquares; + Bitboard pawns = pos.pieces(PAWN, Us); bool possiblePromotion = pawns & TRank7BB; - if (Type == CAPTURE) + // Standard captures and capturing promotions and underpromotions + if (Type == CAPTURE || Type == EVASION || possiblePromotion) { - // Standard captures and capturing promotions in both directions - Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); - mlist = generate_pawn_diagonal_captures(mlist, pawns, enemyPieces, possiblePromotion); - mlist = generate_pawn_diagonal_captures(mlist, pawns, enemyPieces, possiblePromotion); + enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us))); + + if (Type == EVASION) + enemyPieces &= target; // Capture only the checker piece + + mlist = generate_pawn_captures(mlist, pawns, enemyPieces, possiblePromotion); + mlist = generate_pawn_captures(mlist, pawns, enemyPieces, possiblePromotion); } + // Non-capturing promotions and underpromotions if (possiblePromotion) { - // When generating checks consider under-promotion moves (both captures - // and non captures) only if can give a discovery check. Note that dcp - // is dc bitboard or pinned bitboard when Type == EVASION. - Bitboard pp = (Type == CHECK ? pawns & dcp : pawns); + b1 = move_pawns(pawns) & TRank8BB & pos.empty_squares(); - if (Type != EVASION && Type != CAPTURE) - { - Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); - - // Underpromotion captures in the a1-h8 (a8-h1 for black) direction - b1 = move_pawns(pp) & ~FileABB & enemyPieces & TRank8BB; - while (b1) - { - to = pop_1st_bit(&b1); - (*mlist++).move = make_promotion_move(to - TDELTA_NE, to, ROOK); - (*mlist++).move = make_promotion_move(to - TDELTA_NE, to, BISHOP); - (*mlist++).move = make_promotion_move(to - TDELTA_NE, to, KNIGHT); - } - - // Underpromotion captures in the h1-a8 (h8-a1 for black) direction - b1 = move_pawns(pp) & ~FileHBB & enemyPieces & TRank8BB; - while (b1) - { - to = pop_1st_bit(&b1); - (*mlist++).move = make_promotion_move(to - TDELTA_NW, to, ROOK); - (*mlist++).move = make_promotion_move(to - TDELTA_NW, to, BISHOP); - (*mlist++).move = make_promotion_move(to - TDELTA_NW, to, KNIGHT); - } - } - - // Underpromotion pawn pushes. Also queen promotions for evasions and captures. - b1 = move_pawns(pp) & TRank8BB; - b1 &= (Type == EVASION ? blockSquares : pos.empty_squares()); + if (Type == EVASION) + b1 &= target; // Only blocking promotion pushes while (b1) { to = pop_1st_bit(&b1); - if (Type == EVASION || Type == CAPTURE) + + if (Type == CAPTURE || Type == EVASION) (*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN); - if (Type != CAPTURE) + if (Type == NON_CAPTURE || Type == EVASION) { (*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK); (*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP); (*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT); } + + // This is the only possible under promotion that can give a check + // not already included in the queen-promotion. + if (Type == CHECK && bit_is_set(pos.attacks_from(to), pos.king_square(Them))) + (*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT); } } + // Standard pawn pushes and double pushes if (Type != CAPTURE) { - Bitboard emptySquares = pos.empty_squares(); - dcPawns1 = dcPawns2 = EmptyBoardBB; - if (Type == CHECK && (pawns & dcp)) + emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares()); + + // Single and double pawn pushes + b1 = move_pawns(pawns) & emptySquares & ~TRank8BB; + b2 = move_pawns(b1 & TRank3BB) & emptySquares; + + // Filter out unwanted pushes according to the move type + if (Type == EVASION) { - // Pawn moves which gives discovered check. This is possible only if the - // pawn is not on the same file as the enemy king, because we don't - // generate captures. - dcPawns1 = move_pawns(pawns & dcp & ~file_bb(ksq)) & emptySquares & ~TRank8BB; - dcPawns2 = move_pawns(dcPawns1 & TRank3BB) & emptySquares; + b1 &= target; + b2 &= target; } + else if (Type == CHECK) + { + // Pawn moves which give direct cheks + b1 &= pos.attacks_from(ksq, Them); + b2 &= pos.attacks_from(ksq, Them); + + // Pawn moves which gives discovered check. This is possible only if + // the pawn is not on the same file as the enemy king, because we + // don't generate captures. + if (pawns & target) // For CHECK type target is dc bitboard + { + Bitboard dc1 = move_pawns(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB; + Bitboard dc2 = move_pawns(dc1 & TRank3BB) & emptySquares; - // Single pawn pushes - b1 = move_pawns(pawns) & emptySquares & ~TRank8BB; - b2 = (Type == CHECK ? (b1 & pos.attacks_from(ksq, Them)) | dcPawns1 : - (Type == EVASION ? b1 & blockSquares : b1)); - SERIALIZE_MOVES_D(b2, -TDELTA_N); - - // Double pawn pushes - b1 = move_pawns(b1 & TRank3BB) & emptySquares; - b2 = (Type == CHECK ? (b1 & pos.attacks_from(ksq, Them)) | dcPawns2 : - (Type == EVASION ? b1 & blockSquares : b1)); + b1 |= dc1; + b2 |= dc2; + } + } + SERIALIZE_MOVES_D(b1, -TDELTA_N); SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N); } - else if (pos.ep_square() != SQ_NONE) // En passant captures + + // En passant captures + if ((Type == CAPTURE || Type == EVASION) && pos.ep_square() != SQ_NONE) { assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6); assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3); + // An en passant capture can be an evasion only if the checking piece + // is the double pushed pawn and so is in the target. Otherwise this + // is a discovery check and we are forced to do otherwise. + if (Type == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N)) + return mlist; + b1 = pawns & pos.attacks_from(pos.ep_square(), Them); + assert(b1 != EmptyBoardBB); while (b1) @@ -664,7 +601,7 @@ namespace { } template - MoveStack* generate_discovered_checks(const Position& pos, Square from, MoveStack* mlist) { + MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) { assert(Piece != QUEEN); diff --git a/src/movegen.h b/src/movegen.h index fdc90139..7d2efec3 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -35,7 +35,7 @@ extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist); extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist); extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc); -extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned); +extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist); extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false); extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned); extern bool move_is_legal(const Position& pos, const Move m); diff --git a/src/movepick.cpp b/src/movepick.cpp index a2ea3a73..a562662e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -144,7 +144,7 @@ void MovePicker::go_next_phase() { case PH_EVASIONS: assert(pos.is_check()); - lastMove = generate_evasions(pos, moves, pinned); + lastMove = generate_evasions(pos, moves); score_evasions(); return; diff --git a/src/position.cpp b/src/position.cpp index be4e234d..306cebfc 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -458,13 +458,6 @@ void Position::find_checkers() { /// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal -bool Position::pl_move_is_legal(Move m) const { - - // If we're in check, all pseudo-legal moves are legal, because our - // check evasion generator only generates true legal moves. - return is_check() || pl_move_is_legal(m, pinned_pieces(side_to_move())); -} - bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { assert(is_ok()); @@ -1337,7 +1330,6 @@ int Position::see(Square from, Square to) const { Bitboard attackers, stmAttackers, b; - assert(!shortcut || from != SQ_NONE); assert(square_is_ok(from) || from == SQ_NONE); assert(square_is_ok(to)); diff --git a/src/position.h b/src/position.h index f2f517e2..2568563c 100644 --- a/src/position.h +++ b/src/position.h @@ -202,7 +202,6 @@ public: template Bitboard attacks_from(Square s, Color c) const; // Properties of moves - bool pl_move_is_legal(Move m) const; bool pl_move_is_legal(Move m, Bitboard pinned) const; bool move_is_check(Move m) const; bool move_is_check(Move m, Bitboard dcCandidates) const; diff --git a/src/san.cpp b/src/san.cpp index 0e9b5310..f1558878 100644 --- a/src/san.cpp +++ b/src/san.cpp @@ -142,13 +142,14 @@ Move move_from_san(const Position& pos, const string& movestr) { assert(pos.is_ok()); MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H); + Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); // Castling moves if (movestr == "O-O-O" || movestr == "O-O-O+") { Move m; while ((m = mp.get_next_move()) != MOVE_NONE) - if (move_is_long_castle(m) && pos.pl_move_is_legal(m)) + if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned)) return m; return MOVE_NONE; @@ -157,7 +158,7 @@ Move move_from_san(const Position& pos, const string& movestr) { { Move m; while ((m = mp.get_next_move()) != MOVE_NONE) - if (move_is_short_castle(m) && pos.pl_move_is_legal(m)) + if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned)) return m; return MOVE_NONE; @@ -367,11 +368,12 @@ namespace { return AMBIGUITY_NONE; MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H); + Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Move mv, moveList[8]; int n = 0; while ((mv = mp.get_next_move()) != MOVE_NONE) - if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv)) + if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv, pinned)) moveList[n++] = mv; if (n == 1) -- 2.39.2