public virtual MoveType ApplyMove(Move move, bool alreadyValidated, out Piece captured) { ChessUtilities.ThrowIfNull(move, "move"); captured = null; if (!alreadyValidated && !IsValidMove(move)) { return(MoveType.Invalid); } MoveType type = MoveType.Move; Piece movingPiece = GetPieceAt(move.OriginalPosition.File, move.OriginalPosition.Rank); Piece capturedPiece = GetPieceAt(move.NewPosition.File, move.NewPosition.Rank); captured = capturedPiece; Piece newPiece = movingPiece; bool isCapture = capturedPiece != null; CastlingType castle = CastlingType.None; if (movingPiece is Pawn) { i_halfMoveClock = 0; PositionDistance pd = new PositionDistance(move.OriginalPosition, move.NewPosition); if (pd.DistanceX == 1 && pd.DistanceY == 1 && GetPieceAt(move.NewPosition) == null) { // en passant isCapture = true; captured = GetPieceAt(move.NewPosition.File, move.OriginalPosition.Rank); SetPieceAt(move.NewPosition.File, move.OriginalPosition.Rank, null); } if (move.NewPosition.Rank == (move.Player == Player.White ? 8 : 1)) { newPiece = MapPgnCharToPiece(move.Promotion.Value, move.Player).AsPromotion(); type |= MoveType.Promotion; } } else if (movingPiece is King) { if (movingPiece.Owner == Player.White) { CanWhiteCastleKingSide = CanWhiteCastleQueenSide = false; } else { CanBlackCastleKingSide = CanBlackCastleQueenSide = false; } if (CastlingCanBeLegal && ((GetPieceAt(move.NewPosition) is Rook && GetPieceAt(move.NewPosition).Owner == move.Player) || ((move.NewPosition.File == File.C || move.NewPosition.File == File.G) && (move.Player == Player.White ? InitialWhiteKingFile : InitialBlackKingFile) == File.E && move.OriginalPosition.File == File.E))) { castle = ApplyCastle(move); type |= MoveType.Castling; isCapture = false; } } else if (movingPiece is Rook) { if (move.Player == Player.White) { if (move.OriginalPosition.File == File.A && move.OriginalPosition.Rank == 1) { CanWhiteCastleQueenSide = false; } else if (move.OriginalPosition.File == File.H && move.OriginalPosition.Rank == 1) { CanWhiteCastleKingSide = false; } } else { if (move.OriginalPosition.File == File.A && move.OriginalPosition.Rank == 8) { CanBlackCastleQueenSide = false; } else if (move.OriginalPosition.File == File.H && move.OriginalPosition.Rank == 8) { CanBlackCastleKingSide = false; } } } if (isCapture) { type |= MoveType.Capture; i_halfMoveClock = 0; if (move.NewPosition.File == File.A && move.NewPosition.Rank == 1) { CanWhiteCastleQueenSide = false; } else if (move.NewPosition.File == File.H && move.NewPosition.Rank == 1) { CanWhiteCastleKingSide = false; } else if (move.NewPosition.File == File.A && move.NewPosition.Rank == 8) { CanBlackCastleQueenSide = false; } else if (move.NewPosition.File == File.H && move.NewPosition.Rank == 8) { CanBlackCastleKingSide = false; } } if (!isCapture && !(movingPiece is Pawn)) { i_halfMoveClock++; if (i_halfMoveClock >= 100) { fiftyMoves = true; } else { fiftyMoves = false; } } if (move.Player == Player.Black) { i_fullMoveNumber++; } if (castle == CastlingType.None) { SetPieceAt(move.NewPosition.File, move.NewPosition.Rank, newPiece); SetPieceAt(move.OriginalPosition.File, move.OriginalPosition.Rank, null); } WhoseTurn = ChessUtilities.GetOpponentOf(move.Player); AddDetailedMove(new DetailedMove(move, movingPiece, isCapture, castle)); return(type); }
public ChessPiece(Piece piece, Player player) { Piece = piece; Player = player; }
protected virtual GameCreationData FenStringToGameCreationData(string fen) { Dictionary <char, Piece> fenMappings = FenMappings; string[] parts = fen.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (!AllowedFenPartsLength.Contains(parts.Length)) { throw new ArgumentException("The FEN string has too much, or too few, parts."); } Piece[][] board = new Piece[8][]; string[] rows = parts[0].Split('/'); if (!ValidFenBoardRows.Contains(rows.Length)) { throw new ArgumentException("The board in the FEN string has an invalid number of rows."); } GameCreationData data = new GameCreationData(); data.Board = InterpretBoardOfFen(parts[0]); if (parts[1] == "w") { data.WhoseTurn = Player.White; } else if (parts[1] == "b") { data.WhoseTurn = Player.Black; } else { throw new ArgumentException("Expected `w` or `b` for the active player in the FEN string."); } if (parts[2].Contains("K")) { data.CanWhiteCastleKingSide = true; } else { data.CanWhiteCastleKingSide = false; } if (parts[2].Contains("Q")) { data.CanWhiteCastleQueenSide = true; } else { data.CanWhiteCastleQueenSide = false; } if (parts[2].Contains("k")) { data.CanBlackCastleKingSide = true; } else { data.CanBlackCastleKingSide = false; } if (parts[2].Contains("q")) { data.CanBlackCastleQueenSide = true; } else { data.CanBlackCastleQueenSide = false; } if (parts[3] == "-") { data.EnPassant = null; } else { Position ep = new Position(parts[3]); if ((data.WhoseTurn == Player.White && (ep.Rank != 6 || !(data.Board[3][(int)ep.File] is Pawn))) || (data.WhoseTurn == Player.Black && (ep.Rank != 3 || !(data.Board[4][(int)ep.File] is Pawn)))) { throw new ArgumentException("Invalid en passant field in FEN."); } data.EnPassant = ep; } int halfmoveClock; if (int.TryParse(parts[4], out halfmoveClock)) { data.HalfMoveClock = halfmoveClock; } else { throw new ArgumentException("Halfmove clock in FEN is invalid."); } int fullMoveNumber; if (int.TryParse(parts[5], out fullMoveNumber)) { data.FullMoveNumber = fullMoveNumber; } else { throw new ArgumentException("Fullmove number in FEN is invalid."); } return(data); }
protected virtual void SetPieceAt(File file, int rank, Piece piece) { Board[8 - rank][(int)file] = piece; }
public virtual bool IsInCheck(Player player) { if (player == Player.None) { throw new ArgumentException("IsInCheck: Player.None is an invalid argument."); } Cache <bool> cache = player == Player.White ? inCheckCacheWhite : inCheckCacheBlack; if (cache.CachedAt == Moves.Count) { return(cache.Value); } Position kingPos = new Position(File.None, -1); for (int r = 1; r <= Board.Length; r++) { for (int f = 0; f < Board[8 - r].Length; f++) { Piece curr = GetPieceAt((File)f, r); if (curr is King && curr.Owner == player) { kingPos = new Position((File)f, r); break; } } if (kingPos != new Position(File.None, -1)) { break; } } if (kingPos.File == File.None) { return(cache.UpdateCache(false, Moves.Count)); } for (int r = 1; r <= Board.Length; r++) { for (int f = 0; f < Board[8 - r].Length; f++) { Piece curr = GetPieceAt((File)f, r); if (curr == null) { continue; } Player p = curr.Owner; Move move = new Move(new Position((File)f, r), kingPos, p); List <Move> moves = new List <Move>(); if (curr is Pawn && ((move.NewPosition.Rank == 8 && move.Player == Player.White) || (move.NewPosition.Rank == 1 && move.Player == Player.Black))) { moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'Q')); moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'R')); moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'B')); moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'N')); moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'K')); } else { moves.Add(move); } foreach (Move m in moves) { if (IsValidMove(m, false, false)) { return(cache.UpdateCache(true, Moves.Count)); } } } } return(cache.UpdateCache(false, Moves.Count)); }
protected virtual GameCreationData FenStringToGameCreationData(string fen) { Dictionary<char, Piece> fenMappings = FenMappings; string[] parts = fen.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (!AllowedFenPartsLength.Contains(parts.Length)) throw new ArgumentException("The FEN string has too much, or too few, parts."); Piece[][] board = new Piece[8][]; string[] rows = parts[0].Split('/'); if (rows.Length != 8) throw new ArgumentException("The board in the FEN string does not have 8 rows."); GameCreationData data = new GameCreationData(); for (int i = 0; i < 8; i++) { string row = rows[i]; Piece[] currentRow = new Piece[8] { null, null, null, null, null, null, null, null }; int j = 0; foreach (char c in row) { if (char.IsDigit(c)) { j += (int)char.GetNumericValue(c); continue; } if (!fenMappings.ContainsKey(c)) throw new ArgumentException("The FEN string contains an unknown piece."); currentRow[j] = fenMappings[c]; j++; } if (j != 8) { throw new ArgumentException("Not enough pieces provided for a row in the FEN string."); } board[i] = currentRow; } data.Board = board; if (parts[1] == "w") { data.WhoseTurn = Player.White; } else if (parts[1] == "b") { data.WhoseTurn = Player.Black; } else { throw new ArgumentException("Expected `w` or `b` for the active player in the FEN string."); } if (parts[2].Contains('K')) data.CanWhiteCastleKingSide = true; else data.CanWhiteCastleKingSide = false; if (parts[2].Contains('Q')) data.CanWhiteCastleQueenSide = true; else data.CanWhiteCastleQueenSide = false; if (parts[2].Contains('k')) data.CanBlackCastleKingSide = true; else data.CanBlackCastleKingSide = false; if (parts[2].Contains('q')) data.CanBlackCastleQueenSide = true; else data.CanBlackCastleQueenSide = false; if (parts[3] == "-") data.EnPassant = null; else { Position ep = new Position(parts[3]); if ((data.WhoseTurn == Player.White && (ep.Rank != 6 || !(data.Board[3][(int)ep.File] is Pawn))) || (data.WhoseTurn == Player.Black && (ep.Rank != 3 || !(data.Board[4][(int)ep.File] is Pawn)))) { throw new ArgumentException("Invalid en passant field in FEN."); } data.EnPassant = ep; } int halfmoveClock; if (int.TryParse(parts[4], out halfmoveClock)) { data.HalfMoveClock = halfmoveClock; } else { throw new ArgumentException("Halfmove clock in FEN is invalid."); } int fullMoveNumber; if (int.TryParse(parts[5], out fullMoveNumber)) { data.FullMoveNumber = fullMoveNumber; } else { throw new ArgumentException("Fullmove number in FEN is invalid."); } return data; }
public ChessGame(Piece[][] board, Player whoseTurn) { Board = CloneBoard(board); _moves = new List<DetailedMove>(); WhoseTurn = whoseTurn; Piece e1 = GetPieceAt(File.E, 1); Piece e8 = GetPieceAt(File.E, 8); Piece a1 = GetPieceAt(File.A, 1); Piece h1 = GetPieceAt(File.H, 1); Piece a8 = GetPieceAt(File.A, 8); Piece h8 = GetPieceAt(File.H, 8); CanBlackCastleKingSide = CanBlackCastleQueenSide = CanWhiteCastleKingSide = CanWhiteCastleQueenSide = CastlingCanBeLegal; if (CastlingCanBeLegal) { if (!(e1 is King) || e1.Owner != Player.White) CanWhiteCastleKingSide = CanWhiteCastleQueenSide = false; if (!(e8 is King) || e8.Owner != Player.Black) CanBlackCastleKingSide = CanBlackCastleQueenSide = false; if (!(a1 is Rook) || a1.Owner != Player.White) CanWhiteCastleQueenSide = false; if (!(h1 is Rook) || h1.Owner != Player.White) CanWhiteCastleKingSide = false; if (!(a8 is Rook) || a8.Owner != Player.Black) CanBlackCastleQueenSide = false; if (!(h8 is Rook) || h8.Owner != Player.Black) CanBlackCastleKingSide = false; } }
protected static Piece[][] CloneBoard(Piece[][] originalBoard) { ChessUtilities.ThrowIfNull(originalBoard, "originalBoard"); Piece[][] newBoard = new Piece[originalBoard.Length][]; for (int i = 0; i < originalBoard.Length; i++) { newBoard[i] = new Piece[originalBoard[i].Length]; Array.Copy(originalBoard[i], newBoard[i], originalBoard[i].Length); } return newBoard; }