static void Main(string[] args) { // Let's start by creating a chess game instance. ChessGame game = new ChessGame(); // Now the game's board is in the start position and it's white's turn. Console.WriteLine("It's this color's turn: {0}", game.WhoseTurn); // This is how to find out which piece is at a certain position: Piece pieceAtA1 = game.GetPieceAt(new Position("A1")); // Or "a1", the casing doesn't matter /* There are other overloading options as well: * game.GetPieceAt(new Position(File.A, 1)); * game.GetPieceAt(File.A, 1); * All those three options return the same. */ Console.WriteLine("What piece is there at A1? {0}", pieceAtA1.GetFenCharacter()); // GetFenCharacter() returns the FEN character for the given piece. White piece: uppercase, black piece: lowercase. The character is the first char of a piece's name (exception: Knight -> N/n because King -> K/k). // The Piece class is the abstract base class for pieces. All piece classes (e.g. Rook) derive from this class. // White has to make a turn. They want to move their E2 pawn to E4. Is that valid? Move e2e4 = new Move("E2", "E4", Player.White); bool isValid = game.IsValidMove(e2e4); Console.WriteLine("E2-E4 for white is valid: {0}", isValid); // Great, it's valid! So white wants to actually make that move. MoveType type = game.ApplyMove(e2e4, true); // The first argument is the move, the second argument indicates whether it's already validated. Here it is, so pass 'true'. If it's not validated yet, ApplyMove will do it. **Only pass `true` if it's really validated! If you pass `true`, ApplyMove won't do ANY validity checks.** // The return type is the MoveType enum. It holds one, or a combination, of these values: Invalid, Move, Capture, Castling, Promotion // Each valid move will always carry the 'Move' value. If it's also something else, it will carry both values (e.g. if the move is a capture, `type` will have the value MoveType.Move | MoveType.Capture). // MoveType is a flags enumeration. https://msdn.microsoft.com/en-us/library/ms229062%28v=vs.100%29.aspx // e4 is just a normal move, so `type` will just be MoveType.Move. Console.WriteLine("Move type: {0}", type); // When a move has been made, check the Status property. It will let you know if there is a special event: check, checkmate, stalemate ... GameStatus status = game.Status; Console.WriteLine("Special event? {0}", status.Event); // Here it just returns 'None' because nothing special happened. // GameStatus has two other properties: PlayerWhoCausedEvent (quite self-explanatory) and EventExplanation (used if Chess.NET needs to be more specific about an event, e.g. when it's a draw, explaining why). // Now it's black's turn. Console.WriteLine("It's this color's turn: {0}", game.WhoseTurn); // You can figure out all valid moves using GetValidMoves. IEnumerable<Move> validMoves = game.GetValidMoves(Player.Black); // Here it returns all valid moves for black, but you can also find all valid moves *from a certain position* by passing a Position instance as argument. Console.WriteLine("How many valid moves does black have? {0}", validMoves.Count()); // It might happen that you don't really care about all valid moves, but just want to know if there are valid moves. Chess.NET also has a method for that: bool hasValidMoves = game.HasAnyValidMoves(Player.Black); // Again, you can also pass a Position instance here. Console.WriteLine("Black has any valid moves: {0}", hasValidMoves); // Congratulations! You have learned about the most important methods of Chess.NET. Enjoy using the library :) Console.ReadKey(); }
public override bool IsLegalMove(Move move, ChessGame game) { ChessUtility.ThrowIfNull(move, "move"); ChessUtility.ThrowIfNull(game, "game"); BoardPosition origin = move.OriginalPosition; BoardPosition destination = move.NewPosition; BoardDistance posDelta = new BoardDistance(origin, destination); if (posDelta.X != 0 && posDelta.Y != 0) { return(false); } bool increasingRank = destination.Rank > origin.Rank; bool increasingFile = (int)destination.File > (int)origin.File; if (posDelta.X == 0) { int f = (int)origin.File; for (int r = origin.Rank + (increasingRank ? 1 : -1); increasingRank?r <destination.Rank : r> destination.Rank; r += increasingRank ? 1 : -1) { if (game.GetPieceAt((ChessFile)f, r) != null) { return(false); } } } else // (posDelta.DeltaY == 0) { int r = origin.Rank; for (int f = (int)origin.File + (increasingFile ? 1 : -1); increasingFile?f <(int)destination.File : f> (int) destination.File; f += increasingFile ? 1 : -1) { if (game.GetPieceAt((ChessFile)f, r) != null) { return(false); } } } return(true); }
public static bool MoveIsValid(ChessBoard board, int startIndex, int endIndex) { var move = new Move(startIndex.GetChessDotNetPosition(), endIndex.GetChessDotNetPosition(), board.Cells[startIndex].IsBlack.ChessDotNetPlayer()); var gameCreationData = new GameCreationData(); for (var i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) { var cell = board.Cells[i * j]; gameCreationData.Board[i][j] = cell.Piece.Value.ChessDotNetPiece(cell.IsBlack); } } var game = new ChessDotNet.ChessGame(gameCreationData); return(game.IsValidMove(move)); }
/// <summary> /// 生成文字描述。 /// </summary> public void GenerateFriendlyText() { var sb = new System.Text.StringBuilder(); sb.Append(Piece.GetFriendlyName()); sb.Append("从"); sb.Append(OriginalPosition.ToString()); sb.Append("到"); sb.Append(NewPosition.ToString()); if (IsCapture && CapturedPiece != null) { sb.Append(", 吃"); sb.Append(CapturedPiece.GetFriendlyName()); } if (Promotion.HasValue) { sb.Append(", 晋升为"); sb.Append(ChessGame.OriginalMapPgnCharToPiece(Promotion.Value, Player).GetFriendlyName()); } if (Castling.Equals(CastlingType.KingSide)) { sb.Append(", 王翼易位"); } if (Castling.Equals(CastlingType.QueenSide)) { sb.Append(", 后翼易位"); } if(IsEnpassant) { sb.Append(", 吃过路兵"); } if (IsCheckmate.HasValue && IsCheckmate.Value) { sb.Append(", 将死"); } else if (IsChecking.HasValue && IsChecking.Value) { sb.Append(", 将军"); } this._storedFriendlyText = sb.ToString(); }
protected virtual bool IsInCheck(Player player) { List <Position> piecePositions = new List <Position>(); Position kingPos = new Position(File.None, Rank.None); for (int i = 0; i < Board.Length; i++) { for (int j = 0; j < Board[i].Length; j++) { ChessPiece curr = Board[i][j]; if (curr.Piece != Piece.None && curr.Player == Utilities.GetOpponentOf(player)) { piecePositions.Add(new Position((File)j, (Rank)i)); } else if (curr.Piece == Piece.King && curr.Player == player) { kingPos = new Position((File)j, (Rank)i); } } } if (kingPos.File == File.None) { return(false); } ChessGame copy = new ChessGame(Board, Utilities.GetOpponentOf(player), false); for (int i = 0; i < piecePositions.Count; i++) { if (copy.IsValidMove(new Move(piecePositions[i], kingPos, Utilities.GetOpponentOf(player)), false)) { return(true); } } return(false); }
/// <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); } } }
public abstract ReadOnlyCollection<Move> GetValidMoves(Position from, bool returnIfAny, ChessGame game, Func<Move, bool> gameMoveValidator);
public abstract bool IsValidMove(Move move, ChessGame game);
public abstract ReadOnlyCollection <Move> GetLegalMoves(BoardPosition position, bool returnIfAny, ChessGame game, Func <Move, bool> gameMoveValidator);
public abstract bool IsLegalMove(Move move, ChessGame game);
public override ReadOnlyCollection <Move> GetLegalMoves(BoardPosition from, bool returnIfAny, ChessGame game, Func <Move, bool> gameMoveValidator) { ChessUtility.ThrowIfNull(from, "from"); List <Move> validMoves = new List <Move>(); ChessPiece piece = game.GetPieceAt(from); int l0 = game.BoardHeight; int l1 = game.BoardWidth; for (int i = -7; i < 8; i++) { if (i == 0) { continue; } if (from.Rank + i > 0 && from.Rank + i <= l0) { Move move = new Move(from, new BoardPosition(from.File, from.Rank + i), piece.Owner); if (gameMoveValidator(move)) { validMoves.Add(move); if (returnIfAny) { return(new ReadOnlyCollection <Move>(validMoves)); } } } if ((int)from.File + i > -1 && (int)from.File + i < l1) { Move move = new Move(from, new BoardPosition(from.File + i, from.Rank), piece.Owner); if (gameMoveValidator(move)) { validMoves.Add(move); if (returnIfAny) { return(new ReadOnlyCollection <Move>(validMoves)); } } } } return(new ReadOnlyCollection <Move>(validMoves)); }
/// <summary> /// 生成 SAN 字符串。 /// </summary> /// <param name="gameBeforeTheMove"></param> /// <returns></returns> public string GenerateSANString(ChessGame gameBeforeTheMove) { string SANResult; if (Castling.Equals(CastlingType.KingSide)) { SANResult = "O-O"; } else if (Castling.Equals(CastlingType.QueenSide)) { SANResult = "O-O-O"; } else { var sb = new System.Text.StringBuilder(); if (!(Piece is Pieces.Pawn)) { sb.Append(char.ToUpper(Piece.GetFenCharacter())); } Piece[][] board = gameBeforeTheMove.GetBoard(); List<Move> validMoves = new List<Move>(); for (int r = 0; r < 8; r++) { for (int f = 0; f < 8; f++) { if (board[r][f] != Piece) continue; Move m = new Move(new Position((File)f, 8 - r), this.NewPosition, this.Player, this.Promotion); if (gameBeforeTheMove.IsValidMove(m)) { validMoves.Add(m); } } } if (validMoves.Count == 0) throw new PgnException("This move " + this.ToString() + " is not valid for gameBeforeTheMove."); else if (validMoves.Count > 1) { bool fileUnique = true; bool rankUnique = true; foreach (var move in validMoves) { if(!(move.OriginalPosition.Equals(this.OriginalPosition))) { if (move.OriginalPosition.File == this.OriginalPosition.File) { fileUnique = false; } if (move.OriginalPosition.Rank == this.OriginalPosition.Rank) { rankUnique = false; } } } if (fileUnique) sb.Append((char)((int)'a' + (int)this.OriginalPosition.File)); else if (rankUnique) sb.Append(this.OriginalPosition.Rank.ToString()); else { sb.Append((char)((int)'a' + (int)this.OriginalPosition.File)); sb.Append(this.OriginalPosition.Rank.ToString()); } } if (IsCapture) sb.Append("x"); sb.Append(this.NewPosition.ToString().ToLower()); if (Promotion.HasValue) { sb.Append("="); sb.Append(Promotion.Value); } if (IsCheckmate.HasValue && IsCheckmate.Value) { sb.Append("#"); } else if (IsChecking.HasValue && IsChecking.Value) { sb.Append("+"); } SANResult = sb.ToString(); } try { ChessDotNet.PgnMoveReader.ParseMove(SANResult, Player, gameBeforeTheMove); } catch (PgnException) { throw new System.ArgumentException("This move " + SANResult + " is not valid for gameBeforeTheMove."); } catch (System.ArgumentException) { throw new System.ArgumentException("This move " + SANResult + " is not valid for gameBeforeTheMove."); } this._storedSANString = SANResult; return SANResult; }
public abstract ReadOnlyCollection <Move> GetValidMoves(Square from, bool returnIfAny, ChessGame game);
public virtual bool WouldBeInCheckAfter(Move move, Player player) { ChessUtilities.ThrowIfNull(move, "move"); GameCreationData gcd = new GameCreationData(); gcd.Board = Board; gcd.CanWhiteCastleKingSide = CanWhiteCastleKingSide; gcd.CanWhiteCastleQueenSide = CanWhiteCastleQueenSide; gcd.CanBlackCastleKingSide = CanBlackCastleKingSide; gcd.CanBlackCastleQueenSide = CanBlackCastleQueenSide; gcd.EnPassant = null; if (_moves.Count > 0) { DetailedMove last = _moves.Last(); if (last.Piece is Pawn && new PositionDistance(last.OriginalPosition, last.NewPosition).DistanceY == 2) { gcd.EnPassant = new Position(last.NewPosition.File, last.Player == Player.White ? 3 : 6); } } gcd.HalfMoveClock = _halfMoveClock; gcd.FullMoveNumber = _fullMoveNumber; ChessGame copy = new ChessGame(gcd); copy.ApplyMove(move, true); return copy.IsInCheck(player); }
protected virtual bool IsInCheck(Player player) { List<Position> piecePositions = new List<Position>(); Position kingPos = new Position(File.None, Rank.None); for (int i = 0; i < Board.Length; i++) { for (int j = 0; j < Board[i].Length; j++) { ChessPiece curr = Board[i][j]; if (curr.Piece != Piece.None && curr.Player == Utilities.GetOpponentOf(player)) { piecePositions.Add(new Position((File)j, (Rank)i)); } else if (curr.Piece == Piece.King && curr.Player == player) { kingPos = new Position((File)j, (Rank)i); } } } if (kingPos.File == File.None) return false; ChessGame copy = new ChessGame(Board, Utilities.GetOpponentOf(player), false); for (int i = 0; i < piecePositions.Count; i++) { if (copy.IsValidMove(new Move(piecePositions[i], kingPos, Utilities.GetOpponentOf(player)), false)) { return true; } } return false; }
protected virtual bool WouldBeInCheckAfter(Move move, Player player) { Utilities.ThrowIfNull(move, "move"); ChessGame copy = new ChessGame(Board, player, false); copy.ApplyMove(move, true, false); GameStatus status = copy.CalculateStatus(player, false); return status.Event == GameEvent.Check && status.PlayerWhoCausedEvent != player; }
public abstract ReadOnlyCollection <Move> GetValidMoves(Position from, bool returnIfAny, ChessGame game, Func <Move, bool> gameMoveValidator);