internal static void init() { for (int i = 0; i < Constants.BROKER_SLOTS; i++) { _pool[i] = new CheckInfo[0]; } }
internal static CheckInfo GetObject() { int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK; if (_cnt[slotID] == _pool[slotID].Length) { int poolLength = _pool[slotID].Length; CheckInfo[] temp = new CheckInfo[poolLength + Constants.BrokerCapacity]; Array.Copy(_pool[slotID], temp, poolLength); for (int i = 0; i < Constants.BrokerCapacity; i++) { temp[poolLength + i] = new CheckInfo(); } _pool[slotID] = temp; } return _pool[slotID][_cnt[slotID]++]; }
private static void generate_all( GenType type, Position pos, MoveStack[] mlist, ref int mpos, int us, ulong target, CheckInfo ci) { var Checks = type == GenType.QUIET_CHECKS; generate_pawn_moves(us, type, pos, mlist, ref mpos, target, ci); generate_moves(PieceTypeC.KNIGHT, Checks, pos, mlist, ref mpos, us, target, ci); generate_moves(PieceTypeC.BISHOP, Checks, pos, mlist, ref mpos, us, target, ci); generate_moves(PieceTypeC.ROOK, Checks, pos, mlist, ref mpos, us, target, ci); generate_moves(PieceTypeC.QUEEN, Checks, pos, mlist, ref mpos, us, target, ci); if (!Checks && type != GenType.EVASIONS) { Square from = pos.king_square(us); Bitboard b = Position.attacks_from_KING(from) & target; // SERIALIZE(b); while (b != 0) { #if X64 Bitboard bb = b; b &= (b - 1); mlist[mpos++].move = ((Utils.BSFTable[((bb & (0xffffffffffffffff - bb + 1)) * DeBruijn_64) >> 58]) | (from << 6)); #else mlist[mpos++].move = Utils.make_move(from, Utils.pop_lsb(ref b)); #endif } } if (type != GenType.CAPTURES && type != GenType.EVASIONS && pos.can_castle_C(us) != 0) { generate_castle(CastlingSideC.KING_SIDE, Checks, pos, mlist, ref mpos, us); generate_castle(CastlingSideC.QUEEN_SIDE, Checks, pos, mlist, ref mpos, us); } }
private static void generate_moves( int Pt, bool Checks, Position pos, MoveStack[] mlist, ref int mpos, int us, ulong target, CheckInfo ci) { Debug.Assert(Pt != PieceTypeC.KING && Pt != PieceTypeC.PAWN); var pl = pos.pieceList[us][Pt]; var plPos = 0; for (var from = pl[plPos]; from != SquareC.SQ_NONE; from = pl[++plPos]) { if (Checks) { if ((Pt == PieceTypeC.BISHOP || Pt == PieceTypeC.ROOK || Pt == PieceTypeC.QUEEN) && ((Utils.PseudoAttacks[Pt][from] & (ulong)target & ci.checkSq[Pt]) == 0)) { continue; } if ((ci.dcCandidates != 0) && (Utils.bit_is_set(ci.dcCandidates, from) != 0)) { continue; } } var b = pos.attacks_from_PTS(Pt, from) & (ulong)target; if (Checks) { b &= ci.checkSq[Pt]; } // SERIALIZE(b); while (b != 0) { mlist[mpos++].move = Utils.make_move(from, Utils.pop_lsb(ref b)); } } }
private static void generate_promotions( GenType Type, int Delta, MoveStack[] ms, ref int mpos, ulong pawnsOn7, ulong target, CheckInfo ci) { var b = move_pawns(Delta, pawnsOn7) & target; while (b != 0) { var to = Utils.pop_lsb(ref b); if (Type == GenType.CAPTURES || Type == GenType.EVASIONS || Type == GenType.NON_EVASIONS) { ms[mpos++].move = Utils.make(to - Delta, to, MoveTypeC.PROMOTION, PieceTypeC.QUEEN); } if (Type == GenType.QUIETS || Type == GenType.EVASIONS || Type == GenType.NON_EVASIONS) { ms[mpos++].move = Utils.make(to - Delta, to, MoveTypeC.PROMOTION, PieceTypeC.ROOK); ms[mpos++].move = Utils.make(to - Delta, to, MoveTypeC.PROMOTION, PieceTypeC.BISHOP); ms[mpos++].move = Utils.make(to - Delta, to, MoveTypeC.PROMOTION, PieceTypeC.KNIGHT); } // Knight-promotion is the only one that can give a direct check not // already included in the queen-promotion. if (Type == GenType.QUIET_CHECKS && (Utils.bit_is_set(Utils.StepAttacksBB[PieceC.W_KNIGHT][to], ci.ksq) != 0)) { ms[mpos++].move = Utils.make(to - Delta, to, MoveTypeC.PROMOTION, PieceTypeC.KNIGHT); } } }
private static void generate_pawn_moves( int Us, GenType Type, Position pos, MoveStack[] ms, ref int mpos, ulong target, CheckInfo ci) { // Compute our parametrized parameters at compile time, named according to // the point of view of white side. var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); var TRank8BB = (Us == ColorC.WHITE ? Constants.Rank8BB : Constants.Rank1BB); var TRank7BB = (Us == ColorC.WHITE ? Constants.Rank7BB : Constants.Rank2BB); var TRank3BB = (Us == ColorC.WHITE ? Constants.Rank3BB : Constants.Rank6BB); var UP = (Us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); var RIGHT = (Us == ColorC.WHITE ? SquareC.DELTA_NE : SquareC.DELTA_SW); var LEFT = (Us == ColorC.WHITE ? SquareC.DELTA_NW : SquareC.DELTA_SE); ulong b1, b2, dc1, dc2, emptySquares = 0; int to; var pawnsOn7 = (pos.byTypeBB[PieceTypeC.PAWN] & pos.byColorBB[Us]) & TRank7BB; var pawnsNotOn7 = (pos.byTypeBB[PieceTypeC.PAWN] & pos.byColorBB[Us]) & ~TRank7BB; var enemies = (Type == GenType.EVASIONS ? pos.byColorBB[Them] & target : Type == GenType.CAPTURES ? target : pos.byColorBB[Them]); // Single and double pawn pushes, no promotions if (Type != GenType.CAPTURES) { emptySquares = (Type == GenType.QUIETS ? target : ~pos.occupied_squares); b1 = move_pawns(UP, pawnsNotOn7) & emptySquares; b2 = move_pawns(UP, b1 & TRank3BB) & emptySquares; if (Type == GenType.EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; } if (Type == GenType.QUIET_CHECKS) { b1 &= Utils.StepAttacksBB[((Them << 3) | PieceTypeC.PAWN)][ci.ksq]; b2 &= Utils.StepAttacksBB[((Them << 3) | PieceTypeC.PAWN)][ci.ksq]; // Add pawn pushes which give 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. Note that a possible discovery check // promotion has been already generated among captures. if ((pawnsNotOn7 & ci.dcCandidates) != 0) // Target is dc bitboard { dc1 = move_pawns(UP, pawnsNotOn7 & ci.dcCandidates) & emptySquares & ~(Utils.FileBB[ci.ksq & 7]); dc2 = move_pawns(UP, dc1 & TRank3BB) & emptySquares; b1 |= dc1; b2 |= dc2; } } while (b1 != 0) { to = Utils.pop_lsb(ref b1); ms[mpos++].move = (to | ((to - UP) << 6)); } while (b2 != 0) { to = Utils.pop_lsb(ref b2); ms[mpos++].move = (to | ((to - UP - UP) << 6)); } } // Promotions and underpromotions if ((pawnsOn7 != 0) && (Type != GenType.EVASIONS || ((target & TRank8BB) != 0))) { if (Type == GenType.CAPTURES) { emptySquares = ~pos.occupied_squares; } if (Type == GenType.EVASIONS) { emptySquares &= target; } generate_promotions(Type, RIGHT, ms, ref mpos, pawnsOn7, enemies, ci); generate_promotions(Type, LEFT, ms, ref mpos, pawnsOn7, enemies, ci); generate_promotions(Type, UP, ms, ref mpos, pawnsOn7, emptySquares, ci); } // Standard and en-passant captures if (Type == GenType.CAPTURES || Type == GenType.EVASIONS || Type == GenType.NON_EVASIONS) { b1 = move_pawns(RIGHT, pawnsNotOn7) & enemies; b2 = move_pawns(LEFT, pawnsNotOn7) & enemies; while (b1 != 0) { to = Utils.pop_lsb(ref b1); ms[mpos++].move = (to | ((to - RIGHT) << 6)); } while (b2 != 0) { to = Utils.pop_lsb(ref b2); ms[mpos++].move = (to | ((to - LEFT) << 6)); } if (pos.st.epSquare != SquareC.SQ_NONE) { Debug.Assert(Utils.rank_of(pos.st.epSquare) == Utils.relative_rank_CR(Us, RankC.RANK_6)); // 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 == GenType.EVASIONS && (((target & Utils.SquareBB[pos.st.epSquare - UP]) == 0))) { return; } b1 = pawnsNotOn7 & Utils.StepAttacksBB[((Them << 3) | PieceTypeC.PAWN)][pos.st.epSquare]; Debug.Assert(b1 != 0); while (b1 != 0) { ms[mpos++].move = Utils.make(Utils.pop_lsb(ref b1), pos.st.epSquare, MoveTypeC.ENPASSANT); } } } }
private static void generate_direct_checks(PieceType Pt, Position pos, MoveStack[] ms, ref int mpos, Color us, CheckInfo ci) { Debug.Assert(Pt != PieceTypeC.KING && Pt != PieceTypeC.PAWN); Bitboard b, target; Square[] pl = pos.pieceList[us][Pt]; int plPos = 0; Square from = pl[plPos]; if ((from = pl[plPos]) != SquareC.SQ_NONE) { target = ci.checkSq[Pt] & ~pos.occupied_squares; // Non capture checks only do { if ((Pt == PieceTypeC.BISHOP || Pt == PieceTypeC.ROOK || Pt == PieceTypeC.QUEEN) && ((Utils.PseudoAttacks[Pt][from] & target) == 0)) continue; if ((ci.dcCandidates != 0) && (Utils.bit_is_set(ci.dcCandidates, from) != 0)) continue; b = pos.attacks_from_PTS(Pt, from) & target; while (b != 0) { ms[mpos++].move = Utils.make_move(from, Utils.pop_1st_bit(ref b)); } } while ((from = pl[++plPos]) != SquareC.SQ_NONE); } }
internal void do_move(Move m, StateInfo newSt, CheckInfo ci, bool moveIsCheck) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(newSt != st); nodes++; Key k = st.key; newSt.pawnKey = st.pawnKey; newSt.materialKey = st.materialKey; newSt.npMaterialWHITE = st.npMaterialWHITE; newSt.npMaterialBLACK = st.npMaterialBLACK; newSt.castleRights = st.castleRights; newSt.rule50 = st.rule50; newSt.pliesFromNull = st.pliesFromNull; newSt.psqScore = st.psqScore; newSt.epSquare = st.epSquare; newSt.previous = st; st = newSt; // Update side to move k ^= zobSideToMove; // Increment the 50 moves rule draw counter. Resetting it to zero in the // case of a capture or a pawn move is taken care of later. st.rule50++; st.pliesFromNull++; if ((m & (3 << 14)) == (3 << 14)) { st.key = k; do_castle_move(true, m); return; } Color us = sideToMove; Color them = us ^ 1; Square from = ((m >> 6) & 0x3F); Square to = (m & 0x3F); Piece piece = board[from]; PieceType pt = (piece & 7); PieceType capture = ((m & (3 << 14)) == (2 << 14)) ? PieceTypeC.PAWN : (board[to] & 7); Debug.Assert(Utils.color_of(piece) == us); Debug.Assert(Utils.color_of(piece_on(to)) != us); Debug.Assert(capture != PieceTypeC.KING); if (capture != 0) { Square capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (capture == PieceTypeC.PAWN) { if ((m & (3 << 14)) == (2 << 14)) { capsq += (them == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); Debug.Assert(pt == PieceTypeC.PAWN); Debug.Assert(to == st.epSquare); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_6); Debug.Assert(piece_on(to) == PieceC.NO_PIECE); Debug.Assert(piece_on(capsq) == Utils.make_piece(them, PieceTypeC.PAWN)); board[capsq] = PieceC.NO_PIECE; } st.pawnKey ^= zobrist[them][PieceTypeC.PAWN][capsq]; } else { if (them == 0) { st.npMaterialWHITE -= PieceValueMidgame[capture]; } else { st.npMaterialBLACK -= PieceValueMidgame[capture]; } } // Remove the captured piece Bitboard capPieceMask = Utils.SquareBB[capsq]; occupied_squares ^= capPieceMask; byTypeBB[capture] ^= capPieceMask; byColorBB[them] ^= capPieceMask; // Update piece list, move the last piece at index[capsq] position and // shrink the list. // // WARNING: This is a not revresible operation. When we will reinsert the // captured piece in undo_move() we will put it at the end of the list and // not in its original place, it means index[] and pieceList[] are not // guaranteed to be invariant to a do_move() + undo_move() sequence. int[] plThemCapture = pieceList[them][capture]; int pcThemCapture = --pieceCount[them][capture]; Square lastSquare = plThemCapture[pcThemCapture]; index[lastSquare] = index[capsq]; plThemCapture[index[lastSquare]] = lastSquare; plThemCapture[pcThemCapture] = SquareC.SQ_NONE; // Update hash keys k ^= zobrist[them][capture][capsq]; st.materialKey ^= zobrist[them][capture][pcThemCapture]; // Update incremental scores st.psqScore -= pieceSquareTable[((them << 3) | capture)][capsq]; // Reset rule 50 counter st.rule50 = 0; } // Update hash key k ^= zobrist[us][pt][from] ^ zobrist[us][pt][to]; // Reset en passant square if (st.epSquare != SquareC.SQ_NONE) { k ^= zobEp[st.epSquare & 7]; st.epSquare = SquareC.SQ_NONE; } // Update castle rights if needed if ((st.castleRights != 0) && ((castleRightsMask[from] | castleRightsMask[to]) != 0)) { int cr = castleRightsMask[from] | castleRightsMask[to]; k ^= zobCastle[st.castleRights & cr]; st.castleRights &= ~cr; } // Move the piece Bitboard from_to_bb = Utils.SquareBB[from] ^ Utils.SquareBB[to]; occupied_squares ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[us] ^= from_to_bb; board[to] = board[from]; board[from] = PieceC.NO_PIECE; // Update piece lists, index[from] is not updated and becomes stale. This // works as long as index[] is accessed just by known occupied squares. index[to] = index[from]; pieceList[us][pt][index[to]] = to; // If the moving piece is a pawn do some special extra work if (pt == PieceTypeC.PAWN) { // Set en-passant square, only if moved pawn can be captured if ((to ^ from) == 16 && ((((Utils.StepAttacksBB[((us << 3) | PieceTypeC.PAWN)][from + (us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)]) & (byTypeBB[PieceTypeC.PAWN] & byColorBB[them]))) != 0)) { st.epSquare = ((from + to) / 2); k ^= zobEp[st.epSquare & 7]; } if ((m & (3 << 14)) == (1 << 14)) { PieceType promotion = (((m >> 12) & 3) + 2); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_8); Debug.Assert(promotion >= PieceTypeC.KNIGHT && promotion <= PieceTypeC.QUEEN); // Replace the pawn with the promoted piece byTypeBB[PieceTypeC.PAWN] ^= Utils.SquareBB[to]; byTypeBB[promotion] |= Utils.SquareBB[to]; board[to] = ((us << 3) | promotion); // Update piece lists, move the last pawn at index[to] position // and shrink the list. Add a new promotion piece to the list. int[] plUsPawn = pieceList[us][PieceTypeC.PAWN]; Square lastSquare = plUsPawn[--pieceCount[us][PieceTypeC.PAWN]]; index[lastSquare] = index[to]; plUsPawn[index[lastSquare]] = lastSquare; plUsPawn[pieceCount[us][PieceTypeC.PAWN]] = SquareC.SQ_NONE; index[to] = pieceCount[us][promotion]; pieceList[us][promotion][index[to]] = to; // Update hash keys k ^= zobrist[us][PieceTypeC.PAWN][to] ^ zobrist[us][promotion][to]; st.pawnKey ^= zobrist[us][PieceTypeC.PAWN][to]; st.materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]++] ^ zobrist[us][PieceTypeC.PAWN][pieceCount[us][PieceTypeC.PAWN]]; // Update incremental score st.psqScore += pieceSquareTable[((us << 3) | promotion)][to] - pieceSquareTable[((us << 3) | PieceTypeC.PAWN)][to]; // Update material if (us == 0) { st.npMaterialWHITE += PieceValueMidgame[promotion]; } else { st.npMaterialBLACK += PieceValueMidgame[promotion]; } } // Update pawn hash key st.pawnKey ^= zobrist[us][PieceTypeC.PAWN][from] ^ zobrist[us][PieceTypeC.PAWN][to]; // Reset rule 50 draw counter st.rule50 = 0; } // Update incremental scores st.psqScore += (pieceSquareTable[piece][to] - pieceSquareTable[piece][from]); // Set capture piece st.capturedType = capture; // Update the key with the final value st.key = k; // Update checkers bitboard, piece must be already moved st.checkersBB = 0; if (moveIsCheck) { if ((m & (3 << 14)) != 0) st.checkersBB = attackers_to(pieceList[them][PieceTypeC.KING][0]) & byColorBB[us]; else { // Direct checks if ((ci.checkSq[pt] & Utils.SquareBB[to]) != 0) { st.checkersBB |= Utils.SquareBB[to]; } // Discovery checks if ((ci.dcCandidates != 0) && ((ci.dcCandidates & Utils.SquareBB[from]) != 0)) { if (pt != PieceTypeC.ROOK) st.checkersBB |= attacks_from_ROOK(pieceList[them][PieceTypeC.KING][0]) & ((byTypeBB[PieceTypeC.ROOK] | byTypeBB[PieceTypeC.QUEEN]) & byColorBB[us]); if (pt != PieceTypeC.BISHOP) st.checkersBB |= attacks_from_BISHOP(pieceList[them][PieceTypeC.KING][0]) & ((byTypeBB[PieceTypeC.BISHOP] | byTypeBB[PieceTypeC.QUEEN]) & byColorBB[us]); } } } // Finish sideToMove = sideToMove ^ 1; Debug.Assert(pos_is_ok()); }
/// move_gives_check() tests whether a pseudo-legal move gives a check internal bool move_gives_check(Move m, CheckInfo ci) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(ci.dcCandidates == discovered_check_candidates()); Debug.Assert(Utils.color_of(piece_moved(m)) == sideToMove); Square from = (m >> 6) & 0x3F; Square to = m & 0x3F; PieceType pt = board[from] & 7; // Direct check ? if ((ci.checkSq[pt] & Utils.SquareBB[to]) != 0) return true; // Discovery check ? if ((ci.dcCandidates != 0) && ((ci.dcCandidates & Utils.SquareBB[from]) != 0)) { // For pawn and king moves we need to verify also direction if ((pt != PieceTypeC.PAWN && pt != PieceTypeC.KING) || (((Utils.BetweenBB[from][to] | Utils.BetweenBB[from][pieceList[sideToMove ^ 1][PieceTypeC.KING][0]] | Utils.BetweenBB[to][pieceList[sideToMove ^ 1][PieceTypeC.KING][0]]) & (Utils.SquareBB[from] | Utils.SquareBB[to] | Utils.SquareBB[pieceList[sideToMove ^ 1][PieceTypeC.KING][0]])) == 0)) return true; } // Can we skip the ugly special cases ? if ((m & (3 << 14)) == 0) return false; Color us = sideToMove; Square ksq = pieceList[sideToMove ^ 1][PieceTypeC.KING][0]; // Promotion with check ? if ((m & (3 << 14)) == (1 << 14)) { return (attacks_from((((m >> 12) & 3) + 2), to, occupied_squares ^ Utils.SquareBB[from]) & Utils.SquareBB[ksq]) != 0; } // En passant capture with check ? We have already handled the case // of direct checks and ordinary discovered check, the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. if ((m & (3 << 14)) == (2 << 14)) { Square capsq = (((from >> 3) << 3) | (to & 7)); Bitboard b = (occupied_squares ^ Utils.SquareBB[from] ^ Utils.SquareBB[capsq]) | Utils.SquareBB[to]; return ((Utils.rook_attacks_bb(ksq, b) & ((byTypeBB[PieceTypeC.ROOK] | byTypeBB[PieceTypeC.QUEEN]) & byColorBB[us])) != 0) || ((Utils.bishop_attacks_bb(ksq, b) & ((byTypeBB[PieceTypeC.BISHOP] | byTypeBB[PieceTypeC.QUEEN]) & byColorBB[us])) != 0); } // Castling with check ? if ((m & (3 << 14)) == (3 << 14)) { Square kfrom = from; Square rfrom = to; // 'King captures the rook' notation Square kto = ((rfrom > kfrom ? SquareC.SQ_G1 : SquareC.SQ_C1) ^ (us * 56)); Square rto = ((rfrom > kfrom ? SquareC.SQ_F1 : SquareC.SQ_D1) ^ (us * 56)); Bitboard b = (occupied_squares ^ Utils.SquareBB[kfrom] ^ Utils.SquareBB[rfrom]) | Utils.SquareBB[rto] | Utils.SquareBB[kto]; return (Utils.rook_attacks_bb(rto, b) & Utils.SquareBB[ksq]) != 0; } return false; }
internal void do_move(int m, StateInfo newSt, CheckInfo ci, bool moveIsCheck) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(newSt != this.st); this.nodes++; var k = this.st.key; newSt.pawnKey = this.st.pawnKey; newSt.materialKey = this.st.materialKey; newSt.npMaterialWHITE = this.st.npMaterialWHITE; newSt.npMaterialBLACK = this.st.npMaterialBLACK; newSt.castleRights = this.st.castleRights; newSt.rule50 = this.st.rule50; newSt.pliesFromNull = this.st.pliesFromNull; newSt.psqScore = this.st.psqScore; newSt.epSquare = this.st.epSquare; newSt.previous = this.st; this.st = newSt; // Update side to move k ^= Zobrist.side; // Increment the 50 moves rule draw counter. Resetting it to zero in the // case of a capture or a pawn move is taken care of later. this.st.rule50++; this.st.pliesFromNull++; var us = this.sideToMove; var them = us ^ 1; Square from = Utils.from_sq(m); Square to = Utils.to_sq(m); Piece piece = piece_on(from); PieceType pt = Utils.type_of(piece); PieceType capture = Utils.type_of_move(m) == MoveTypeC.ENPASSANT ? PieceTypeC.PAWN : Utils.type_of(piece_on(to)); Debug.Assert(Utils.color_of(piece) == us); Debug.Assert(piece_on(to) == PieceC.NO_PIECE || Utils.color_of(piece_on(to)) == them || Utils.type_of_move(m) == MoveTypeC.CASTLING); Debug.Assert(capture != PieceTypeC.KING); if (Utils.type_of_move(m) == MoveTypeC.CASTLING) { Debug.Assert(piece == Utils.make_piece(us, PieceTypeC.KING)); bool kingSide = to > from; Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rto = Utils.relative_square(us, kingSide ? SquareC.SQ_F1 : SquareC.SQ_D1); to = Utils.relative_square(us, kingSide ? SquareC.SQ_G1 : SquareC.SQ_C1); capture = PieceTypeC.NO_PIECE_TYPE; do_castle(from, to, rfrom, rto); st.psqScore += psq_delta(Utils.make_piece(us, PieceTypeC.ROOK), rfrom, rto); k ^= Zobrist.psq[us][PieceTypeC.ROOK][rfrom] ^ Zobrist.psq[us][PieceTypeC.ROOK][rto]; } if (capture != 0) { var capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (capture == PieceTypeC.PAWN) { if (Utils.type_of_move(m) == MoveTypeC.ENPASSANT) { capsq += (them == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); Debug.Assert(pt == PieceTypeC.PAWN); Debug.Assert(to == this.st.epSquare); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_6); Debug.Assert(this.piece_on(to) == PieceC.NO_PIECE); Debug.Assert(this.piece_on(capsq) == Utils.make_piece(them, PieceTypeC.PAWN)); this.board[capsq] = PieceC.NO_PIECE; } this.st.pawnKey ^= Zobrist.psq[them][PieceTypeC.PAWN][capsq]; } else { if (them == 0) { this.st.npMaterialWHITE -= PieceValue[PhaseC.MG][capture]; } else { this.st.npMaterialBLACK -= PieceValue[PhaseC.MG][capture]; } } // Remove the captured piece var capPieceMask = Utils.SquareBB[capsq]; this.occupied_squares ^= capPieceMask; this.byTypeBB[capture] ^= capPieceMask; this.byColorBB[them] ^= capPieceMask; // Update piece list, move the last piece at index[capsq] position and // shrink the list. // // WARNING: This is a not revresible operation. When we will reinsert the // captured piece in undo_move() we will put it at the end of the list and // not in its original place, it means index[] and pieceList[] are not // guaranteed to be invariant to a do_move() + undo_move() sequence. var plThemCapture = this.pieceList[them][capture]; var pcThemCapture = --this.pieceCount[them][capture]; var lastSquare = plThemCapture[pcThemCapture]; this.index[lastSquare] = this.index[capsq]; plThemCapture[this.index[lastSquare]] = lastSquare; plThemCapture[pcThemCapture] = SquareC.SQ_NONE; // Update hash keys k ^= Zobrist.psq[them][capture][capsq]; this.st.materialKey ^= Zobrist.psq[them][capture][pcThemCapture]; // Update incremental scores this.st.psqScore -= Zobrist.PieceSquareTable[((them << 3) | capture)][capsq]; // Reset rule 50 counter this.st.rule50 = 0; } // Update hash key k ^= Zobrist.psq[us][pt][from] ^ Zobrist.psq[us][pt][to]; // Reset en passant square if (this.st.epSquare != SquareC.SQ_NONE) { k ^= Zobrist.enpassant[this.st.epSquare & 7]; this.st.epSquare = SquareC.SQ_NONE; } // Update castle rights if needed if ((this.st.castleRights != 0) && ((this.castleRightsMask[from] | this.castleRightsMask[to]) != 0)) { var cr = this.castleRightsMask[from] | this.castleRightsMask[to]; k ^= Zobrist.castle[this.st.castleRights & cr]; this.st.castleRights &= ~cr; } // Move the piece. The tricky Chess960 castle is handled earlier if (Utils.type_of_move(m) != MoveTypeC.CASTLING) { Bitboard from_to_bb = Utils.SquareBB[from] ^ Utils.SquareBB[to]; occupied_squares ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; byColorBB[us] ^= from_to_bb; board[from] = PieceC.NO_PIECE; board[to] = piece; // Update piece lists, index[from] is not updated and becomes stale. This // works as long as index[] is accessed just by known occupied squares. index[to] = index[from]; pieceList[us][pt][index[to]] = to; } // If the moving piece is a pawn do some special extra work if (pt == PieceTypeC.PAWN) { // Set en-passant square, only if moved pawn can be captured if ((to ^ from) == 16 && ((((Utils.StepAttacksBB[((us << 3) | PieceTypeC.PAWN)][ from + (us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)]) & (this.byTypeBB[PieceTypeC.PAWN] & this.byColorBB[them]))) != 0)) { this.st.epSquare = ((from + to) / 2); k ^= Zobrist.enpassant[this.st.epSquare & 7]; } if (Utils.type_of_move(m) == MoveTypeC.PROMOTION) { var promotion = (((m >> 12) & 3) + 2); Debug.Assert(Utils.relative_rank_CS(us, to) == RankC.RANK_8); Debug.Assert(promotion >= PieceTypeC.KNIGHT && promotion <= PieceTypeC.QUEEN); // Replace the pawn with the promoted piece this.byTypeBB[PieceTypeC.PAWN] ^= Utils.SquareBB[to]; this.byTypeBB[promotion] |= Utils.SquareBB[to]; this.board[to] = ((us << 3) | promotion); // Update piece lists, move the last pawn at index[to] position // and shrink the list. Add a new promotion piece to the list. var plUsPawn = this.pieceList[us][PieceTypeC.PAWN]; var lastSquare = plUsPawn[--this.pieceCount[us][PieceTypeC.PAWN]]; this.index[lastSquare] = this.index[to]; plUsPawn[this.index[lastSquare]] = lastSquare; plUsPawn[this.pieceCount[us][PieceTypeC.PAWN]] = SquareC.SQ_NONE; this.index[to] = this.pieceCount[us][promotion]; this.pieceList[us][promotion][this.index[to]] = to; // Update hash keys k ^= Zobrist.psq[us][PieceTypeC.PAWN][to] ^ Zobrist.psq[us][promotion][to]; this.st.pawnKey ^= Zobrist.psq[us][PieceTypeC.PAWN][to]; this.st.materialKey ^= Zobrist.psq[us][promotion][this.pieceCount[us][promotion]++] ^ Zobrist.psq[us][PieceTypeC.PAWN][this.pieceCount[us][PieceTypeC.PAWN]]; // Update incremental score this.st.psqScore += Zobrist.PieceSquareTable[((us << 3) | promotion)][to] - Zobrist.PieceSquareTable[((us << 3) | PieceTypeC.PAWN)][to]; // Update material if (us == 0) { this.st.npMaterialWHITE += PieceValue[PhaseC.MG][promotion]; } else { this.st.npMaterialBLACK += PieceValue[PhaseC.MG][promotion]; } } // Update pawn hash key this.st.pawnKey ^= Zobrist.psq[us][PieceTypeC.PAWN][from] ^ Zobrist.psq[us][PieceTypeC.PAWN][to]; // Reset rule 50 draw counter this.st.rule50 = 0; } // Update incremental scores this.st.psqScore += (Zobrist.PieceSquareTable[piece][to] - Zobrist.PieceSquareTable[piece][from]); // Set capture piece this.st.capturedType = capture; // Update the key with the final value this.st.key = k; // Update checkers bitboard, piece must be already moved this.st.checkersBB = 0; if (moveIsCheck) { if (Utils.type_of_move(m) != MoveTypeC.NORMAL) { this.st.checkersBB = this.attackers_to(this.pieceList[them][PieceTypeC.KING][0]) & this.byColorBB[us]; } else { // Direct checks if ((ci.checkSq[pt] & Utils.SquareBB[to]) != 0) { this.st.checkersBB |= Utils.SquareBB[to]; } // Discovery checks if ((ci.dcCandidates != 0) && ((ci.dcCandidates & Utils.SquareBB[from]) != 0)) { if (pt != PieceTypeC.ROOK) { this.st.checkersBB |= this.attacks_from_ROOK(this.pieceList[them][PieceTypeC.KING][0]) & ((this.byTypeBB[PieceTypeC.ROOK] | this.byTypeBB[PieceTypeC.QUEEN]) & this.byColorBB[us]); } if (pt != PieceTypeC.BISHOP) { this.st.checkersBB |= this.attacks_from_BISHOP(this.pieceList[them][PieceTypeC.KING][0]) & ((this.byTypeBB[PieceTypeC.BISHOP] | this.byTypeBB[PieceTypeC.QUEEN]) & this.byColorBB[us]); } } } } // Finish this.sideToMove = this.sideToMove ^ 1; Debug.Assert(this.pos_is_ok()); }
/// move_gives_check() tests whether a pseudo-legal move gives a check internal bool move_gives_check(int m, CheckInfo ci) { Debug.Assert(Utils.is_ok_M(m)); Debug.Assert(ci.dcCandidates == this.discovered_check_candidates()); Debug.Assert(Utils.color_of(this.piece_moved(m)) == this.sideToMove); var from = (m >> 6) & 0x3F; var to = m & 0x3F; var pt = this.board[from] & 7; // Direct check ? if ((ci.checkSq[pt] & Utils.SquareBB[to]) != 0) { return true; } // Discovery check ? if ((ci.dcCandidates != 0) && ((ci.dcCandidates & Utils.SquareBB[from]) != 0)) { // For pawn and king moves we need to verify also direction if ((pt != PieceTypeC.PAWN && pt != PieceTypeC.KING) || (((Utils.BetweenBB[from][to] | Utils.BetweenBB[from][this.pieceList[this.sideToMove ^ 1][PieceTypeC.KING][0]] | Utils.BetweenBB[to][this.pieceList[this.sideToMove ^ 1][PieceTypeC.KING][0]]) & (Utils.SquareBB[from] | Utils.SquareBB[to] | Utils.SquareBB[this.pieceList[this.sideToMove ^ 1][PieceTypeC.KING][0]])) == 0)) { return true; } } // Can we skip the ugly special cases ? if (Utils.type_of_move(m) == MoveTypeC.NORMAL) { return false; } var us = this.sideToMove; var ksq = this.pieceList[this.sideToMove ^ 1][PieceTypeC.KING][0]; switch (Utils.type_of_move(m)) { case MoveTypeC.PROMOTION: return (attacks_from((((m >> 12) & 3) + 2), to, this.occupied_squares ^ Utils.SquareBB[from]) & Utils.SquareBB[ksq]) != 0; // En passant capture with check ? We have already handled the case // of direct checks and ordinary discovered check, the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. case MoveTypeC.ENPASSANT: { var capsq = (((from >> 3) << 3) | (to & 7)); var b = (this.occupied_squares ^ Utils.SquareBB[from] ^ Utils.SquareBB[capsq]) | Utils.SquareBB[to]; return ((Utils.rook_attacks_bb(ksq, b) & ((this.byTypeBB[PieceTypeC.ROOK] | this.byTypeBB[PieceTypeC.QUEEN]) & this.byColorBB[us])) != 0) || ((Utils.bishop_attacks_bb(ksq, b) & ((this.byTypeBB[PieceTypeC.BISHOP] | this.byTypeBB[PieceTypeC.QUEEN]) & this.byColorBB[us])) != 0); } case MoveTypeC.CASTLING: if (Utils.type_of_move(m) == MoveTypeC.CASTLING) { var kfrom = from; var rfrom = to; // 'King captures the rook' notation var kto = ((rfrom > kfrom ? SquareC.SQ_G1 : SquareC.SQ_C1) ^ (us * 56)); var rto = ((rfrom > kfrom ? SquareC.SQ_F1 : SquareC.SQ_D1) ^ (us * 56)); var b = (this.occupied_squares ^ Utils.SquareBB[kfrom] ^ Utils.SquareBB[rfrom]) | Utils.SquareBB[rto] | Utils.SquareBB[kto]; return (Utils.rook_attacks_bb(rto, b) & Utils.SquareBB[ksq]) != 0; } Debug.Assert(false); return false; default: Debug.Assert(false); return false; } }