import { Result } from '../../domain/entities/result/Result'; /** * Enhanced race result generator with detailed incident types */ export class RaceResultGenerator { /** * Generate realistic race results with detailed incidents */ static generateRaceResults( raceId: string, driverIds: string[], driverRatings: Map ): Result[] { // Create driver performance data const driverPerformances = driverIds.map(driverId => ({ driverId, rating: driverRatings.get(driverId) ?? 1500, // Default rating randomFactor: Math.random() - 0.5, // -0.5 to +0.5 randomization })); // Sort by performance (rating + randomization) driverPerformances.sort((a, b) => { const perfA = a.rating + (a.randomFactor * 200); // ±100 rating points randomization const perfB = b.rating + (b.randomFactor * 200); return perfB - perfA; // Higher performance first }); // Generate qualifying results for start positions (similar but different from race results) const qualiPerformances = driverPerformances.map(p => ({ ...p, randomFactor: Math.random() - 0.5, // New randomization for quali })); qualiPerformances.sort((a, b) => { const perfA = a.rating + (a.randomFactor * 150); const perfB = b.rating + (b.randomFactor * 150); return perfB - perfA; }); // Generate results const results: Result[] = []; for (const [index, performance] of driverPerformances.entries()) { const driverId = performance.driverId; const position = index + 1; const startPosition = qualiPerformances.findIndex(p => p.driverId === driverId) + 1; // Generate realistic lap times (90-120 seconds for a lap) const baseLapTime = 90000 + Math.random() * 30000; const positionBonus = (position - 1) * 500; // Winners are faster const fastestLap = Math.round(baseLapTime + positionBonus + Math.random() * 5000); // Generate detailed incidents const incidents = this.generateDetailedIncidents(position, driverPerformances.length); results.push( Result.create({ id: `${raceId}-${driverId}`, raceId, driverId, position, startPosition, fastestLap, incidents, }) ); } return results; } /** * Generate detailed incidents with specific types */ private static generateDetailedIncidents(position: number, totalDrivers: number): number { // Base probability increases for lower positions (more aggressive driving) const baseProbability = Math.min(0.85, position / totalDrivers + 0.1); // Add some randomness const randomFactor = Math.random(); if (randomFactor > baseProbability) { return 0; // Clean race } // Determine incident severity based on position and randomness const severityRoll = Math.random(); if (severityRoll < 0.4) { // Minor incident (track limits, small contact) return 1; } else if (severityRoll < 0.7) { // Moderate incident (off-track, contact with damage) return 2; } else if (severityRoll < 0.9) { // Major incident (spin, collision) return 3; } else { // Severe incident (multiple cars involved, safety car) return Math.floor(Math.random() * 2) + 3; // 3-4 incidents } } /** * Get incident type description for a given incident count */ static getIncidentDescription(incidents: number): string { switch (incidents) { case 0: return 'Clean race'; case 1: return 'Track limits violation'; case 2: return 'Contact with another car'; case 3: return 'Off-track incident'; case 4: return 'Collision requiring safety car'; default: return `${incidents} incidents`; } } /** * Calculate incident penalty points for standings */ static getIncidentPenaltyPoints(incidents: number): number { // Each incident deducts points from championship standings return Math.max(0, incidents - 1) * 2; // First incident free, then 2 points each } }