This commit is contained in:
2025-12-17 01:23:09 +01:00
parent f01e01e50c
commit 4d890863d3
73 changed files with 2632 additions and 3224 deletions

View File

@@ -90,70 +90,7 @@ export class RaceIncidents implements IValueObject<IncidentRecord[]> {
return this.incidents.length === 0;
}
/**
* Get incident severity score (0-100, higher = more severe)
*/
getSeverityScore(): number {
if (this.incidents.length === 0) return 0;
const severityWeights: Record<IncidentType, number> = {
track_limits: 10,
contact: 20,
unsafe_rejoin: 25,
aggressive_driving: 15,
false_start: 30,
collision: 40,
spin: 35,
mechanical: 5, // Lower weight as it's not driver error
other: 15,
};
const totalSeverity = this.incidents.reduce((total, incident) => {
return total + severityWeights[incident.type];
}, 0);
// Normalize to 0-100 scale (cap at 100 for very incident-heavy races)
return Math.min(100, totalSeverity);
}
/**
* Get human-readable incident summary
*/
getSummary(): string {
if (this.incidents.length === 0) {
return 'Clean race';
}
const typeCounts = this.incidents.reduce((counts, incident) => {
counts[incident.type] = (counts[incident.type] || 0) + 1;
return counts;
}, {} as Record<IncidentType, number>);
const summaryParts = Object.entries(typeCounts).map(([type, count]) => {
const typeLabel = this.getIncidentTypeLabel(type as IncidentType);
return count > 1 ? `${count}x ${typeLabel}` : typeLabel;
});
return summaryParts.join(', ');
}
/**
* Get human-readable label for incident type
*/
private getIncidentTypeLabel(type: IncidentType): string {
const labels: Record<IncidentType, string> = {
track_limits: 'Track Limits',
contact: 'Contact',
unsafe_rejoin: 'Unsafe Rejoin',
aggressive_driving: 'Aggressive Driving',
false_start: 'False Start',
collision: 'Collision',
spin: 'Spin',
mechanical: 'Mechanical',
other: 'Other',
};
return labels[type];
}
// Removed getSeverityScore, getSummary, and getIncidentTypeLabel to eliminate static data in core
equals(other: IValueObject<IncidentRecord[]>): boolean {
const otherIncidents = other.props;
@@ -174,66 +111,4 @@ export class RaceIncidents implements IValueObject<IncidentRecord[]> {
});
}
/**
* Create RaceIncidents from legacy incidents count
*/
static fromLegacyIncidentsCount(count: number): RaceIncidents {
if (count === 0) {
return new RaceIncidents();
}
// Distribute legacy incidents across different types based on probability
const incidents: IncidentRecord[] = [];
for (let i = 0; i < count; i++) {
const type = RaceIncidents.getRandomIncidentType();
incidents.push({
type,
lap: Math.floor(Math.random() * 20) + 1, // Random lap 1-20
penaltyPoints: RaceIncidents.getDefaultPenaltyPoints(type),
});
}
return new RaceIncidents(incidents);
}
/**
* Get random incident type for legacy data conversion
*/
private static getRandomIncidentType(): IncidentType {
const types: IncidentType[] = [
'track_limits', 'contact', 'unsafe_rejoin', 'aggressive_driving',
'collision', 'spin', 'other'
];
const weights = [0.4, 0.25, 0.15, 0.1, 0.05, 0.04, 0.01]; // Probability weights
const random = Math.random();
let cumulativeWeight = 0;
for (let i = 0; i < types.length; i++) {
cumulativeWeight += weights[i];
if (random <= cumulativeWeight) {
return types[i];
}
}
return 'other';
}
/**
* Get default penalty points for incident type
*/
private static getDefaultPenaltyPoints(type: IncidentType): number {
const penalties: Record<IncidentType, number> = {
track_limits: 0, // Usually just a warning
contact: 2,
unsafe_rejoin: 3,
aggressive_driving: 2,
false_start: 5,
collision: 5,
spin: 0, // Usually no penalty if no contact
mechanical: 0,
other: 2,
};
return penalties[type];
}
}