/** * Domain Value Object: GameConstraints * * Represents game-specific constraints for leagues. * Different sim racing games have different maximum grid sizes. */ import type { IValueObject } from '@gridpilot/shared/domain'; export interface GameConstraintsData { readonly maxDrivers: number; readonly maxTeams: number; readonly defaultMaxDrivers: number; readonly minDrivers: number; readonly supportsTeams: boolean; readonly supportsMultiClass: boolean; } export interface GameConstraintsProps { gameId: string; constraints: GameConstraintsData; } /** * Game-specific constraints for popular sim racing games */ const GAME_CONSTRAINTS: Record & { default: GameConstraintsData } = { iracing: { maxDrivers: 64, maxTeams: 32, defaultMaxDrivers: 24, minDrivers: 2, supportsTeams: true, supportsMultiClass: true, }, acc: { maxDrivers: 30, maxTeams: 15, defaultMaxDrivers: 24, minDrivers: 2, supportsTeams: true, supportsMultiClass: false, }, rf2: { maxDrivers: 64, maxTeams: 32, defaultMaxDrivers: 24, minDrivers: 2, supportsTeams: true, supportsMultiClass: true, }, ams2: { maxDrivers: 32, maxTeams: 16, defaultMaxDrivers: 20, minDrivers: 2, supportsTeams: true, supportsMultiClass: true, }, lmu: { maxDrivers: 32, maxTeams: 16, defaultMaxDrivers: 24, minDrivers: 2, supportsTeams: true, supportsMultiClass: true, }, // Default for unknown games default: { maxDrivers: 32, maxTeams: 16, defaultMaxDrivers: 20, minDrivers: 2, supportsTeams: true, supportsMultiClass: false, }, }; function getConstraintsForId(gameId: string): GameConstraintsData { const lower = gameId.toLowerCase(); const fromMap = GAME_CONSTRAINTS[lower]; if (fromMap) { return fromMap; } return GAME_CONSTRAINTS.default; } export class GameConstraints implements IValueObject { readonly gameId: string; readonly constraints: GameConstraintsData; private constructor(gameId: string, constraints: GameConstraintsData) { this.gameId = gameId; this.constraints = constraints; } get props(): GameConstraintsProps { return { gameId: this.gameId, constraints: this.constraints, }; } equals(other: IValueObject): boolean { return this.props.gameId === other.props.gameId; } /** * Get constraints for a specific game */ static forGame(gameId: string): GameConstraints { const constraints = getConstraintsForId(gameId); const lowerId = gameId.toLowerCase(); return new GameConstraints(lowerId, constraints); } /** * Get all supported game IDs */ static getSupportedGames(): string[] { return Object.keys(GAME_CONSTRAINTS).filter(id => id !== 'default'); } /** * Maximum drivers allowed for this game */ get maxDrivers(): number { return this.constraints.maxDrivers; } /** * Maximum teams allowed for this game */ get maxTeams(): number { return this.constraints.maxTeams; } /** * Default driver count for new leagues */ get defaultMaxDrivers(): number { return this.constraints.defaultMaxDrivers; } /** * Minimum drivers required */ get minDrivers(): number { return this.constraints.minDrivers; } /** * Whether this game supports team-based leagues */ get supportsTeams(): boolean { return this.constraints.supportsTeams; } /** * Whether this game supports multi-class racing */ get supportsMultiClass(): boolean { return this.constraints.supportsMultiClass; } /** * Validate a driver count against game constraints */ validateDriverCount(count: number): { valid: boolean; error?: string } { if (count < this.minDrivers) { return { valid: false, error: `Minimum ${this.minDrivers} drivers required`, }; } if (count > this.maxDrivers) { return { valid: false, error: `Maximum ${this.maxDrivers} drivers allowed for ${this.gameId.toUpperCase()}`, }; } return { valid: true }; } /** * Validate a team count against game constraints */ validateTeamCount(count: number): { valid: boolean; error?: string } { if (!this.supportsTeams) { return { valid: false, error: `${this.gameId.toUpperCase()} does not support team-based leagues`, }; } if (count > this.maxTeams) { return { valid: false, error: `Maximum ${this.maxTeams} teams allowed for ${this.gameId.toUpperCase()}`, }; } return { valid: true }; } }