/// <summary> /// /// </summary> /// <param name="boardState"></param> /// <param name="move"></param> /// <param name="performCheckTest"> /// This has a small performance impact when reverse engineering SAN from the board move /// (approx 20ms a game on my rig) which is why it's optional and defaults to off</param> /// <returns></returns> public StandardAlgebraicNotation BuildFrom(IBoardState <ChessPieceEntity> boardState, BoardMove move, bool performCheckTest = false) { var fromItem = boardState.GetItem(move.From); var piece = fromItem.Item.Piece; int?fromFile = null; int?fromRank = null; var toFile = move.To.X; var toRank = move.To.Y; var moveType = boardState.IsEmpty(move.To) ? SanMoveTypes.Move : SanMoveTypes.Take; // Are they any other pieces, // of same type as the from item // that can also move to the same location var otherPieces = boardState.GetItems() .Where(i => !i.Location.Equals(move.From)) .Where(i => i.Item.Is(fromItem.Item.Player, fromItem.Item.Piece)) .ThatCanMoveTo(move.To); // TODO: That I need this resharper disable is probably a smell // ReSharper disable PossibleMultipleEnumeration if (otherPieces.Any()) { fromFile = move.From.X; var file = fromFile; otherPieces = otherPieces .Where(i => i.Location.X == file); } if (otherPieces.Any()) { fromRank = move.From.Y; otherPieces = new List <LocatedItem <ChessPieceEntity> >(); } if (otherPieces.Any()) { Throw.InvalidSan($"Unable to disambiguate {move}"); } // ReSharper restore PossibleMultipleEnumeration if (piece == ChessPieceName.Pawn && moveType == SanMoveTypes.Take) { fromFile = fromItem.Location.X; } ChessPieceName?promotionPiece = null; if (move.ExtraData is ChessPieceEntityFactory.ChessPieceEntityFactoryTypeExtraData data) { promotionPiece = data.PieceName; } var inCheck = performCheckTest && _checkDetectionService.DoesMoveCauseCheck(boardState, move); return(new StandardAlgebraicNotation(piece, fromFile, fromRank, toFile, toRank, moveType, promotionPiece, inCheck)); }