/// <summary>
/// 将 SAN 字符串解析为 Move。
/// </summary>
/// <param name="moveInStr"></param>
/// <param name="player"></param>
/// <param name="game"></param>
/// <returns></returns>
public static Move ParseMove(string moveInStr, Player player, ChessGame game)
{
try
{
string move = moveInStr.TrimEnd('#', '?', '!', '+').Trim();
Position origin = null;
Position destination = null;
Piece piece = null;
char? promotion = null;
if (move.Length == 0)
{
throw new PgnException("The length of Move string is zero.");
}
if (move.Length > 2)
{
string possiblePromotionPiece = move.Substring(move.Length - 2).ToUpperInvariant();
if (possiblePromotionPiece[0] == '=')
{
promotion = possiblePromotionPiece[1];
move = move.Remove(move.Length - 2, 2);
}
}
if (move.ToUpperInvariant() == "O-O")
{
int r = player == Player.White ? 1 : 8;
origin = new Position(File.E, r);
destination = new Position(File.G, r);
piece = new King(player);
}
else if (move.ToUpperInvariant() == "O-O-O")
{
int r = player == Player.White ? 1 : 8;
origin = new Position(File.E, r);
destination = new Position(File.C, r);
piece = new King(player);
}
if (piece == null)
{
piece = game.MapPgnCharToPiece(move[0], player);
}
if (!(piece is Pawn))
{
move = move.Remove(0, 1);
}
int rankRestriction = -1;
File fileRestriction = File.None;
if (destination == null)
{
if (move.Length == 0)
{
throw new PgnException("The position of Move is empty.");
}
if (move[0] == 'x')
{
move = move.Remove(0, 1);
}
else if (move.Length == 4 && move[1] == 'x')
{
move = move.Remove(1, 1);
}
if (move.Length == 2)
{
destination = new Position(move);
}
else if (move.Length == 3)
{
if (char.IsDigit(move[0]))
{
rankRestriction = int.Parse(move[0].ToString());
}
else
{
bool recognized = Enum.TryParse <File>(move[0].ToString(), true, out fileRestriction);
if (!recognized)
{
throw new PgnException("Invalid PGN: unrecognized origin file.");
}
}
destination = new Position(move.Remove(0, 1));
}
else if (move.Length == 4)
{
origin = new Position(move.Substring(0, 2));
destination = new Position(move.Substring(2, 2));
}
else
{
throw new PgnException("Invalid PGN.");
}
}
Move m;
if (origin != null)
{
m = new Move(origin, destination, player, promotion);
if (game.IsValidMove(m))
{
return(m);
}
else
{
throw new PgnException("Invalid PGN: contains invalid moves.");
}
}
else
{
Piece[][] board = game.GetBoard();
List <Move> validMoves = new List <Move>();
for (int r = 0; r < game.BoardHeight; r++)
{
if (rankRestriction != -1 && r != 8 - rankRestriction)
{
continue;
}
for (int f = 0; f < game.BoardWidth; f++)
{
if (fileRestriction != File.None && f != (int)fileRestriction)
{
continue;
}
if (board[r][f] != piece)
{
continue;
}
m = new Move(new Position((File)f, 8 - r), destination, player, promotion);
if (game.IsValidMove(m, true))
{
validMoves.Add(m);
}
}
}
if (validMoves.Count == 0)
{
throw new PgnException("Invalid PGN: contains invalid moves.");
}
if (validMoves.Count > 1)
{
throw new PgnException("Invalid PGN: contains ambiguous moves.");
}
return(validMoves[0]);
}
}
catch (Exception e)
{
if (e is PgnException eP)
{
throw eP;
}
else
{
throw new PgnException(e.Message + Environment.NewLine + e.StackTrace);
}
}
}