/// <summary> /// Checks if it's the two-squares move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public bool IsTwoSquaresMove(Board board, int from, int to) { return Board.Rank(from) == 1 && Board.Rank(to) == 3 && Board.File(from) == Board.File(to) &&// the same file board[from + Board.SideSquareNo] == null && board[from + (Board.SideSquareNo << 1)] == null;// the first and second squares are empty }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return base.MightMove(board, from, to) && Math.Abs(Board.File(from) - Board.File(to)) == Math.Abs(Board.Rank(from) - Board.Rank(to)) &&// it's a diagonal move board.IsPathClear(from, to);// the path is clear }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { // the first condition for a piece to be able to move // is that starting square and the ending square be different // and the ending square be empty or opposite piece return from != to && !(board[to] is BlackPiece); }
/// <summary> /// Checks if it's the en passant move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public bool IsEnPassantCaptureMove(Board board, int from, int to) { return board.Status.EnPassantTarget != null &&// there is a en passant target to == board.Status.EnPassantTarget.Value && Board.Rank(from) == 4 && Board.Rank(board.Status.EnPassantTarget.Value) == 5 && Math.Abs(Board.File(from) - Board.File(to)) == 1;// it's a diagonal move }
/// <summary> /// Takes back the move, it doesn't check if it's a valid move /// </summary> /// <param name="board">The board</param> internal override void TakeBack(Board board) { board.Status = before;// set the board status to the before board status board[from] = board[to];// put the piece on the starting square board[Target] = capture;// put back the en passant target board[to] = null;// empty the ending square }
/// <summary> /// Makes the move, it doesn't check if it's a valid move /// </summary> /// <param name="board">The board</param> internal override void Make(Board board) { capture = board[Target];// set the capture as the en passant target board[Target] = null;// empty the en passant target square board[to] = board[from];// put the piece on the ending square board[from] = null;// empty the starting square board.Status = after;// set the board status to the after board status }
/// <summary> /// Verifies if the piece attacks the "to" square, on this board, from the "from" square. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool Attacks(Board board, int from, int to) { // castling is not considered an attack // so the king attacks the squares around him return base.MightMove(board, from, to) && Math.Abs(Board.Rank(from) - Board.Rank(to)) <= 1 && Math.Abs(Board.File(from) - Board.File(to)) <= 1; }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return base.MightMove(board, from, to) && Board.File(from) != Board.File(to) &&// the files are different Board.Rank(from) != Board.Rank(to) &&// the ranks are different (Math.Abs(Board.File(from) - Board.File(to)) + Math.Abs(Board.Rank(from) - Board.Rank(to))) == 3;// the rank difference plus file difference must be 3 }
/// <summary> /// Verifies if the king can castle short. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public bool CanCastleShort(Board board, int from, int to) { return ( from == Board.E1 && to == Board.G1 && board.Status.WhiteCouldCastleShort &&//check if the king or the rook didn't already move board.IsPathClear(Board.E1, Board.H1) &&// check if the path is clear !board.IsAttackedByBlack(Board.E1) && !board.IsAttackedByBlack(Board.F1) && !board.IsAttackedByBlack(Board.G1)// check if the squares traversed by king are not attacked ); }
/// <summary> /// Verifies if the king can castle long. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public bool CanCastleLong(Board board, int from, int to) { return ( from == Board.E8 && to == Board.C8 && board.Status.BlackCouldCastleLong &&//check if the king or the rook didn't already move board.IsPathClear(Board.A8, Board.E8) &&// check if the path is clear !board.IsAttackedByWhite(Board.E8) && !board.IsAttackedByWhite(Board.D8) && !board.IsAttackedByWhite(Board.C8)// check if the squares traversed by king are not attacked ); }
/// <summary> /// Verifies if the piece attacks the "to" square, on this board, from the "from" square. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool Attacks(Board board, int from, int to) { // a pawn attacks one square in front diagonaly // and only if it's an empty square or an opposite side piece return Board.Rank(to) == Board.Rank(from) + 1 &&// it's on the front row Math.Abs(Board.File(to) - Board.File(from)) == 1 &&// it's a diagonal move !(board[to] is BlackPiece);// it's not occupied by a same side piece }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return base.MightMove(board, from, to) && ( Board.File(from) == Board.File(to) ||// the same file Board.Rank(from) == Board.Rank(to)// the same rank ) && board.IsPathClear(from, to);// the path is clear }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return base.MightMove(board, from, to) && ( (Math.Abs(Board.Rank(from) - Board.Rank(to)) <= 1 && Math.Abs(Board.File(from) - Board.File(to)) <= 1) ||// the king can move one square CanCastleLong(board, from, to) ||// the king can castle long CanCastleShort(board, from, to)// the king can castle short ); }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return base.MightMove(board, from, to) && ( Board.File(from) == Board.File(to) ||// the same file Board.Rank(from) == Board.Rank(to) ||// the same rank Math.Abs(Board.File(from) - Board.File(to)) == Math.Abs(Board.Rank(from) - Board.Rank(to))// it's a diagonal move ) && board.IsPathClear(from, to); }
/// <summary> /// Generates the move. /// In this class, the move is not verified if it puts its own king in check. /// This is implemented in the BlackPiece and WhitePiece subclasses. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> internal virtual Move GenerateMove(Board board, int from, int to) { if (!MightMove(board, from, to)) { return null; } Move move = new Move(board.Status, from, to); move.ChangeSideToMove();// change side to move move.SetEnPassantTarget(null);// reset the en passant target if (board[to] == null)// if there is no capture { move.IncrementPly();// increment the ply } else// if there is a capture { move.ResetPly();// reset the ply // if there this moves captures a rook // and the ending squares is one of the board corners // reset castling availability if (board[to] is WhiteRook) { if (to == Board.A1) { move.MakeWhiteLongCastlingUnavail(); } else if (to == Board.H1) { move.MakeWhiteShortCastlingUnavail(); } } else if (board[to] is BlackRook) { if (to == Board.A8) { move.MakeBlackLongCastlingUnavail(); } else if (to == Board.H8) { move.MakeBlackShortCastlingUnavail(); } } } return move; }
/// <summary> /// Makes the move, it doesn't check if it's a valid move. /// The capture must be set before making the move, othewise it throws InvalidOperationException. /// </summary> /// <param name="board">The board</param> internal override void Make(Board board) { if (promotionPiece != null) { // set the ending square board[to] = promotionPiece; } else { // if the promotion was not set throw an exception throw new InvalidOperationException(Resources.NoPromotionMsg); } promotedPiece = board[from];// set the promoted piece board[from] = null;// empty the starting square board.Status = after;// set the board status to the after board status }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return // a pawn can move one square in front to an empty square ( to == from + Board.SideSquareNo &&// it's the front square board[to] == null// it's an empty square ) || // a pawn can move one square in front diagonally if there is an opposite side piece ( Board.Rank(to) == Board.Rank(from) + 1 &&// it's on the front row Math.Abs(Board.File(to) - Board.File(from)) == 1 &&// it's a diagonal move board[to] is WhitePiece// it's an opposite side piece ) || IsTwoSquaresMove(board, from, to) ||// it's the two-squares move IsEnPassantCaptureMove(board, from, to);// it's the en passant move }
/// <summary> /// Generates the move. /// Adds check verification to move generation, /// returns null if its own king will be in check. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> internal override Move GenerateMove(Board board, int from, int to) { Move move = base.GenerateMove(board, from, to); if (move != null) { // verify for king in check move.Make(board); bool result = !board.WhiteKingInCheck(); move.TakeBack(board); return result ? move : null; } else { return null; } }
/// <summary> /// Generates the move. /// Adds check verification to move generation, /// returns null if its own king will be in check. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> internal override Move GenerateMove(Board board, int from, int to) { Move move = base.GenerateMove(board, from, to); if (move != null) { move.IncrementMoves();// the number of moves is incremented after Black moves // verify for king in check move.Make(board); bool result = !board.BlackKingInCheck(); move.TakeBack(board); return result ? move : null; } else { return null; } }
/// <summary> /// Generates the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> internal override Move GenerateMove(Board board, int from, int to) { Move move = base.GenerateMove(board, from, to); // just reset castling availability if (move != null) { if (from == Board.A1) { move.MakeWhiteLongCastlingUnavail(); } else if (from == Board.H1) { move.MakeWhiteShortCastlingUnavail(); } return move; } else { return null; } }
/// <summary> /// Generates the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> internal override Move GenerateMove(Board board, int from, int to) { Move move; // if it's a castling move // CanCastleLong already verifies if the king will // end in check so we don't have to verify it again if (CanCastleLong(board, from, to)) { move = new CastlingMove(board.Status, Board.E1, Board.C1, new Move(board.Status, Board.A1, Board.D1)); move.ChangeSideToMove();// change side to move move.MakeWhiteLongCastlingUnavail();// reset castling availability move.MakeWhiteShortCastlingUnavail();// reset castling availability move.SetEnPassantTarget(null);// reset en passant target move.IncrementPly();// increment the ply return move; } // if it's a castling move // CanCastleShort already verifies if the king will // end in check so we don't have to verify it again if (CanCastleShort(board, from, to)) { move = new CastlingMove(board.Status, Board.E1, Board.G1, new Move(board.Status, Board.H1, Board.F1)); move.ChangeSideToMove();// change side to move move.MakeWhiteLongCastlingUnavail();// reset castling availability move.MakeWhiteShortCastlingUnavail();// reset castling availability move.SetEnPassantTarget(null);// reset en passant target move.IncrementPly();// increment the ply return move; } // if it's a regular move // just reset castling availability move = base.GenerateMove(board, from, to); if (move != null) { move.MakeWhiteLongCastlingUnavail(); move.MakeWhiteShortCastlingUnavail(); return move; } else { return null; } }
/// <summary> /// Gets the board FEN (Forsyth-Edwards notation). /// </summary> /// <param name="board">The board</param> /// <returns></returns> public static string GetFEN(Board board) { // the board must not be null if (board == null) { throw new ArgumentNullException("board", "Resources.NullBoardMsg"); } StringBuilder sb = new StringBuilder(80); int emptySqNo = 0; for (int sqIndex = 0; sqIndex < Board.SquareNo; sqIndex++) { // if the end of row is reached if (sqIndex % Board.SideSquareNo == 0 && sqIndex > 0) { // write the number of empty squares (if any) and reset it if (emptySqNo != 0) { sb.Append(emptySqNo.ToString(CultureInfo.InvariantCulture)); emptySqNo = 0; } sb.Append('/');// write '/' } //if there is a piece on this square if (board[sqIndex] != null) { // write the number of empty squares (if any) and reset it if (emptySqNo != 0) { sb.Append(emptySqNo.ToString(CultureInfo.InvariantCulture)); emptySqNo = 0; } sb.Append(pieceTypeToCharConversion[board[sqIndex].GetType()]);// write piece char representation } // if the square is empty else { emptySqNo++;// increment the number of empty squares } } // write the number of empty squares (if any) if (emptySqNo != 0) { sb.Append(emptySqNo.ToString(CultureInfo.InvariantCulture)); } sb.Append(' ').Append(board.Status.WhiteTurn ? 'w' : 'b');// write the side to move char // write castling availability chars if (board.Status.WhiteCouldCastleLong || board.Status.WhiteCouldCastleShort || board.Status.BlackCouldCastleLong || board.Status.BlackCouldCastleShort) { sb.Append(' '); if (board.Status.WhiteCouldCastleShort) { sb.Append('K'); } if (board.Status.WhiteCouldCastleLong) { sb.Append('Q'); } if (board.Status.BlackCouldCastleShort) { sb.Append('k'); } if (board.Status.BlackCouldCastleLong) { sb.Append('q'); } } else { sb.Append(' ').Append('-'); } // write en passant target notation sb.Append(' ').Append((board.Status.EnPassantTarget != null ? GetNotation(board.Status.EnPassantTarget.Value) : "-")); // write ply sb.Append(' ').Append(board.Status.Ply.ToString(CultureInfo.InvariantCulture)); // write move number sb.Append(' ').Append(board.Status.Moves.ToString(CultureInfo.InvariantCulture)); return sb.ToString(); }
/// <summary> /// Takes back the move, it doesn't check if it's a valid move. /// </summary> /// <param name="board">The board</param> internal virtual void TakeBack(Board board) { board.Status = before;// set the board status to the before board status board[from] = board[to];// put the piece on starting square board[to] = capture;// put back the capture }
/// <summary> /// Computes the board hash using Zobrist method. /// </summary> /// <param name="board">The board</param> /// <returns></returns> public static int GetHash(Board board) { // the board must not be null if (board == null) { throw new ArgumentNullException("board", "Resources.NullBoardMsg"); } long hash = 0; // loop through the squares for (int sqIndex = 0; sqIndex < Board.SquareNo; sqIndex++) { // if the square is not empty if (board[sqIndex] != null) { // XOR the Zobrist key which coresponds to type of piece and for this square // to find the key index, offset the key index of the type by the square number hash ^= zobristKeys[pieceZobristIndexTable[board[sqIndex].GetType()] + sqIndex]; } } // if White is to move, XOR the corresponding Zobrist key if (board.Status.WhiteTurn) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo]; } // if the Kings could castle, the corresponding Zobrist keys if (board.Status.WhiteCouldCastleLong) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 1]; } if (board.Status.WhiteCouldCastleShort) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 2]; } if (board.Status.BlackCouldCastleLong) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 3]; } if (board.Status.BlackCouldCastleShort) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 4]; } // if there is an en passant target, XOR the corresponding Zobrist key if (board.Status.EnPassantTarget != null) { // the en passant targets have rank 2 or 5 if (Board.Rank(board.Status.EnPassantTarget.Value) == 2) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 5 + Board.File(board.Status.EnPassantTarget.Value)]; } else if (Board.Rank(board.Status.EnPassantTarget.Value) == 5) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 13 + Board.File(board.Status.EnPassantTarget.Value)]; } } // XOR the first 4 bytes with the last 4 bytes to return an 32-bit integer return (int)((hash & 0xFFFFFFFF) ^ (hash >> 32)); }
/// <summary> /// Gets a board from its FEN (Forsyth-Edwards notation). /// Throws ArgumentException if it's not a valid position. /// </summary> /// <param name="fen">The FEN string</param> /// <returns></returns> public static Board GetFENBoard(string fen) { try { // the FEN string must not be null if (fen == null) { throw new ArgumentNullException("fen", "Resources.NullFENMsg"); } Piece[] squares = new Piece[Board.SquareNo]; BoardStatus status = new BoardStatus(); Piece piece; int index = 0, pos = 0; while (fen[index] != ' ') { // get the piece from its char representation piece = GetPieceFromChar(fen[index]); // if there is a piece if (piece != null) { squares[pos++] = piece;// put the piece on board } // if there is a number else if (fen[index] >= '1' && fen[index] <= '8') { pos += fen[index] - '0';// skip empty squares } // if there another char than '/' throw exception else if (fen[index] != '/') { throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } index++; } index++; // set side to move if (fen[index] == 'w') { status.WhiteTurn = true; } else if (fen[index] == 'b') { status.BlackTurn = true; } else { throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } index += 2; // set castling availability if (fen[index] == '-') { index++; } else { do { switch (fen[index]) { case 'K': status.WhiteCouldCastleShort = true; break; case 'Q': status.WhiteCouldCastleLong = true; break; case 'k': status.BlackCouldCastleShort = true; break; case 'q': status.BlackCouldCastleLong = true; break; default: throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } } while (fen[++index] != ' '); } index++; // set en passant target if (fen[index] == '-') { status.EnPassantTarget = null; index++; } else { // checks if the square notation is correct if (fen[index] < 'a' || fen[index] > 'h' || fen[index + 1] < '1' || fen[index + 1] > '8') { throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } status.EnPassantTarget = GetPosition(fen[index], fen[index + 1]); index += 2; } index++; // set ply try { status.Ply = Int32.Parse(fen.Substring(index, fen.IndexOf(' ', index) - index), CultureInfo.InvariantCulture); } catch { throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } index = fen.IndexOf(' ', index) + 1; // set move number try { status.Moves = Int32.Parse(fen.Substring(index), CultureInfo.InvariantCulture); } catch { throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } Board board = new Board(squares, status); // verfies if the board is in valid state, throws ArgumentException if it's not VerifyState(board); return board; } // if the FEN string was too short catch (IndexOutOfRangeException) { throw new ArgumentException("Resources.IllegalFENFormatMsg", "fen"); } }
/// <summary> /// Verifies if the piece attacks the "to" square, on this board, from the "from" square. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public virtual bool Attacks(Board board, int from, int to) { return MightMove(board, from, to);// usually a piece attacks a square if it can move there }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public abstract bool MightMove(Board board, int from, int to);
/// <summary> /// Verifies if the board is in a valid state, throws ArgumentException if it's not. /// </summary> /// <param name="board">The board</param> public static void VerifyState(Board board) { // the board must not be null if (board == null) { throw new ArgumentNullException("board", "Resources.NullBoardMsg"); } // hastable to remember the number of each piece type // a pair consists in a piece type and the number of pieces on this board Dictionary<Type, int> pieceNo = new Dictionary<Type, int>(Piece.TypesNo); // initialize the values with 0 foreach (Type type in pieceZobristIndexTable.Keys) { pieceNo[type] = 0; } // loop through the squares for (int sqIndex = 0; sqIndex < Board.SquareNo; sqIndex++) { if (board[sqIndex] != null) { // increment the number of this piece type Type type = board[sqIndex].GetType(); pieceNo[type] = pieceNo[type] + 1; // check if the White Pawn has rank 1 if (type == typeof(WhitePawn) && Utils.GetRankNotation(sqIndex) == '1') { throw new ArgumentException("Resources.WhitePawnRank1Msg", "board"); } // check if the Black Pawn has rank 8 if (type == typeof(BlackPawn) && Utils.GetRankNotation(sqIndex) == '8') { throw new ArgumentException("Resources.BlackPawnRank8Msg", "board"); } } } // if there are too many White pieces // check if the number of Pawns // plus the number of extra Queens, Rooks, Bishops, Knights // (which can result only by promotion) exceeds 8 if ((pieceNo[typeof(WhitePawn)] + Math.Max(pieceNo[typeof(WhiteQueen)] - 1, 0) + Math.Max(pieceNo[typeof(WhiteRook)] - 2, 0) + Math.Max(pieceNo[typeof(WhiteBishop)] - 2, 0) + Math.Max(pieceNo[typeof(WhiteKnight)] - 2, 0)) > 8) { throw new ArgumentException("Resources.TooManyWhitePiecesMsg", "board"); } // if there are too many Black pieces // check if the number of Pawns // plus the number of extra Queens, Rooks, Bishops, Knights // (which can result only by promotion) exceeds 8 if ((pieceNo[typeof(BlackPawn)] + Math.Max(pieceNo[typeof(BlackQueen)] - 1, 0) + Math.Max(pieceNo[typeof(BlackRook)] - 2, 0) + Math.Max(pieceNo[typeof(BlackBishop)] - 2, 0) + Math.Max(pieceNo[typeof(BlackKnight)] - 2, 0)) > 8) { throw new ArgumentException("Resources.TooManyBlackPiecesMsg", "board"); } // check for White King if (pieceNo[typeof(WhiteKing)] > 1) { throw new ArgumentException("Resources.MoreThanOneWhiteKingMsg", "board"); } if (pieceNo[typeof(WhiteKing)] < 1) { throw new ArgumentException("Resources.NoWhiteKingMsg", "board"); } // check for Black King if (pieceNo[typeof(BlackKing)] > 1) { throw new ArgumentException("Resources.MoreThanOneBlackKingMsg", "board"); } if (pieceNo[typeof(BlackKing)] < 1) { throw new ArgumentException("Resources.NoBlackKingMsg", "board"); } // check if the King and the Rook are in place for castling availability if (board.Status.WhiteCouldCastleShort && !(board[Board.E1] is WhiteKing && board[Board.H1] is WhiteRook)) { throw new ArgumentException("Resources.IllegalWhiteKingShortCastlingMsg", "board"); } // check if the King and the Rook are in place for castling availability if (board.Status.WhiteCouldCastleLong && !(board[Board.E1] is WhiteKing && board[Board.A1] is WhiteRook)) { throw new ArgumentException("Resources.IllegalWhiteKingLongCastlingMsg", "board"); } // check if the King and the Rook are in place for castling availability if (board.Status.BlackCouldCastleShort && !(board[Board.E8] is BlackKing && board[Board.H8] is BlackRook)) { throw new ArgumentException("Resources.IllegalBlackKingShortCastlingMsg", "board"); } // check if the King and the Rook are in place for castling availability if (board.Status.BlackCouldCastleLong && !(board[Board.E8] is BlackKing && board[Board.A8] is BlackRook)) { throw new ArgumentException("Resources.IllegalBlackKingLongCastlingMsg", "board"); } // check the en passant target if ( board.Status.EnPassantTarget != null && !( ( Board.Rank(board.Status.EnPassantTarget.Value) == 5 && board.Status.BlackTurn &&// the rank of en passant target is correct board[board.Status.EnPassantTarget.Value - Board.SideSquareNo] is WhitePawn &&// there is a pawn in front of the target board[board.Status.EnPassantTarget.Value] == null && board[board.Status.EnPassantTarget.Value + Board.SideSquareNo] == null// the en passant target square and the one behind are empty ) || ( Board.Rank(board.Status.EnPassantTarget.Value) == 2 && board.Status.WhiteTurn &&// the rank of en passant target is correct board[board.Status.EnPassantTarget.Value + Board.SideSquareNo] is BlackPawn &&// there is a pawn in front of the target board[board.Status.EnPassantTarget.Value] == null && board[board.Status.EnPassantTarget.Value - Board.SideSquareNo] == null// the en passant target square and the one behind are empty ) ) ) { throw new ArgumentException("Resources.IllegalEPTargetMsg", "board"); } // check if ply >= 0 if (board.Status.Ply < 0) { throw new ArgumentException("Resources.NonNegativePlyMsg", "board"); } // check if move number >= 1 if (board.Status.Moves < 1) { throw new ArgumentException("Resources.PositiveMoveNoMsg", "board"); } // check if the side which is not to move is in check if ((board.Status.WhiteTurn && board.BlackKingInCheck()) || (board.Status.BlackTurn && board.WhiteKingInCheck())) { throw new ArgumentException("Resources.NoSideNotToMoveCheckMsg", "board"); } }
/// <summary> /// Generates the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> internal override Move GenerateMove(Board board, int from, int to) { Move move; // if it's an en passant capture if (IsEnPassantCaptureMove(board, from, to)) { move = new EnPassantCaptureMove(board.Status, from, to); move.ChangeSideToMove();// change the side to move move.SetEnPassantTarget(null);// reset the en passant target move.ResetPly();// reset the ply move.IncrementMoves();// increment the number of moves // we need to verify for check move.Make(board); bool result = !board.BlackKingInCheck(); move.TakeBack(board); return result ? move : null; } move = base.GenerateMove(board, from, to); if (move != null) { move.ResetPly();// reset the ply // if it's the two-squares move if (IsTwoSquaresMove(board, from, to)) { // set the en passant target move.SetEnPassantTarget(to - Board.SideSquareNo); } // if it's a promotion if (Board.Rank(to) == Board.SideSquareNo - 1) { // we don't need to verify for check again // so the promotion delegate will not be triggered // later we will change the promotion type as needed move = new PromotionMove(board.Status, from, to); move.ChangeSideToMove();// change the side to move move.SetEnPassantTarget(null);// reset the en passant target move.ResetPly();// reset the ply move.IncrementMoves();// increment the number of moves (move as PromotionMove).SetCapture(board[to]);// the capture information is set } return move; } else { return null; } }
/// <summary> /// Takes back the move, it doesn't check if it's a valid move. /// </summary> /// <param name="board">The board</param> internal override void TakeBack(Board board) { board.Status = before;// set the board status to the before board status board[from] = promotedPiece;// put the promoted piece on starting square board[to] = capture;// put back the capture }