public void setPieceAt(long square, char piece) { pawns &= ~square; queens &= ~square; rooks &= ~square; bishops &= ~square; knights &= ~square; kings &= ~square; if (piece == ' ' || piece == '.') { whites &= ~square; blacks &= ~square; return; } else if (piece == Character.toLowerCase(piece)) { whites &= ~square; blacks |= square; } else { whites |= square; blacks &= ~square; } switch (Character.toLowerCase(piece)) { case 'p': pawns |= square; break; case 'q': queens |= square; break; case 'r': rooks |= square; break; case 'b': bishops |= square; break; case 'n': knights |= square; break; case 'k': kings |= square; break; } key = ZobristKey.getKey(this); setCheckFlags(); }
/** * Moves and also updates the board's zobrist key verify legality, if not legal undo move and * return false */ public boolean doMove(int move, boolean verifyCheck, boolean fillSanInfo) { if (move == Move.NONE) { return false; } // Save history saveHistory(move, fillSanInfo); // Count consecutive moves without capture or without pawn move fiftyMovesRule++; moveNumber++; // Count Ply moves boolean turn = getTurn(); int color = turn ? Color.W : Color.B; if ((flags & FLAGS_PASSANT) != 0) { // Remove passant flags: from the zobrist key key[1 - color] ^= ZobristKey.passantFile[BitboardUtils.getFile(flags & FLAGS_PASSANT)]; // and from the flags flags &= ~FLAGS_PASSANT; } if (move == Move.NULL) { // Change turn flags ^= FLAG_TURN; key[0] ^= ZobristKey.whiteMove; return true; } int fromIndex = Move.getFromIndex(move); long from = Move.getFromSquare(move); // Check if we are applying a move in the other turn if ((from & getMines()) == 0) { undoMove(); return false; } int toIndex = Move.getToIndex(move); long to = Move.getToSquare(move); long moveMask = from | to; // Move is as easy as xor with this mask (exceptions are promotions, captures and // en-passant captures) int moveType = Move.getMoveType(move); int pieceMoved = Move.getPieceMoved(move); boolean capture = Move.isCapture(move); // Is it is a capture, remove pieces in destination square if (capture) { fiftyMovesRule = 0; // En-passant pawn captures remove captured pawn, put the pawn in to int toIndexCapture = toIndex; if (moveType == Move.TYPE_PASSANT) { to = (getTurn() ? (to >>> 8) : (to << 8)); toIndexCapture += (getTurn() ? -8 : 8); } key[1 - color] ^= ZobristKey.getKeyPieceIndex(toIndexCapture, getPieceAt(to)); whites &= ~to; blacks &= ~to; pawns &= ~to; queens &= ~to; rooks &= ~to; bishops &= ~to; knights &= ~to; } // Pawn movements switch (pieceMoved) { case Piece.PAWN: fiftyMovesRule = 0; // Set new passant flags if pawn is advancing two squares (marks // the destination square where the pawn can be captured) // Set only passant flags when the other side can capture if (((from << 16) & to) != 0 && (bbAttacks.pawn[Color.W][toIndex - 8] & pawns & getOthers()) != 0) { // white flags |= (from << 8); } if (((from >>> 16) & to) != 0 && (bbAttacks.pawn[Color.B][toIndex + 8] & pawns & getOthers()) != 0) { // blask flags |= (from >>> 8); } if ((flags & FLAGS_PASSANT) != 0) { key[color] ^= ZobristKey.passantFile[BitboardUtils.getFile(flags & FLAGS_PASSANT)]; } if (moveType == Move.TYPE_PROMOTION_QUEEN || moveType == Move.TYPE_PROMOTION_KNIGHT || moveType == Move.TYPE_PROMOTION_BISHOP || moveType == Move.TYPE_PROMOTION_ROOK) { // Promotions: // change // the piece pawns &= ~from; key[color] ^= ZobristKey.pawn[color][fromIndex]; switch (moveType) { case Move.TYPE_PROMOTION_QUEEN: queens |= to; key[color] ^= ZobristKey.queen[color][toIndex]; break; case Move.TYPE_PROMOTION_KNIGHT: knights |= to; key[color] ^= ZobristKey.knight[color][toIndex]; break; case Move.TYPE_PROMOTION_BISHOP: bishops |= to; key[color] ^= ZobristKey.bishop[color][toIndex]; break; case Move.TYPE_PROMOTION_ROOK: rooks |= to; key[color] ^= ZobristKey.rook[color][toIndex]; break; } } else { pawns ^= moveMask; key[color] ^= ZobristKey.pawn[color][fromIndex] ^ ZobristKey.pawn[color][toIndex]; } break; case Piece.ROOK: rooks ^= moveMask; key[color] ^= ZobristKey.rook[color][fromIndex] ^ ZobristKey.rook[color][toIndex]; break; case Piece.BISHOP: bishops ^= moveMask; key[color] ^= ZobristKey.bishop[color][fromIndex] ^ ZobristKey.bishop[color][toIndex]; break; case Piece.KNIGHT: knights ^= moveMask; key[color] ^= ZobristKey.knight[color][fromIndex] ^ ZobristKey.knight[color][toIndex]; break; case Piece.QUEEN: queens ^= moveMask; key[color] ^= ZobristKey.queen[color][fromIndex] ^ ZobristKey.queen[color][toIndex]; break; case Piece.KING: // if castling, moves rooks too if (moveType == Move.TYPE_KINGSIDE_CASTLING || moveType == Move.TYPE_QUEENSIDE_CASTLING) { // {White Kingside, White Queenside, Black Kingside, Black Queenside} int j = (color << 1) + (moveType == Move.TYPE_QUEENSIDE_CASTLING ? 1 : 0); toIndex = CASTLING_KING_DESTINY_INDEX[j]; int originRookIndex = BitboardUtils.square2Index(castlingRooks[j]); int destinyRookIndex = CASTLING_ROOK_DESTINY_INDEX[j]; // Recalculate move mask for chess960 castlings moveMask = from ^ (1L << toIndex); long rookMoveMask = (1L << originRookIndex) ^ (1L << destinyRookIndex); key[color] ^= ZobristKey.rook[color][originRookIndex] ^ ZobristKey.rook[color][destinyRookIndex]; if (getTurn()) { whites ^= rookMoveMask; } else { blacks ^= rookMoveMask; } rooks ^= rookMoveMask; } kings ^= moveMask; key[color] ^= ZobristKey.king[color][fromIndex] ^ ZobristKey.king[color][toIndex]; break; } // Move pieces in colour fields if (getTurn()) { whites ^= moveMask; } else { blacks ^= moveMask; } // Tests to disable castling if ((flags & FLAG_WHITE_KINGSIDE_CASTLING) != 0 && // ((turn && pieceMoved == Piece.KING) || from == castlingRooks[0] || to == castlingRooks[0])) { flags &= ~FLAG_WHITE_KINGSIDE_CASTLING; key[0] ^= ZobristKey.whiteKingSideCastling; } if ((flags & FLAG_WHITE_QUEENSIDE_CASTLING) != 0 && // ((turn && pieceMoved == Piece.KING) || from == castlingRooks[1] || to == castlingRooks[1])) { flags &= ~FLAG_WHITE_QUEENSIDE_CASTLING; key[0] ^= ZobristKey.whiteQueenSideCastling; } if ((flags & FLAG_BLACK_KINGSIDE_CASTLING) != 0 && // ((!turn && pieceMoved == Piece.KING) || from == castlingRooks[2] || to == castlingRooks[2])) { flags &= ~FLAG_BLACK_KINGSIDE_CASTLING; key[1] ^= ZobristKey.blackKingSideCastling; } if ((flags & FLAG_BLACK_QUEENSIDE_CASTLING) != 0 && // ((!turn && pieceMoved == Piece.KING) || from == castlingRooks[3] || to == castlingRooks[3])) { flags &= ~FLAG_BLACK_QUEENSIDE_CASTLING; key[1] ^= ZobristKey.blackQueenSideCastling; } // Change turn flags ^= FLAG_TURN; key[0] ^= ZobristKey.whiteMove; if (verifyCheck) { if (isValid()) { setCheckFlags(); if (fillSanInfo) { if (isMate()) { // Append # when mate movesSan.put(moveNumber - 1, movesSan.get(moveNumber - 1).replace("+", "#")); } } } else { undoMove(); return false; } } else { // Trust move check flag if (Move.isCheck(move)) { flags |= FLAG_CHECK; } else { flags &= ~FLAG_CHECK; } } return true; }
/** Sets fen without destroying move history. If lastMove = null destroy the move history */ public void setFenMove(String fen, String lastMove) { long tmpWhites = 0; long tmpBlacks = 0; long tmpPawns = 0; long tmpRooks = 0; long tmpQueens = 0; long tmpBishops = 0; long tmpKnights = 0; long tmpKings = 0; long tmpFlags; int tmpFiftyMovesRule = 0; long tmpCastlingRooks[] = {0, 0, 0, 0}; int fenMoveNumber = 0; int i = 0; long j = Square.A8; String[] tokens = fen.split("[ \\t\\n\\x0B\\f\\r]+"); String board = tokens[0]; while ((i < board.length()) && (j != 0)) { char p = board.charAt(i++); if (p != '/') { int number = 0; try { number = Integer.parseInt(String.valueOf(p)); } catch (Exception ignored) { } for (int k = 0; k < (number == 0 ? 1 : number); k++) { tmpWhites = (tmpWhites & ~j) | ((number == 0) && (p == Character.toUpperCase(p)) ? j : 0); tmpBlacks = (tmpBlacks & ~j) | ((number == 0) && (p == Character.toLowerCase(p)) ? j : 0); tmpPawns = (tmpPawns & ~j) | (Character.toUpperCase(p) == 'P' ? j : 0); tmpRooks = (tmpRooks & ~j) | (Character.toUpperCase(p) == 'R' ? j : 0); tmpQueens = (tmpQueens & ~j) | (Character.toUpperCase(p) == 'Q' ? j : 0); tmpBishops = (tmpBishops & ~j) | (Character.toUpperCase(p) == 'B' ? j : 0); tmpKnights = (tmpKnights & ~j) | (Character.toUpperCase(p) == 'N' ? j : 0); tmpKings = (tmpKings & ~j) | (Character.toUpperCase(p) == 'K' ? j : 0); j >>>= 1; if (j == 0) { break; // security } } } } // Now the rest ... String turn = tokens[1]; tmpFlags = 0; if ("b".equals(turn)) { tmpFlags |= FLAG_TURN; } if (tokens.length > 2) { // Set castling rights supporting XFEN to disambiguate positions in Chess960 String castlings = tokens[2]; chess960 = false; // Squares to the sides of the kings {White Kingside, White Queenside, Black Kingside, Black // Queenside} long whiteKingLateralSquares[] = { BitboardUtils.b_d & ((tmpKings & tmpWhites) - 1), BitboardUtils.b_d & ~(((tmpKings & tmpWhites) - 1) | tmpKings & tmpWhites), BitboardUtils.b_u & ((tmpKings & tmpBlacks) - 1), BitboardUtils.b_u & ~(((tmpKings & tmpBlacks) - 1) | tmpKings & tmpBlacks) }; // Squares where we can find a castling rook long possibleCastlingRookSquares[] = {0, 0, 0, 0}; for (int k = 0; k < castlings.length(); k++) { char c = castlings.charAt(k); switch (c) { case 'K': possibleCastlingRookSquares[0] = whiteKingLateralSquares[0]; break; case 'Q': possibleCastlingRookSquares[1] = whiteKingLateralSquares[1]; break; case 'k': possibleCastlingRookSquares[2] = whiteKingLateralSquares[2]; break; case 'q': possibleCastlingRookSquares[3] = whiteKingLateralSquares[3]; break; default: // Shredder-FEN receives the name of the file where the castling rook is int whiteFile = "ABCDEFGH".indexOf(c); int blackFile = "abcdefgh".indexOf(c); if (whiteFile >= 0) { long rookSquare = BitboardUtils.b_d & BitboardUtils.FILE[whiteFile]; if ((rookSquare & whiteKingLateralSquares[0]) != 0) { possibleCastlingRookSquares[0] = rookSquare; } else if ((rookSquare & whiteKingLateralSquares[1]) != 0) { possibleCastlingRookSquares[1] = rookSquare; } } else if (blackFile >= 0) { long rookSquare = BitboardUtils.b_u & BitboardUtils.FILE[blackFile]; if ((rookSquare & whiteKingLateralSquares[2]) != 0) { possibleCastlingRookSquares[2] = rookSquare; } else if ((rookSquare & whiteKingLateralSquares[3]) != 0) { possibleCastlingRookSquares[3] = rookSquare; } } } } // Now store the squares of the castling rooks tmpCastlingRooks[0] = BitboardUtils.lsb(tmpRooks & tmpWhites & possibleCastlingRookSquares[0]); tmpCastlingRooks[1] = BitboardUtils.msb(tmpRooks & tmpWhites & possibleCastlingRookSquares[1]); tmpCastlingRooks[2] = BitboardUtils.lsb(tmpRooks & tmpBlacks & possibleCastlingRookSquares[2]); tmpCastlingRooks[3] = BitboardUtils.msb(tmpRooks & tmpBlacks & possibleCastlingRookSquares[3]); // Set the castling flags and detect Chess960 if (tmpCastlingRooks[0] != 0) { tmpFlags |= FLAG_WHITE_KINGSIDE_CASTLING; if ((tmpWhites & tmpKings) != 1L << 3 || tmpCastlingRooks[0] != 1L) { chess960 = true; } } if (tmpCastlingRooks[1] != 0) { tmpFlags |= FLAG_WHITE_QUEENSIDE_CASTLING; if ((tmpWhites & tmpKings) != 1L << 3 || tmpCastlingRooks[1] != 1L << 7) { chess960 = true; } } if (tmpCastlingRooks[2] != 0) { tmpFlags |= FLAG_BLACK_KINGSIDE_CASTLING; if ((tmpBlacks & tmpKings) != 1L << 59 || tmpCastlingRooks[2] != 1L << 56) { chess960 = true; } } if (tmpCastlingRooks[3] != 0) { tmpFlags |= FLAG_BLACK_QUEENSIDE_CASTLING; if ((tmpBlacks & tmpKings) != 1L << 59 || tmpCastlingRooks[3] != 1L << 63) { chess960 = true; } } // END FEN castlings if (tokens.length > 3) { String passant = tokens[3]; tmpFlags |= FLAGS_PASSANT & BitboardUtils.algebraic2Square(passant); if (tokens.length > 4) { try { tmpFiftyMovesRule = Integer.parseInt(tokens[4]); } catch (Exception e) { tmpFiftyMovesRule = 0; } if (tokens.length > 5) { String moveNumberString = tokens[5]; int aux = Integer.parseInt(moveNumberString); fenMoveNumber = ((aux > 0 ? aux - 1 : aux) << 1) + ((tmpFlags & FLAG_TURN) == 0 ? 0 : 1); if (fenMoveNumber < 0) { fenMoveNumber = 0; } } } } } // try to apply the last move to see if we are advancing or undoing moves if ((moveNumber + 1) == fenMoveNumber && lastMove != null) { doMove(Move.getFromString(this, lastMove, true)); } else if (fenMoveNumber < moveNumber) { for (int k = moveNumber; k > fenMoveNumber; k--) { undoMove(); } } // Check if board changed or if we can keep the history if (whites != tmpWhites // || blacks != tmpBlacks // || pawns != tmpPawns // || rooks != tmpRooks // || queens != tmpQueens // || bishops != tmpBishops // || knights != tmpKnights // || kings != tmpKings // || (flags & FLAG_TURN) != (tmpFlags & FLAG_TURN)) { // board reset movesSan.clear(); initialFen = fen; initialMoveNumber = fenMoveNumber; moveNumber = fenMoveNumber; outBookMove = Integer.MAX_VALUE; whites = tmpWhites; blacks = tmpBlacks; pawns = tmpPawns; rooks = tmpRooks; queens = tmpQueens; bishops = tmpBishops; knights = tmpKnights; kings = tmpKings; fiftyMovesRule = tmpFiftyMovesRule; // Flags are not completed till verify, so skip checking flags = tmpFlags; castlingRooks[0] = tmpCastlingRooks[0]; castlingRooks[1] = tmpCastlingRooks[1]; castlingRooks[2] = tmpCastlingRooks[2]; castlingRooks[3] = tmpCastlingRooks[3]; // Set zobrist key and check flags key = ZobristKey.getKey(this); setCheckFlags(); // and save history resetHistory(); saveHistory(0, false); } else { if (moveNumber < outBookMove) { outBookMove = Integer.MAX_VALUE; } } }