/// <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> /// Gets the end of the SAN (standard algebraic notation) for a move - promotion and check/checkmate representation - after the move is made /// without checking the status of the game or if it's a valid move. /// </summary> /// <param name="game">The game</param> /// <param name="move">The move</param> /// <returns></returns> public static string GetSANEnd(Game game, Move move) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", "Resources.NullGameMsg"); } // the move must not be null if (move == null) { throw new ArgumentNullException("move", "Resources.NullMoveMsg"); } StringBuilder sb = new StringBuilder(3); if (move is PromotionMove) { // add promotion notation sb.Append('=').Append(pieceTypeToUpperCharConversion[(move as PromotionMove).PromotionType]); } if (game.Status == GameStatus.Check) { // add check representation sb.Append('+'); } else if (game.Status == GameStatus.Checkmate) { // add checkmate representation sb.Append('#'); } return sb.ToString(); }
/// <summary> /// Makes a move. /// If the move is illegal or it's null, throws an Argument(Null)Exception. /// </summary> /// <param name="move">The move</param> public void Make(Move move) { // check to see if the move it's not null and it's valid if (move == null) { throw new ArgumentNullException("move", Resources.NullMoveMsg); } bool findMove = false; foreach (Move m in PossibleMoves) { if (move.From == m.From && move.To == m.To) { findMove = true; break; } } if (!findMove) { throw new ArgumentException(Resources.IllegalMoveMsg, "move"); } // build the event args CancelMoveEventArgs moveEventArgs = new CancelMoveEventArgs(move, currentBoardIndex - 1); // raise the Moving event OnMoving(moveEventArgs); // if the move was cancelled if (moveEventArgs.Cancel) { return; } // remove the moves after current board index (if any) if (!IsLast) { moveHistory.RemoveRange(currentBoardIndex, moveHistory.Count - currentBoardIndex); } // if this is a promotion, the promotion is not set and the promotion delegate is not null, call the promotion delegate if (move is PromotionMove && (move as PromotionMove).PromotionType == null && Promote != null) { (move as PromotionMove).PromotionType = Promote(); } // make the move move.Make(currentBoard); // add the current hash to the history AddHistoryHash(); // generate the possible moves GenerateMoves(); // set the status of the game SetStatus(); // increment the current board index currentBoardIndex++; // add move to history moveHistory.Add(move); // raise the Moved event OnMoved(new MoveEventArgs(move, currentBoardIndex - 1)); }
/// <summary> /// Gets the begin of the SAN (standard algebraic notation) for a move - without promotion or check/checkmate representation - before the move is made /// without checking the status of the game or if it's a valid move. /// </summary> /// <param name="game">The game</param> /// <param name="move">The move</param> /// <returns></returns> public static string GetSANBegin(Game game, Move move) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", "Resources.NullGameMsg"); } // the move must not be null if (move == null) { throw new ArgumentNullException("move", "Resources.NullMoveMsg"); } StringBuilder sb = new StringBuilder(6); //if it's a castling move if (move is CastlingMove) { if (move.To == Board.G1 || move.To == Board.G8) { // short castling sb.Append("O-O"); } else if (move.To == Board.C1 || move.To == Board.C8) { // long castling sb.Append("O-O-O"); } } else { // add piece type and disambiguation char p = pieceTypeToUpperCharConversion[game.CurrentBoard[move.From].GetType()]; if (p == 'P') { // if the pawn captures, add the starting square file if (move.HasCapture) { sb.Append(GetFileNotation(move.From)); } } else { // add piece char sb.Append(p); // add disambiguation // disambigutationList will contain starting squares that contain the same // type of piece and that can move to the same ending square as "move" List<int> disambiguationList = new List<int>(5); foreach (Move m in game.PossibleMoves) { if (m.To == move.To && m.From != move.From && game.CurrentBoard[m.From].ToString() == game.CurrentBoard[move.From].ToString()) { disambiguationList.Add(m.From); } } if (disambiguationList.Count > 0) { // see if the file is unique bool isFileUnique = true; foreach (int from in disambiguationList) { if (Board.File(move.From) == Board.File(from)) { isFileUnique = false; break; } } if (isFileUnique) { // insert file sb.Append(Utils.GetFileNotation(move.From)); } else { // see if the rank is unique bool isRankUnique = true; foreach (int from in disambiguationList) { if (Board.Rank(move.From) == Board.Rank(from)) { isRankUnique = false; break; } } if (isRankUnique) { // insert rank sb.Append(Utils.GetRankNotation(move.From)); } else { // both file and rank are not unique, insert both of them sb.Append(Utils.GetNotation(move.From)); } } } } // if there is a capture, add capture notation if (move.HasCapture) { sb.Append('x'); } // add destination square sb.Append(GetNotation(move.To)); } return sb.ToString(); }
/// <summary> /// Gets the move CAN (coordinate algebraic notation) without checking the status of the game or if it's a valid move. /// </summary> /// <param name="move">The move</param> /// <returns></returns> public static string GetCAN(Move move) { // the move must not be null if (move == null) { throw new ArgumentNullException("move", "Resources.NullMoveMsg"); } StringBuilder sb = new StringBuilder(5); // add starting square notation sb.Append(GetNotation(move.From)); // add ending square notation sb.Append(GetNotation(move.To)); // add promotion notation (if any) if (move is PromotionMove) { sb.Append(pieceTypeToLowerCharConversion[(move as PromotionMove).PromotionType]); } return sb.ToString(); }
/// <summary> /// Constructor. /// </summary> /// <param name="move"></param> /// <param name="index"></param> public CancelMoveEventArgs(Move move, int index) : base(move, index) { }
/// <summary> /// Constructor. /// </summary> /// <param name="move"></param> /// <param name="index"></param> public MoveEventArgs(Move move, int index) { this.move = move; this.index = index; }
/// <summary> /// Constructor. /// </summary> /// <param name="before">The before status</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <param name="rookMove">The rook move</param> internal CastlingMove(BoardStatus before, int from, int to, Move rookMove) : base(before, from, to) { this.rookMove = rookMove; }