internal void UndoMove() { if (!CanUndoMove()) { throw new InvalidOperationException("Undo cannot be performed: no moves."); } var data = _undoMoveDatas.Pop(); PiecePosition.SetPiece(data.Move.From, data.MovedPiece); PiecePosition.SetPiece(data.Move.To, Piece.None); if (data.CapturedPiece != Piece.None) { PiecePosition.SetPiece(data.CapturedPieceSquare, data.CapturedPiece); } else if (data.CastlingRookMove != null) { var castlingRook = PiecePosition.SetPiece(data.CastlingRookMove.To, Piece.None); if (castlingRook.GetPieceType() != PieceType.Rook || castlingRook.GetSide() != data.MovedPiece.GetSide()) { throw ChessPlatformException.CreateInconsistentStateError(); } PiecePosition.SetPiece(data.CastlingRookMove.From, castlingRook); } PiecePosition.EnsureConsistency(); }
internal MakeMoveData MakeMove( [NotNull] GameMove move, GameSide movingSide, [CanBeNull] EnPassantCaptureInfo enPassantCaptureInfo, ref CastlingOptions castlingOptions) { if (move is null) { throw new ArgumentNullException(nameof(move)); } var piece = PiecePosition[move.From]; if (piece == Piece.None || piece.GetSide() != movingSide) { throw new ArgumentException($@"Invalid move '{move}' in the position.", nameof(move)); } GameMove castlingRookMove = null; Square? enPassantCapturedPieceSquare = null; var movingSideAllCastlingOptions = ChessHelper.GameSideToCastlingOptionsMap[movingSide]; // Performing checks before actual move! var castlingInfo = CheckCastlingMove(move); var isEnPassantCapture = IsEnPassantCapture(move.From, move.To, enPassantCaptureInfo); var isPawnPromotion = IsPawnPromotion(move.From, move.To); var moveData = MovePieceInternal(move); var capturedPiece = moveData.CapturedPiece; if (isEnPassantCapture) { if (enPassantCaptureInfo is null) { throw ChessPlatformException.CreateInconsistentStateError(); } enPassantCapturedPieceSquare = enPassantCaptureInfo.TargetPieceSquare; capturedPiece = PiecePosition.SetPiece(enPassantCaptureInfo.TargetPieceSquare, Piece.None); if (capturedPiece.GetPieceType() != PieceType.Pawn) { throw ChessPlatformException.CreateInconsistentStateError(); } } else if (isPawnPromotion) { if (move.PromotionResult == PieceType.None) { throw new ChessPlatformException($@"Promoted piece type is not specified ({move})."); } var previousPiece = PiecePosition.SetPiece(move.To, move.PromotionResult.ToPiece(movingSide)); if (previousPiece.GetPieceType() != PieceType.Pawn) { throw ChessPlatformException.CreateInconsistentStateError(); } } else if (castlingInfo != null) { if (!castlingOptions.IsAllSet(castlingInfo.Option)) { throw new ChessPlatformException( $@"The castling {{{move}}} ({castlingInfo.CastlingType.GetName()}) is not allowed."); } castlingRookMove = castlingInfo.RookMove; var rookMoveData = MovePieceInternal(castlingRookMove); if (rookMoveData.CapturedPiece != Piece.None) { throw ChessPlatformException.CreateInconsistentStateError(); } castlingOptions &= ~movingSideAllCastlingOptions; } var movingSideCurrentCastlingOptions = castlingOptions & movingSideAllCastlingOptions; if (movingSideCurrentCastlingOptions != CastlingOptions.None) { switch (piece.GetPieceType()) { case PieceType.King: castlingOptions &= ~movingSideAllCastlingOptions; break; case PieceType.Rook: { var castlingInfoByRook = ChessConstants.AllCastlingInfos.SingleOrDefault(obj => obj.RookMove.From == move.From); if (castlingInfoByRook != null) { castlingOptions &= ~castlingInfoByRook.Option; } } break; } } var oppositeSide = movingSide.Invert(); var oppositeSideAllCastlingOptions = ChessHelper.GameSideToCastlingOptionsMap[oppositeSide]; var oppositeSideCurrentCastlingOptions = castlingOptions & oppositeSideAllCastlingOptions; if (oppositeSideCurrentCastlingOptions != CastlingOptions.None && capturedPiece.GetPieceType() == PieceType.Rook) { var oppositeCastlingInfo = ChessConstants.AllCastlingInfos.SingleOrDefault(obj => obj.RookMove.From == move.To); if (oppositeCastlingInfo != null) { castlingOptions &= ~oppositeCastlingInfo.Option; } } var undoMoveData = new MakeMoveData( move, moveData.MovedPiece, capturedPiece, castlingRookMove, enPassantCapturedPieceSquare); _undoMoveDatas.Push(undoMoveData); PiecePosition.EnsureConsistency(); return(undoMoveData); }