/// <summary> /// Gets a move from its SAN (standard algebraic notation). /// Throws ArgumentException if it's not a valid move. /// </summary> /// <param name="game">The game</param> /// <param name="san">The SAN string</param> /// <returns></returns> public static Move GetSANMove(Game game, string san) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", "Resources.NullGameMsg"); } // the SAN string must not be null if (san == null) { throw new ArgumentNullException("san", "Resources.NullSANMsg"); } Move move = null; // if it's a short castling move if (san == "O-O") { foreach (Move m in game.PossibleMoves) { if (m is CastlingMove && (m.To == Board.G1 || m.To == Board.G8)) { move = m; break; } } } // if it's a long castling move else if (san == "O-O-O") { foreach (Move m in game.PossibleMoves) { if (m is CastlingMove && (m.To == Board.C1 || m.To == Board.C8)) { move = m; break; } } } else { int index = san.Length - 1; // remove chess and checkmate representation (if any) if (index > -1 && (san[index] == '+' || san[index] == '#')) { index--; } if (index < 1) { throw new ArgumentException("Resources.IllegalSANFormatMsg", "san"); } // get the promotion (if any) char prom = '\0'; if (san[index - 1] == '=') { prom = san[index]; index -= 2; } if (index < 1 || san[index - 1] < 'a' || san[index - 1] > 'h' || san[index] < '1' || san[index] > '8') { throw new ArgumentException("Resources.IllegalSANFormatMsg", "san"); } // get the ending square int to = Utils.GetPosition(san[index - 1], san[index]); index -= 2; // remove capture char (if any) if (index > -1 && san[index] == 'x') { index--; } // get the rank of the starting square (if any) int? rank = null; if (index > -1 && san[index] >= '1' && san[index] <= '8') { rank = GetRank(san[index]); index--; } // get the file of the starting square (if any) int? file = null; if (index > -1 && san[index] >= 'a' && san[index] <= 'h') { file = GetFile(san[index]); index--; } // get piece type char (if any) char pieceChar = 'P'; if (index > -1) { pieceChar = san[index]; index--; } // look into possible moves foreach (Move m in game.PossibleMoves) { if ( m.To == to &&// the ending squares match (file == null || (file != null && Board.File(m.From) == file.Value)) &&// the starting squares files match (if any) (rank == null || (rank != null && Board.Rank(m.From) == rank.Value)) &&// the starting squares ranks match (if any) pieceTypeToUpperCharConversion[game.CurrentBoard[m.From].GetType()] == pieceChar// the piece type chars match ) { move = m; break; } } // if it's a promotion move, set the promotion if (move is PromotionMove) { (move as PromotionMove).PromotionType = GetPromotionType(game.CurrentBoard.Status.WhiteTurn, prom); } } if (move != null) { return move; } else { throw new ArgumentException("Resources.IllegalSANMoveMsg", "san"); } }
/// <summary> /// Get the SAN (standard algebraic notation) index for a move - move number and the side to move /// without checking the status of the game or if it's a valid move. /// </summary> /// <param name="game">The game</param> /// <returns></returns> public static string GetSANIndex(Game game) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", "Resources.NullGameMsg"); } StringBuilder sb = new StringBuilder(5); // add move number sb.Append(game.CurrentBoard.Status.Moves.ToString(CultureInfo.InvariantCulture)); // add side to move notation if (game.CurrentBoard.Status.WhiteTurn) { sb.Append('.'); } else { sb.Append("..."); } return sb.ToString(); }
/// <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 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> /// Gets a move from its CAN (coordinate algebraic notation). /// Throws ArgumentException if it's not a valid move. /// </summary> /// <param name="game">The game</param> /// <param name="can">The CAN string</param> /// <returns></returns> public static Move GetCANMove(Game game, string can) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", "Resources.NullGameMsg"); } // the CAN string must not be null if (can == null) { throw new ArgumentNullException("can", "Resources.NullCANMsg"); } // check if the string is well-formed if (can.Length < 4 || can[0] < 'a' || can[0] > 'h' || can[1] < '1' || can[1] > '8' || can[2] < 'a' || can[2] > 'h' || can[3] < '1' || can[3] > '8') { throw new ArgumentException("Resources.IllegalCANFormatMsg", "can"); } // get the starting square int from = GetPosition(can[0], can[1]); // get the ending square int to = GetPosition(can[2], can[3]); // get the promotion Type promotionType = (can.Length > 4) ? GetPromotionType(game.CurrentBoard.Status.WhiteTurn, can[4]) : null; // look into the possible moves to find it Move move = game.GetMove(from, to, promotionType); if ((move == null) || (move is PromotionMove && (move as PromotionMove).PromotionType == null)) { throw new ArgumentException("Resources.IllegalCANMoveMsg", "can"); } return move; }