/** Record move as a failure. */ public void addFail(Position pos, Move m, int depth) { int p = pos.getPiece(m.from); int cnt = depth; countFail[p][m.to] += cnt; score[p][m.to] = -1; }
/** * Prints board to console */ public static void DispBoard(Position pos) { string ll = " +----+----+----+----+----+----+----+----+"; SystemHelper.println(ll); for (int y = 7; y >= 0; y--) { string ret = " |"; for (int x = 0; x < 8; x++) { ret+=' '; int p = pos.getPiece(Position.getSquare(x, y)); if (p == Piece.EMPTY) { bool dark = Position.darkSquare(x, y); ret+=(dark ? ".. |" : " |"); } else { ret+=(Piece.isWhite(p) ? ' ' : '*'); string pieceName = pieceToChar(p); if (pieceName.Length == 0) pieceName = "P"; ret+=pieceName; ret+=" |"; } } SystemHelper.println(ret); SystemHelper.println(ll); } }
/** Record move as a success. */ public void addSuccess(Position pos, Move m, int depth) { int p = pos.getPiece(m.from); int cnt = depth; int val = countSuccess[p][m.to] + cnt; if (val > 1000) { val /= 2; countFail[p][m.to] /= 2; } countSuccess[p][m.to] = val; score[p][m.to] = -1; }
/** Get a score between 0 and 49, depending of the success/fail ratio of the move. */ public int getHistScore(Position pos, Move m) { int p = pos.getPiece(m.from); int ret = score[p][m.to]; if (ret >= 0) return ret; int succ = countSuccess[p][m.to]; int fail = countFail[p][m.to]; if (succ + fail > 0) { ret = succ * 49 / (succ + fail); } else { ret = 0; } score[p][m.to] = ret; return ret; }
/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */ public static void fixupEPSquare(Position pos) { int epSquare = pos.getEpSquare(); if (epSquare >= 0) { MoveGen MG = new MoveGen(); MoveGen.MoveList moves = MG.pseudoLegalMoves(pos); MoveGen.RemoveIllegal(pos, moves); bool epValid = false; for (int mi = 0; mi < moves.size; mi++) { Move m = moves.m[mi]; if (m.to == epSquare) { if (pos.getPiece(m.from) == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) { epValid = true; break; } } } if (!epValid) { pos.setEpSquare(-1); } } }
private static string moveTostring(Position pos, Move move, bool ulongForm, MoveGen.MoveList moves) { string ret = ""; int wKingOrigPos = Position.getSquare(4, 0); int bKingOrigPos = Position.getSquare(4, 7); if (move.from == wKingOrigPos && pos.getPiece(wKingOrigPos) == Piece.WKING) { // Check white castle if (move.to == Position.getSquare(6, 0)) { ret += ("O-O"); } else if (move.to == Position.getSquare(2, 0)) { ret += ("O-O-O"); } } else if (move.from == bKingOrigPos && pos.getPiece(bKingOrigPos) == Piece.BKING) { // Check white castle if (move.to == Position.getSquare(6, 7)) { ret += ("O-O"); } else if (move.to == Position.getSquare(2, 7)) { ret += ("O-O-O"); } } if (ret.Length == 0) { int p = pos.getPiece(move.from); ret += pieceToChar(p); int x1 = Position.getX(move.from); int y1 = Position.getY(move.from); int x2 = Position.getX(move.to); int y2 = Position.getY(move.to); if (ulongForm) { ret += ((char)(x1 + 'a')); ret += ((char)(y1 + '1')); ret += (isCapture(pos, move) ? 'x' : '-'); } else { if (p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) { if (isCapture(pos, move)) { ret += ((char)(x1 + 'a')); } } else { int numSameTarget = 0; int numSameFile = 0; int numSameRow = 0; for (int mi = 0; mi < moves.size; mi++) { Move m = moves.m[mi]; if (m == null) break; if ((pos.getPiece(m.from) == p) && (m.to == move.to)) { numSameTarget++; if (Position.getX(m.from) == x1) numSameFile++; if (Position.getY(m.from) == y1) numSameRow++; } } if (numSameTarget < 2) { // No file/row info needed } else if (numSameFile < 2) { ret += ((char)(x1 + 'a')); // Only file info needed } else if (numSameRow < 2) { ret += ((char)(y1 + '1')); // Only row info needed } else { ret += ((char)(x1 + 'a')); // File and row info needed ret += ((char)(y1 + '1')); } } if (isCapture(pos, move)) { ret += ('x'); } } ret += ((char)(x2 + 'a')); ret += ((char)(y2 + '1')); if (move.promoteTo != Piece.EMPTY) { ret += (pieceToChar(move.promoteTo)); } } UndoInfo ui = new UndoInfo(); if (MoveGen.givesCheck(pos, move)) { pos.makeMove(move, ui); MoveGen MG = new MoveGen(); MoveGen.MoveList nextMoves = MG.pseudoLegalMoves(pos); MoveGen.RemoveIllegal(pos, nextMoves); if (nextMoves.size == 0) { ret += ('#'); } else { ret += ('+'); } pos.unMakeMove(move, ui); } return ret; }
private static bool isCapture(Position pos, Move move) { if (pos.getPiece(move.to) == Piece.EMPTY) { int p = pos.getPiece(move.from); if ((p == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) && (move.to == pos.getEpSquare())) { return true; } else { return false; } } else { return true; } }
/** Return a FEN string corresponding to a chess Position object. */ public static string toFEN(Position pos) { string ret = ""; // Piece placement for (int r = 7; r >=0; r--) { int numEmpty = 0; for (int c = 0; c < 8; c++) { int p = pos.getPiece(Position.getSquare(c, r)); if (p == Piece.EMPTY) { numEmpty++; } else { if (numEmpty > 0) { ret+=numEmpty.ToString(); numEmpty = 0; } switch (p) { case Piece.WKING: ret+=('K'); break; case Piece.WQUEEN: ret+=('Q'); break; case Piece.WROOK: ret+=('R'); break; case Piece.WBISHOP: ret+=('B'); break; case Piece.WKNIGHT: ret+=('N'); break; case Piece.WPAWN: ret+=('P'); break; case Piece.BKING: ret+=('k'); break; case Piece.BQUEEN: ret+=('q'); break; case Piece.BROOK: ret+=('r'); break; case Piece.BBISHOP: ret+=('b'); break; case Piece.BKNIGHT: ret+=('n'); break; case Piece.BPAWN: ret+=('p'); break; default: throw new RuntimeException(); } } } if (numEmpty > 0) { ret += numEmpty.ToString(); } if (r > 0) { ret += ('/'); } } ret += (pos.whiteMove ? " w " : " b "); // Castling rights bool anyCastle = false; if (pos.h1Castle()) { ret += ('K'); anyCastle = true; } if (pos.a1Castle()) { ret += ('Q'); anyCastle = true; } if (pos.h8Castle()) { ret += ('k'); anyCastle = true; } if (pos.a8Castle()) { ret += ('q'); anyCastle = true; } if (!anyCastle) { ret += ('-'); } // En passant target square { ret += (' '); if (pos.getEpSquare() >= 0) { int x = Position.getX(pos.getEpSquare()); int y = Position.getY(pos.getEpSquare()); ret += ((char)(x + 'a')); ret += ((char)(y + '1')); } else { ret += ('-'); } } // Move counters ret += " " + pos.halfMoveClock; ret += " " + pos.fullMoveCounter; return ret; }
/*throws ChessParseError*/ /** Parse a FEN string and return a chess Position object. */ public static Position readFEN(string fen) { Position pos = new Position(); string[] words = fen.Split(' '); if (words.Length < 2) { throw new ChessParseError(/* "Too few pieces " */); } // Piece placement int row = 7; int col = 0; for (int i = 0; i < words[0].Length; i++) { char c = words[0][i]; switch (c) { case '1': col += 1; break; case '2': col += 2; break; case '3': col += 3; break; case '4': col += 4; break; case '5': col += 5; break; case '6': col += 6; break; case '7': col += 7; break; case '8': col += 8; break; case '/': row--; col = 0; break; case 'P': safeSetPiece(pos, col, row, Piece.WPAWN); col++; break; case 'N': safeSetPiece(pos, col, row, Piece.WKNIGHT); col++; break; case 'B': safeSetPiece(pos, col, row, Piece.WBISHOP); col++; break; case 'R': safeSetPiece(pos, col, row, Piece.WROOK); col++; break; case 'Q': safeSetPiece(pos, col, row, Piece.WQUEEN); col++; break; case 'K': safeSetPiece(pos, col, row, Piece.WKING); col++; break; case 'p': safeSetPiece(pos, col, row, Piece.BPAWN); col++; break; case 'n': safeSetPiece(pos, col, row, Piece.BKNIGHT); col++; break; case 'b': safeSetPiece(pos, col, row, Piece.BBISHOP); col++; break; case 'r': safeSetPiece(pos, col, row, Piece.BROOK); col++; break; case 'q': safeSetPiece(pos, col, row, Piece.BQUEEN); col++; break; case 'k': safeSetPiece(pos, col, row, Piece.BKING); col++; break; default: throw new ChessParseError(/* "Invalid piece" */); } } if (words[1].Length == 0) { throw new ChessParseError(/*"Invalid side"*/); } pos.setWhiteMove(words[1][0] == 'w'); // Castling rights int castleMask = 0; if (words.Length > 2) { for (int i = 0; i < words[2].Length; i++) { char c = words[2][i]; switch (c) { case 'K': castleMask |= (1 << Position.H1_CASTLE); break; case 'Q': castleMask |= (1 << Position.A1_CASTLE); break; case 'k': castleMask |= (1 << Position.H8_CASTLE); break; case 'q': castleMask |= (1 << Position.A8_CASTLE); break; case '-': break; default: throw new ChessParseError(/* "Invalid castling flags" */); } } } pos.setCastleMask(castleMask); if (words.Length > 3) { // En passant target square string epstring = words[3]; if (epstring != "-") { if (epstring.Length < 2) { throw new ChessParseError(/* "Invalid en passant square" */); } pos.setEpSquare(getSquare(epstring)); } } try { if (words.Length > 4) { pos.halfMoveClock = int.Parse(words[4]); } if (words.Length > 5) { pos.fullMoveCounter = int.Parse(words[5]); } } catch (NumberFormatException nfe) { // Ignore errors here, since the fields are optional } // Each side must have exactly one king int wKings = 0; int bKings = 0; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { int p = pos.getPiece(Position.getSquare(x, y)); if (p == Piece.WKING) { wKings++; } else if (p == Piece.BKING) { bKings++; } } } if (wKings != 1) { throw new ChessParseError(/* "White must have exactly one king" */); } if (bKings != 1) { throw new ChessParseError(/* "Black must have exactly one king" */); } // Make sure king can not be captured Position pos2 = new Position(pos); pos2.setWhiteMove(!pos.whiteMove); if (MoveGen.inCheck(pos2)) { throw new ChessParseError(/* "King capture possible" */); } fixupEPSquare(pos); return pos; }
/** Like nextPiece(), but handles board edges. */ private static int nextPieceSafe(Position pos, int sq, int delta) { int dx = 0, dy = 0; switch (delta) { case 1: dx=1; dy=0; break; case 9: dx=1; dy=1; break; case 8: dx=0; dy=1; break; case 7: dx=-1; dy=1; break; case -1: dx=-1; dy=0; break; case -9: dx=-1; dy=-1; break; case -8: dx=0; dy=-1; break; case -7: dx=1; dy=-1; break; } int x = Position.getX(sq); int y = Position.getY(sq); while (true) { x += dx; y += dy; if ((x < 0) || (x > 7) || (y < 0) || (y > 7)) { return Piece.EMPTY; } int p = pos.getPiece(Position.getSquare(x, y)); if (p != Piece.EMPTY) return p; } }
/** * Return the next piece in a given direction, starting from sq. */ private static int nextPiece(Position pos, int sq, int delta) { while (true) { sq += delta; int p = pos.getPiece(sq); if (p != Piece.EMPTY) return p; } }
/** * Generate and return a list of pseudo-legal moves. * Pseudo-legal means that the moves doesn't necessarily defend from check threats. */ public MoveList pseudoLegalMoves(Position pos) { MoveList moveList = getMoveListObj(); ulong occupied = pos.whiteBB | pos.blackBB; if (pos.whiteMove) { int sq = 0; ulong m = 0; // Queen moves ulong squares = pos.pieceTypeBB[Piece.WQUEEN]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Rook moves squares = pos.pieceTypeBB[Piece.WROOK]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.rookAttacks(sq, occupied) & ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Bishop moves squares = pos.pieceTypeBB[Piece.WBISHOP]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.bishopAttacks(sq, occupied) & ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // King moves { sq = pos.getKingSq(true); m = BitBoard.kingAttacks[sq] & ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; int k0 = 4; if (sq == k0) { ulong OO_SQ = 0x60L; ulong OOO_SQ = 0xEL; if (((pos.getCastleMask() & (1 << Position.H1_CASTLE)) != 0) && ((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 + 3) == Piece.WROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 + 1)) { setMove(moveList, k0, k0 + 2, Piece.EMPTY); } if (((pos.getCastleMask() & (1 << Position.A1_CASTLE)) != 0) && ((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 - 4) == Piece.WROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 - 1)) { setMove(moveList, k0, k0 - 2, Piece.EMPTY); } } } // Knight moves ulong knights = pos.pieceTypeBB[Piece.WKNIGHT]; while (knights != 0) { sq = BitBoard.numberOfTrailingZeros(knights); m = BitBoard.knightAttacks[sq] & ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; knights &= knights-1; } // Pawn moves ulong pawns = pos.pieceTypeBB[Piece.WPAWN]; m = (pawns << 8) & ~occupied; if (addPawnMovesByMask(moveList, pos, m, -8, true)) return moveList; m = ((m & BitBoard.maskRow3) << 8) & ~occupied; addPawnDoubleMovesByMask(moveList, pos, m, -16); int epSquare = pos.getEpSquare(); ulong epMask = (epSquare >= 0) ? (1UL << epSquare) : 0L; m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask); if (addPawnMovesByMask(moveList, pos, m, -7, true)) return moveList; m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask); if (addPawnMovesByMask(moveList, pos, m, -9, true)) return moveList; } else { int sq = 0; ulong m = 0; // Queen moves ulong squares = pos.pieceTypeBB[Piece.BQUEEN]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)) & ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Rook moves squares = pos.pieceTypeBB[Piece.BROOK]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.rookAttacks(sq, occupied) & ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Bishop moves squares = pos.pieceTypeBB[Piece.BBISHOP]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.bishopAttacks(sq, occupied) & ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // King moves { sq = pos.getKingSq(false); m = BitBoard.kingAttacks[sq] & ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; int k0 = 60; if (sq == k0) { ulong OO_SQ = 0x6000000000000000L; ulong OOO_SQ = 0xE00000000000000L; if (((pos.getCastleMask() & (1 << Position.H8_CASTLE)) != 0) && ((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 + 3) == Piece.BROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 + 1)) { setMove(moveList, k0, k0 + 2, Piece.EMPTY); } if (((pos.getCastleMask() & (1 << Position.A8_CASTLE)) != 0) && ((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 - 4) == Piece.BROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 - 1)) { setMove(moveList, k0, k0 - 2, Piece.EMPTY); } } } // Knight moves ulong knights = pos.pieceTypeBB[Piece.BKNIGHT]; while (knights != 0) { sq = BitBoard.numberOfTrailingZeros(knights); m = BitBoard.knightAttacks[sq] & ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; knights &= knights-1; } // Pawn moves ulong pawns = pos.pieceTypeBB[Piece.BPAWN]; m = (pawns >> 8) & ~occupied; if (addPawnMovesByMask(moveList, pos, m, 8, true)) return moveList; m = ((m & BitBoard.maskRow6) >> 8) & ~occupied; addPawnDoubleMovesByMask(moveList, pos, m, 16); int epSquare = pos.getEpSquare(); ulong epMask = (epSquare >= 0) ? (1UL << epSquare) : 0L; m = (pawns >> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask); if (addPawnMovesByMask(moveList, pos, m, 9, true)) return moveList; m = (pawns >> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask); if (addPawnMovesByMask(moveList, pos, m, 7, true)) return moveList; } return moveList; }
/** Generate captures, checks, and possibly some other moves that are too hard to filter out. */ public MoveList pseudoLegalCapturesAndChecks(Position pos) { MoveList moveList = getMoveListObj(); ulong occupied = pos.whiteBB | pos.blackBB; if (pos.whiteMove) { int sq = 0; ulong m = 0; int bKingSq = pos.getKingSq(false); ulong discovered = 0; // Squares that could generate discovered checks ulong kRookAtk = BitBoard.rookAttacks(bKingSq, occupied); if ((BitBoard.rookAttacks(bKingSq, occupied & ~kRookAtk) & (pos.pieceTypeBB[Piece.WQUEEN] | pos.pieceTypeBB[Piece.WROOK])) != 0) discovered |= kRookAtk; ulong kBishAtk = BitBoard.bishopAttacks(bKingSq, occupied); if ((BitBoard.bishopAttacks(bKingSq, occupied & ~kBishAtk) & (pos.pieceTypeBB[Piece.WQUEEN] | pos.pieceTypeBB[Piece.WBISHOP])) != 0) discovered |= kBishAtk; // Queen moves ulong squares = pos.pieceTypeBB[Piece.WQUEEN]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)); if ((discovered & (1UL<<sq)) == 0) m &= (pos.blackBB | kRookAtk | kBishAtk); m &= ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Rook moves squares = pos.pieceTypeBB[Piece.WROOK]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.rookAttacks(sq, occupied); if ((discovered & (1UL<<sq)) == 0) m &= (pos.blackBB | kRookAtk); m &= ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Bishop moves squares = pos.pieceTypeBB[Piece.WBISHOP]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.bishopAttacks(sq, occupied); if ((discovered & (1UL<<sq)) == 0) m &= (pos.blackBB | kBishAtk); m &= ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // King moves { sq = pos.getKingSq(true); m = BitBoard.kingAttacks[sq]; m &= ((discovered & (1UL<<sq)) == 0) ? pos.blackBB : ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; int k0 = 4; if (sq == k0) { ulong OO_SQ = 0x60L; ulong OOO_SQ = 0xEL; if (((pos.getCastleMask() & (1 << Position.H1_CASTLE)) != 0) && ((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 + 3) == Piece.WROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 + 1)) { setMove(moveList, k0, k0 + 2, Piece.EMPTY); } if (((pos.getCastleMask() & (1 << Position.A1_CASTLE)) != 0) && ((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 - 4) == Piece.WROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 - 1)) { setMove(moveList, k0, k0 - 2, Piece.EMPTY); } } } // Knight moves ulong knights = pos.pieceTypeBB[Piece.WKNIGHT]; ulong kKnightAtk = BitBoard.knightAttacks[bKingSq]; while (knights != 0) { sq = BitBoard.numberOfTrailingZeros(knights); m = BitBoard.knightAttacks[sq] & ~pos.whiteBB; if ((discovered & (1UL<<sq)) == 0) m &= (pos.blackBB | kKnightAtk); m &= ~pos.whiteBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; knights &= knights-1; } // Pawn moves // Captures ulong pawns = pos.pieceTypeBB[Piece.WPAWN]; int epSquare = pos.getEpSquare(); ulong epMask = (epSquare >= 0) ? (1UL << epSquare) : 0L; m = (pawns << 7) & BitBoard.maskAToGFiles & (pos.blackBB | epMask); if (addPawnMovesByMask(moveList, pos, m, -7, false)) return moveList; m = (pawns << 9) & BitBoard.maskBToHFiles & (pos.blackBB | epMask); if (addPawnMovesByMask(moveList, pos, m, -9, false)) return moveList; // Discovered checks and promotions ulong pawnAll = discovered | BitBoard.maskRow7; m = ((pawns & pawnAll) << 8) & ~(pos.whiteBB | pos.blackBB); if (addPawnMovesByMask(moveList, pos, m, -8, false)) return moveList; m = ((m & BitBoard.maskRow3) << 8) & ~(pos.whiteBB | pos.blackBB); addPawnDoubleMovesByMask(moveList, pos, m, -16); // Normal checks m = ((pawns & ~pawnAll) << 8) & ~(pos.whiteBB | pos.blackBB); if (addPawnMovesByMask(moveList, pos, m & BitBoard.bPawnAttacks[bKingSq], -8, false)) return moveList; m = ((m & BitBoard.maskRow3) << 8) & ~(pos.whiteBB | pos.blackBB); addPawnDoubleMovesByMask(moveList, pos, m & BitBoard.bPawnAttacks[bKingSq], -16); } else { int sq = 0; ulong m = 0; int wKingSq = pos.getKingSq(true); ulong discovered = 0; // Squares that could generate discovered checks ulong kRookAtk = BitBoard.rookAttacks(wKingSq, occupied); if ((BitBoard.rookAttacks(wKingSq, occupied & ~kRookAtk) & (pos.pieceTypeBB[Piece.BQUEEN] | pos.pieceTypeBB[Piece.BROOK])) != 0) discovered |= kRookAtk; ulong kBishAtk = BitBoard.bishopAttacks(wKingSq, occupied); if ((BitBoard.bishopAttacks(wKingSq, occupied & ~kBishAtk) & (pos.pieceTypeBB[Piece.BQUEEN] | pos.pieceTypeBB[Piece.BBISHOP])) != 0) discovered |= kBishAtk; // Queen moves ulong squares = pos.pieceTypeBB[Piece.BQUEEN]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = (BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied)); if ((discovered & (1UL<<sq)) == 0) m &= pos.whiteBB | kRookAtk | kBishAtk; m &= ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Rook moves squares = pos.pieceTypeBB[Piece.BROOK]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.rookAttacks(sq, occupied); if ((discovered & (1UL<<sq)) == 0) m &= pos.whiteBB | kRookAtk; m &= ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // Bishop moves squares = pos.pieceTypeBB[Piece.BBISHOP]; while (squares != 0) { sq = BitBoard.numberOfTrailingZeros(squares); m = BitBoard.bishopAttacks(sq, occupied); if ((discovered & (1UL<<sq)) == 0) m &= pos.whiteBB | kBishAtk; m &= ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; squares &= squares-1; } // King moves { sq = pos.getKingSq(false); m = BitBoard.kingAttacks[sq]; m &= ((discovered & (1UL<<sq)) == 0) ? pos.whiteBB : ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; int k0 = 60; if (sq == k0) { ulong OO_SQ = 0x6000000000000000L; ulong OOO_SQ = 0xE00000000000000L; if (((pos.getCastleMask() & (1 << Position.H8_CASTLE)) != 0) && ((OO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 + 3) == Piece.BROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 + 1)) { setMove(moveList, k0, k0 + 2, Piece.EMPTY); } if (((pos.getCastleMask() & (1 << Position.A8_CASTLE)) != 0) && ((OOO_SQ & (pos.whiteBB | pos.blackBB)) == 0) && (pos.getPiece(k0 - 4) == Piece.BROOK) && !sqAttacked(pos, k0) && !sqAttacked(pos, k0 - 1)) { setMove(moveList, k0, k0 - 2, Piece.EMPTY); } } } // Knight moves ulong knights = pos.pieceTypeBB[Piece.BKNIGHT]; ulong kKnightAtk = BitBoard.knightAttacks[wKingSq]; while (knights != 0) { sq = BitBoard.numberOfTrailingZeros(knights); m = BitBoard.knightAttacks[sq] & ~pos.blackBB; if ((discovered & (1UL<<sq)) == 0) m &= pos.whiteBB | kKnightAtk; m &= ~pos.blackBB; if (addMovesByMask(moveList, pos, sq, m)) return moveList; knights &= knights-1; } // Pawn moves // Captures ulong pawns = pos.pieceTypeBB[Piece.BPAWN]; int epSquare = pos.getEpSquare(); ulong epMask = (epSquare >= 0) ? (1UL << epSquare) : 0L; m = (pawns >> 9) & BitBoard.maskAToGFiles & (pos.whiteBB | epMask); if (addPawnMovesByMask(moveList, pos, m, 9, false)) return moveList; m = (pawns >> 7) & BitBoard.maskBToHFiles & (pos.whiteBB | epMask); if (addPawnMovesByMask(moveList, pos, m, 7, false)) return moveList; // Discovered checks and promotions ulong pawnAll = discovered | BitBoard.maskRow2; m = ((pawns & pawnAll) >> 8) & ~(pos.whiteBB | pos.blackBB); if (addPawnMovesByMask(moveList, pos, m, 8, false)) return moveList; m = ((m & BitBoard.maskRow6) >> 8) & ~(pos.whiteBB | pos.blackBB); addPawnDoubleMovesByMask(moveList, pos, m, 16); // Normal checks m = ((pawns & ~pawnAll) >> 8) & ~(pos.whiteBB | pos.blackBB); if (addPawnMovesByMask(moveList, pos, m & BitBoard.wPawnAttacks[wKingSq], 8, false)) return moveList; m = ((m & BitBoard.maskRow6) >> 8) & ~(pos.whiteBB | pos.blackBB); addPawnDoubleMovesByMask(moveList, pos, m & BitBoard.wPawnAttacks[wKingSq], 16); } return moveList; }
/** * Return true if making a move delivers check to the opponent */ public static bool givesCheck(Position pos, Move m) { bool wtm = pos.whiteMove; int oKingSq = pos.getKingSq(!wtm); int oKing = wtm ? Piece.BKING : Piece.WKING; int p = Piece.makeWhite(m.promoteTo == Piece.EMPTY ? pos.getPiece(m.from) : m.promoteTo); int d1 = BitBoard.getDirection(m.to, oKingSq); switch (d1) { case 8: case -8: case 1: case -1: // Rook direction if ((p == Piece.WQUEEN) || (p == Piece.WROOK)) if ((d1 != 0) && (/*MoveGen.*/ nextPiece(pos, m.to, d1) == oKing)) return true; break; case 9: case 7: case -9: case -7: // Bishop direction if ((p == Piece.WQUEEN) || (p == Piece.WBISHOP)) { if ((d1 != 0) && (/*MoveGen.*/ nextPiece(pos, m.to, d1) == oKing)) return true; } else if (p == Piece.WPAWN) { if (((d1 > 0) == wtm) && (pos.getPiece(m.to + d1) == oKing)) return true; } break; default: if (d1 != 0) { // Knight direction if (p == Piece.WKNIGHT) return true; } break; } int d2 = BitBoard.getDirection(m.from, oKingSq); if ((d2 != 0) && (d2 != d1) && (/*MoveGen.*/ nextPiece(pos, m.from, d2) == oKing)) { int p2 = /*MoveGen.*/ nextPieceSafe(pos, m.from, -d2); switch (d2) { case 8: case -8: case 1: case -1: // Rook direction if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || (p2 == (wtm ? Piece.WROOK : Piece.BROOK))) return true; break; case 9: case 7: case -9: case -7: // Bishop direction if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || (p2 == (wtm ? Piece.WBISHOP : Piece.BBISHOP))) return true; break; } } if ((m.promoteTo != Piece.EMPTY) && (d1 != 0) && (d1 == d2)) { switch (d1) { case 8: case -8: case 1: case -1: // Rook direction if ((p == Piece.WQUEEN) || (p == Piece.WROOK)) if ((d1 != 0) && (/*MoveGen.*/ nextPiece(pos, m.from, d1) == oKing)) return true; break; case 9: case 7: case -9: case -7: // Bishop direction if ((p == Piece.WQUEEN) || (p == Piece.WBISHOP)) { if ((d1 != 0) && (/*MoveGen.*/ nextPiece(pos, m.from, d1) == oKing)) return true; } break; } } if (p == Piece.WKING) { if (m.to - m.from == 2) { // O-O if (/*MoveGen.*/ nextPieceSafe(pos, m.from, -1) == oKing) return true; if (/*MoveGen.*/ nextPieceSafe(pos, m.from + 1, wtm ? 8 : -8) == oKing) return true; } else if (m.to - m.from == -2) { // O-O-O if (/*MoveGen.*/ nextPieceSafe(pos, m.from, 1) == oKing) return true; if (/*MoveGen.*/ nextPieceSafe(pos, m.from - 1, wtm ? 8 : -8) == oKing) return true; } } else if (p == Piece.WPAWN) { if (pos.getPiece(m.to) == Piece.EMPTY) { int dx = Position.getX(m.to) - Position.getX(m.from); if (dx != 0) { // en passant int epSq = m.from + dx; int d3 = BitBoard.getDirection(epSq, oKingSq); switch (d3) { case 9: case 7: case -9: case -7: if (/*MoveGen.*/ nextPiece(pos, epSq, d3) == oKing) { int p2 = /*MoveGen.*/ nextPieceSafe(pos, epSq, -d3); if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || (p2 == (wtm ? Piece.WBISHOP : Piece.BBISHOP))) return true; } break; case 1: if (/*MoveGen.*/ nextPiece(pos, Math.Max(epSq, m.from), d3) == oKing) { int p2 = /*MoveGen.*/ nextPieceSafe(pos, Math.Min(epSq, m.from), -d3); if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || (p2 == (wtm ? Piece.WROOK : Piece.BROOK))) return true; } break; case -1: if (/*MoveGen.*/ nextPiece(pos, Math.Min(epSq, m.from), d3) == oKing) { int p2 = /*MoveGen.*/ nextPieceSafe(pos, Math.Max(epSq, m.from), -d3); if ((p2 == (wtm ? Piece.WQUEEN : Piece.BQUEEN)) || (p2 == (wtm ? Piece.WROOK : Piece.BROOK))) return true; } break; } } } } return false; }