UNPKG

advanced-games-library

Version:

Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes

828 lines (718 loc) 25 kB
/** * Advanced Analytics Engine - מנוע ניתוח נתונים מתקדם * מספק ניתוח מעמיק של התנהגות משתמשים, ביצועי משחקים ותובנות עסקיות */ export interface PlayerBehaviorData { playerId: string; sessionId: string; gameType: string; startTime: number; endTime?: number; actions: PlayerAction[]; performance: SessionPerformance; context: SessionContext; } export interface PlayerAction { timestamp: number; type: 'click' | 'swipe' | 'keypress' | 'gameMove' | 'pause' | 'resume' | 'quit'; target?: string; coordinates?: { x: number; y: number }; duration?: number; value?: any; metadata?: Record<string, any>; } export interface SessionPerformance { score: number; accuracy: number; // 0-1 reactionTime: number; // average ms totalMoves: number; mistakeCount: number; hintsUsed: number; completionRate: number; // 0-1 difficultyLevel: number; streakBest: number; } export interface SessionContext { deviceInfo: DeviceInfo; networkQuality: string; timeOfDay: number; // hour 0-23 dayOfWeek: number; // 0-6 gameVersion: string; previousSessions: number; isReturningPlayer: boolean; referralSource?: string; } export interface DeviceInfo { platform: 'ios' | 'android' | 'web'; deviceModel?: string; osVersion?: string; screenSize: { width: number; height: number }; browserInfo?: string; orientation: 'portrait' | 'landscape'; } export interface AnalyticsInsight { id: string; type: 'engagement' | 'performance' | 'retention' | 'monetization' | 'technical'; title: string; description: string; severity: 'low' | 'medium' | 'high' | 'critical'; confidence: number; // 0-1 data: any; actionable: boolean; recommendations: string[]; timestamp: number; } export interface GameAnalytics { gameType: string; totalSessions: number; uniquePlayers: number; averageSessionDuration: number; completionRate: number; retentionRates: { day1: number; day7: number; day30: number; }; difficultyDistribution: { [level: string]: number }; popularFeatures: Array<{ feature: string; usage: number }>; dropoffPoints: Array<{ point: string; percentage: number }>; } export interface PlayerSegment { id: string; name: string; description: string; criteria: SegmentCriteria; playerCount: number; characteristics: PlayerCharacteristics; recommendations: string[]; } export interface SegmentCriteria { skillLevel?: { min: number; max: number }; playtime?: { min: number; max: number }; sessionCount?: { min: number; max: number }; retention?: { min: number; max: number }; spendingLevel?: { min: number; max: number }; gamePreferences?: string[]; deviceType?: string[]; timePattern?: 'morning' | 'afternoon' | 'evening' | 'night'; } export interface PlayerCharacteristics { averageSkillLevel: number; averageSessionDuration: number; preferredGameTypes: string[]; mostActiveTime: number; churnRisk: number; // 0-1 lifetimeValue: number; engagementScore: number; // 0-100 } export interface PredictiveModel { modelType: 'churn' | 'ltv' | 'engagement' | 'difficulty' | 'retention'; accuracy: number; features: string[]; predictions: Map<string, ModelPrediction>; lastTrained: number; trainingData: any[]; } export interface ModelPrediction { playerId: string; prediction: number; confidence: number; factors: Array<{ factor: string; importance: number }>; timestamp: number; } class AdvancedAnalyticsEngine { private behaviorData: Map<string, PlayerBehaviorData[]> = new Map(); private insights: AnalyticsInsight[] = []; private playerSegments: Map<string, PlayerSegment> = new Map(); private predictiveModels: Map<string, PredictiveModel> = new Map(); private realtimeMetrics: Map<string, any> = new Map(); private analyticsConfig = { sessionTimeout: 30 * 60 * 1000, // 30 minutes insightGenerationInterval: 60 * 60 * 1000, // 1 hour dataRetentionDays: 90, minDataPointsForInsights: 50, confidenceThreshold: 0.7 }; constructor() { this.initializeDefaultSegments(); this.initializePredictiveModels(); this.startInsightGeneration(); console.log('📈 Advanced Analytics Engine initialized'); } /** * אתחול segments ברירת מחדל */ private initializeDefaultSegments(): void { const segments: PlayerSegment[] = [ { id: 'new_players', name: 'שחקנים חדשים', description: 'שחקנים עם פחות מ-5 סשנים', criteria: { sessionCount: { min: 0, max: 5 } }, playerCount: 0, characteristics: { averageSkillLevel: 0.3, averageSessionDuration: 5 * 60 * 1000, preferredGameTypes: ['simple-puzzle'], mostActiveTime: 14, churnRisk: 0.6, lifetimeValue: 0, engagementScore: 40 }, recommendations: [ 'ספק הדרכה מפורטת', 'הציע רמות קלות יותר', 'הוסף מערכת תגמולים מהירה' ] }, { id: 'casual_players', name: 'שחקנים מזדמנים', description: 'שחקנים עם 5-20 סשנים ורמת מיומנות בינונית', criteria: { sessionCount: { min: 5, max: 20 }, skillLevel: { min: 0.3, max: 0.7 } }, playerCount: 0, characteristics: { averageSkillLevel: 0.5, averageSessionDuration: 8 * 60 * 1000, preferredGameTypes: ['memory-match', 'simple-puzzle'], mostActiveTime: 20, churnRisk: 0.4, lifetimeValue: 5, engagementScore: 60 }, recommendations: [ 'הוסף תכונות חברתיות', 'ציע אתגרים שבועיים', 'שפר מערכת ההישגים' ] }, { id: 'core_players', name: 'שחקנים מרכזיים', description: 'שחקנים פעילים עם יותר מ-20 סשנים', criteria: { sessionCount: { min: 20, max: Infinity }, skillLevel: { min: 0.6, max: 1.0 } }, playerCount: 0, characteristics: { averageSkillLevel: 0.8, averageSessionDuration: 15 * 60 * 1000, preferredGameTypes: ['quiz', 'reaction-time', 'memory-match'], mostActiveTime: 21, churnRisk: 0.2, lifetimeValue: 25, engagementScore: 85 }, recommendations: [ 'הוסף תוכן מתקדם', 'ציע משחקים מרובי משתתפים', 'פתח תכונות VIP' ] }, { id: 'at_risk', name: 'שחקנים בסיכון', description: 'שחקנים שלא שיחקו בשבוע האחרון', criteria: { retention: { min: 0, max: 0.3 } }, playerCount: 0, characteristics: { averageSkillLevel: 0.4, averageSessionDuration: 3 * 60 * 1000, preferredGameTypes: [], mostActiveTime: 12, churnRisk: 0.8, lifetimeValue: 2, engagementScore: 25 }, recommendations: [ 'שלח התראות מותאמות אישית', 'הציע בונוסים לחזרה', 'בצע סקר משוב' ] } ]; segments.forEach(segment => { this.playerSegments.set(segment.id, segment); }); } /** * אתחול מודלים חזויים */ private initializePredictiveModels(): void { const models: { [key: string]: Omit<PredictiveModel, 'predictions'> } = { churn: { modelType: 'churn', accuracy: 0.0, features: ['sessionFrequency', 'lastSessionTime', 'averageSessionDuration', 'skillImprovement'], lastTrained: 0, trainingData: [] }, ltv: { modelType: 'ltv', accuracy: 0.0, features: ['totalSessions', 'averageScore', 'socialActivity', 'purchaseHistory'], lastTrained: 0, trainingData: [] }, engagement: { modelType: 'engagement', accuracy: 0.0, features: ['sessionDuration', 'actionDensity', 'featureUsage', 'socialInteraction'], lastTrained: 0, trainingData: [] } }; Object.entries(models).forEach(([key, model]) => { this.predictiveModels.set(key, { ...model, predictions: new Map() }); }); } /** * התחלת יצירת תובנות אוטומטית */ private startInsightGeneration(): void { setInterval(() => { this.generateInsights(); }, this.analyticsConfig.insightGenerationInterval); } /** * רישום פעילות שחקן */ trackPlayerAction(playerId: string, sessionId: string, action: PlayerAction): void { if (!this.behaviorData.has(playerId)) { this.behaviorData.set(playerId, []); } const playerSessions = this.behaviorData.get(playerId)!; let currentSession = playerSessions.find(s => s.sessionId === sessionId); if (!currentSession) { currentSession = { playerId, sessionId, gameType: action.metadata?.gameType || 'unknown', startTime: action.timestamp, actions: [], performance: { score: 0, accuracy: 0, reactionTime: 0, totalMoves: 0, mistakeCount: 0, hintsUsed: 0, completionRate: 0, difficultyLevel: 1, streakBest: 0 }, context: this.getCurrentContext(playerId) }; playerSessions.push(currentSession); } currentSession.actions.push(action); // Update real-time metrics this.updateRealtimeMetrics(action); // Update session performance this.updateSessionPerformance(currentSession, action); console.log(`📊 Action tracked: ${action.type} for player ${playerId}`); } /** * סיום סשן */ endSession(playerId: string, sessionId: string, finalPerformance: Partial<SessionPerformance>): void { const playerSessions = this.behaviorData.get(playerId); if (!playerSessions) return; const session = playerSessions.find(s => s.sessionId === sessionId); if (!session) return; session.endTime = Date.now(); session.performance = { ...session.performance, ...finalPerformance }; // Update player segment this.updatePlayerSegment(playerId); // Update predictive models this.updatePredictiveModels(playerId, session); console.log(`🏁 Session ended: ${sessionId} for player ${playerId}`); } /** * יצירת תובנות אוטומטית */ private generateInsights(): void { const insights: AnalyticsInsight[] = []; // Analyze overall trends const overallMetrics = this.calculateOverallMetrics(); // Check for concerning trends if (overallMetrics.averageSessionDuration < 5 * 60 * 1000) { insights.push({ id: `insight_${Date.now()}_1`, type: 'engagement', title: 'משך סשנים קצר', description: 'משך הסשנים הממוצע קצר מהצפוי (פחות מ-5 דקות)', severity: 'medium', confidence: 0.8, data: { averageSessionDuration: overallMetrics.averageSessionDuration }, actionable: true, recommendations: [ 'בדוק נקודות יציאה בתחילת המשחק', 'שפר את ההדרכה הראשונית', 'הוסף מערכת מטרות קצרות טווח' ], timestamp: Date.now() }); } // Check churn risk const highChurnPlayers = this.getHighChurnRiskPlayers(); if (highChurnPlayers.length > 0) { insights.push({ id: `insight_${Date.now()}_2`, type: 'retention', title: 'שחקנים בסיכון גבוה לעזיבה', description: `זוהו ${highChurnPlayers.length} שחקנים עם סיכון גבוה לעזיבה`, severity: 'high', confidence: 0.9, data: { playersAtRisk: highChurnPlayers.length }, actionable: true, recommendations: [ 'שלח הודעות התעוררות מותאמות אישית', 'הציע בונוסים או תכנים חדשים', 'בצע סקר משוב למניעת עזיבה' ], timestamp: Date.now() }); } // Add insights to collection insights.forEach(insight => { this.insights.push(insight); }); // Keep only recent insights this.cleanupOldInsights(); if (insights.length > 0) { console.log(`💡 Generated ${insights.length} new insights`); } } // Helper methods for internal calculations private updateRealtimeMetrics(action: PlayerAction): void { const currentMetrics = this.realtimeMetrics.get('current') || { activeUsers: new Set(), actionsPerMinute: 0, averageSessionDuration: 0, popularActions: new Map(), errorRate: 0 }; if (action.metadata?.playerId) { currentMetrics.activeUsers.add(action.metadata.playerId); } const actionCount = currentMetrics.popularActions.get(action.type) || 0; currentMetrics.popularActions.set(action.type, actionCount + 1); this.realtimeMetrics.set('current', currentMetrics); } private updateSessionPerformance(session: PlayerBehaviorData, action: PlayerAction): void { const performance = session.performance; if (action.type === 'gameMove') { performance.totalMoves++; if (action.metadata?.isCorrect === false) { performance.mistakeCount++; } if (action.duration) { performance.reactionTime = (performance.reactionTime + action.duration) / 2; } } if (action.metadata?.score !== undefined) { performance.score = Math.max(performance.score, action.metadata.score); } if (performance.totalMoves > 0) { performance.accuracy = 1 - (performance.mistakeCount / performance.totalMoves); } } private getCurrentContext(playerId: string): SessionContext { const now = new Date(); return { deviceInfo: { platform: this.detectPlatform(), screenSize: this.getScreenSize(), orientation: this.getOrientation(), browserInfo: typeof navigator !== 'undefined' ? navigator.userAgent : undefined }, networkQuality: 'good', timeOfDay: now.getHours(), dayOfWeek: now.getDay(), gameVersion: '1.0.0', previousSessions: this.getPreviousSessionCount(playerId), isReturningPlayer: this.isReturningPlayer(playerId) }; } private updatePlayerSegment(playerId: string): void { const playerData = this.getPlayerAnalytics(playerId); if (!playerData) return; // Find best matching segment let bestSegment: PlayerSegment | null = null; let bestScore = -1; for (const segment of this.playerSegments.values()) { const score = this.calculateSegmentMatch(playerData, segment.criteria); if (score > bestScore) { bestScore = score; bestSegment = segment; } } if (bestSegment && bestScore > 0.5) { bestSegment.playerCount++; console.log(`👥 Player ${playerId} assigned to segment: ${bestSegment.name}`); } } private calculateSegmentMatch(playerData: any, criteria: SegmentCriteria): number { let score = 0; let criteriaCount = 0; if (criteria.sessionCount) { criteriaCount++; const sessions = playerData.totalSessions || 0; if (sessions >= criteria.sessionCount.min && sessions <= criteria.sessionCount.max) { score++; } } return criteriaCount > 0 ? score / criteriaCount : 0; } private updatePredictiveModels(playerId: string, session: PlayerBehaviorData): void { const features = this.extractFeatures(playerId, session); this.predictiveModels.forEach((model, modelType) => { model.trainingData.push({ playerId, features, timestamp: Date.now() }); }); } private extractFeatures(playerId: string, session: PlayerBehaviorData): any { const playerSessions = this.behaviorData.get(playerId) || []; return { sessionCount: playerSessions.length, averageSessionDuration: this.calculateAverageSessionDuration(playerSessions), lastSessionGap: this.calculateLastSessionGap(playerSessions), skillImprovement: this.calculateSkillImprovement(playerSessions), actionDensity: session.actions.length / ((session.endTime || Date.now()) - session.startTime), errorRate: session.performance.mistakeCount / Math.max(session.performance.totalMoves, 1), completionRate: session.performance.completionRate, timeOfDay: session.context.timeOfDay, dayOfWeek: session.context.dayOfWeek }; } private calculateOverallMetrics(): any { const allSessions: PlayerBehaviorData[] = []; this.behaviorData.forEach(sessions => { allSessions.push(...sessions); }); if (allSessions.length === 0) { return { totalSessions: 0, uniquePlayers: 0, averageSessionDuration: 0, completionRate: 0, averageScore: 0 }; } const totalDuration = allSessions.reduce((sum, session) => { const duration = (session.endTime || Date.now()) - session.startTime; return sum + duration; }, 0); return { totalSessions: allSessions.length, uniquePlayers: this.behaviorData.size, averageSessionDuration: totalDuration / allSessions.length, completionRate: 0.8, averageScore: 100 }; } private getHighChurnRiskPlayers(): string[] { const churnModel = this.predictiveModels.get('churn'); if (!churnModel) return []; const highRiskPlayers: string[] = []; churnModel.predictions.forEach((prediction, playerId) => { if (prediction.prediction > 0.7 && prediction.confidence > this.analyticsConfig.confidenceThreshold) { highRiskPlayers.push(playerId); } }); return highRiskPlayers; } private cleanupOldInsights(): void { const cutoff = Date.now() - (this.analyticsConfig.dataRetentionDays * 24 * 60 * 60 * 1000); this.insights = this.insights.filter(insight => insight.timestamp >= cutoff); } // Helper methods private detectPlatform(): 'ios' | 'android' | 'web' { if (typeof navigator === 'undefined') return 'web'; const ua = navigator.userAgent; if (/iPad|iPhone|iPod/.test(ua)) return 'ios'; if (/Android/.test(ua)) return 'android'; return 'web'; } private getScreenSize(): { width: number; height: number } { if (typeof window === 'undefined') return { width: 0, height: 0 }; return { width: window.screen.width, height: window.screen.height }; } private getOrientation(): 'portrait' | 'landscape' { if (typeof window === 'undefined') return 'portrait'; return window.innerWidth > window.innerHeight ? 'landscape' : 'portrait'; } private getPreviousSessionCount(playerId: string): number { const sessions = this.behaviorData.get(playerId); return sessions ? sessions.length : 0; } private isReturningPlayer(playerId: string): boolean { return this.getPreviousSessionCount(playerId) > 0; } private calculateAverageSessionDuration(sessions: PlayerBehaviorData[]): number { if (sessions.length === 0) return 0; const totalDuration = sessions.reduce((sum, session) => { const duration = (session.endTime || Date.now()) - session.startTime; return sum + duration; }, 0); return totalDuration / sessions.length; } private calculateLastSessionGap(sessions: PlayerBehaviorData[]): number { if (sessions.length === 0) return 0; const lastSession = sessions[sessions.length - 1]; const lastTime = lastSession.endTime || lastSession.startTime; return (Date.now() - lastTime) / (24 * 60 * 60 * 1000); // Days } private calculateSkillImprovement(sessions: PlayerBehaviorData[]): number { if (sessions.length < 2) return 0; const firstScore = sessions[0].performance.score; const lastScore = sessions[sessions.length - 1].performance.score; return lastScore - firstScore; } /** * Public API Methods */ /** * קבלת אנליטיקס של שחקן */ getPlayerAnalytics(playerId: string): any { const sessions = this.behaviorData.get(playerId); if (!sessions || sessions.length === 0) return null; const totalSessions = sessions.length; const totalDuration = sessions.reduce((sum, session) => { const duration = (session.endTime || Date.now()) - session.startTime; return sum + duration; }, 0); const totalScore = sessions.reduce((sum, s) => sum + s.performance.score, 0); const totalMoves = sessions.reduce((sum, s) => sum + s.performance.totalMoves, 0); const totalMistakes = sessions.reduce((sum, s) => sum + s.performance.mistakeCount, 0); return { playerId, totalSessions, averageSessionDuration: totalDuration / totalSessions, averageScore: totalScore / totalSessions, totalPlayTime: totalDuration, accuracy: totalMoves > 0 ? 1 - (totalMistakes / totalMoves) : 0, retentionRate: 0.7, // Simplified skillImprovement: this.calculateSkillImprovement(sessions), preferredGameTypes: ['simple-puzzle'], mostActiveTime: 14, engagementScore: 75 }; } /** * קבלת אנליטיקס משחק */ getGameAnalytics(gameType: string): GameAnalytics { const gameSessions: PlayerBehaviorData[] = []; const uniquePlayers = new Set<string>(); this.behaviorData.forEach(sessions => { sessions.forEach(session => { if (session.gameType === gameType) { gameSessions.push(session); uniquePlayers.add(session.playerId); } }); }); if (gameSessions.length === 0) { return { gameType, totalSessions: 0, uniquePlayers: 0, averageSessionDuration: 0, completionRate: 0, retentionRates: { day1: 0, day7: 0, day30: 0 }, difficultyDistribution: {}, popularFeatures: [], dropoffPoints: [] }; } const totalDuration = gameSessions.reduce((sum, session) => { const duration = (session.endTime || Date.now()) - session.startTime; return sum + duration; }, 0); return { gameType, totalSessions: gameSessions.length, uniquePlayers: uniquePlayers.size, averageSessionDuration: totalDuration / gameSessions.length, completionRate: 0.75, retentionRates: { day1: 0.8, day7: 0.5, day30: 0.3 }, difficultyDistribution: { 'Level 1': 50, 'Level 2': 30, 'Level 3': 20 }, popularFeatures: [], dropoffPoints: [] }; } /** * קבלת תובנות */ getInsights(filter?: { type?: AnalyticsInsight['type']; severity?: AnalyticsInsight['severity']; limit?: number; }): AnalyticsInsight[] { let filteredInsights = [...this.insights]; if (filter?.type) { filteredInsights = filteredInsights.filter(i => i.type === filter.type); } if (filter?.severity) { filteredInsights = filteredInsights.filter(i => i.severity === filter.severity); } // Sort by timestamp (newest first) and severity filteredInsights.sort((a, b) => { const severityOrder = { critical: 4, high: 3, medium: 2, low: 1 }; if (severityOrder[a.severity] !== severityOrder[b.severity]) { return severityOrder[b.severity] - severityOrder[a.severity]; } return b.timestamp - a.timestamp; }); if (filter?.limit) { filteredInsights = filteredInsights.slice(0, filter.limit); } return filteredInsights; } /** * קבלת segments של שחקנים */ getPlayerSegments(): PlayerSegment[] { return Array.from(this.playerSegments.values()); } /** * קבלת מדדים בזמן אמת */ getRealtimeMetrics(): any { const current = this.realtimeMetrics.get('current') || {}; return { activeUsers: current.activeUsers?.size || 0, actionsPerMinute: current.actionsPerMinute || 0, errorRate: current.errorRate || 0, popularActions: current.popularActions ? Array.from(current.popularActions.entries()).map(([action, count]) => ({ action, count })) : [], timestamp: Date.now() }; } /** * ניקוי משאבים */ dispose(): void { // Clear all data this.behaviorData.clear(); this.insights.length = 0; this.playerSegments.clear(); this.predictiveModels.clear(); this.realtimeMetrics.clear(); console.log('📈 Advanced Analytics Engine disposed'); } } export default AdvancedAnalyticsEngine;