class AISystem { constructor() { this.formations = { offensive: { 'LW': { x: 0.7, y: 0.2 }, 'C': { x: 0.75, y: 0.5 }, 'RW': { x: 0.7, y: 0.8 }, 'LD': { x: 0.4, y: 0.3 }, 'RD': { x: 0.4, y: 0.7 } }, defensive: { 'LW': { x: 0.3, y: 0.2 }, 'C': { x: 0.4, y: 0.5 }, 'RW': { x: 0.3, y: 0.8 }, 'LD': { x: 0.25, y: 0.3 }, 'RD': { x: 0.25, y: 0.7 } }, neutral: { 'LW': { x: 0.5, y: 0.25 }, 'C': { x: 0.5, y: 0.5 }, 'RW': { x: 0.5, y: 0.75 }, 'LD': { x: 0.35, y: 0.35 }, 'RD': { x: 0.35, y: 0.65 } } }; this.strategyWeights = { aggressive: { offense: 0.7, defense: 0.3 }, balanced: { offense: 0.5, defense: 0.5 }, defensive: { offense: 0.3, defense: 0.7 } }; this.currentStrategy = 'balanced'; } update(players, puck, gameState) { const puckOwner = this.findPuckOwner(players); const gameContext = this.analyzeGameContext(players, puck, gameState, puckOwner); // Find closest player to puck for each team (excluding goalies) const closestPlayers = this.findClosestPlayersByTeam(players, puck); gameContext.closestPlayers = closestPlayers; players.forEach(player => { if (player.role !== 'G') { this.updatePlayerAI(player, gameContext); } }); } findPuckOwner(players) { return players.find(player => player.state.hasPuck) || null; } findClosestPlayersByTeam(players, puck) { const homeClosest = { player: null, distance: Infinity }; const awayClosest = { player: null, distance: Infinity }; players.forEach(player => { // Skip goalies and players with the puck if (player.role === 'G' || player.state.hasPuck) return; const distance = player.position.distance(puck.position); if (player.team === 'home' && distance < homeClosest.distance) { homeClosest.player = player; homeClosest.distance = distance; } else if (player.team === 'away' && distance < awayClosest.distance) { awayClosest.player = player; awayClosest.distance = distance; } }); return { home: homeClosest.player, away: awayClosest.player }; } analyzeGameContext(players, puck, gameState, puckOwner) { const context = { puckOwner, puckPosition: puck.position, gameTime: gameState.timeRemaining, period: gameState.period, scoreGap: gameState.homeScore - gameState.awayScore, zone: this.determinePuckZone(puck, gameState) }; context.formation = this.selectFormation(context); context.urgency = this.calculateUrgency(context); return context; } determinePuckZone(puck, gameState) { const rinkWidth = gameState.rink.width; const x = puck.position.x; if (x < rinkWidth * 0.33) return 'defensive'; if (x > rinkWidth * 0.67) return 'offensive'; return 'neutral'; } selectFormation(context) { switch (context.zone) { case 'offensive': return 'offensive'; case 'defensive': return 'defensive'; default: return 'neutral'; } } calculateUrgency(context) { let urgency = 0; if (context.gameTime < 300) urgency += 0.3; if (context.gameTime < 120) urgency += 0.3; if (Math.abs(context.scoreGap) > 1) urgency += 0.2; return Math.min(1, urgency); } updatePlayerAI(player, context) { const teamSide = player.team === 'home' ? 1 : -1; const formation = this.formations[context.formation]; const playerFormation = formation[player.role]; if (playerFormation) { const formationPosition = this.calculateFormationPosition( playerFormation, context, teamSide ); player.aiState.formationTarget = formationPosition; } this.updatePlayerBehavior(player, context); } calculateFormationPosition(formation, context, teamSide) { const rink = { width: 1000, height: 600 }; const centerX = rink.width * 0.5; const centerY = rink.height * 0.5; let x, y; if (teamSide === 1) { x = formation.x * rink.width; y = formation.y * rink.height; } else { x = (1 - formation.x) * rink.width; y = formation.y * rink.height; } const puckInfluence = this.calculatePuckInfluence(context.puckPosition, new Vector2(x, y)); x += puckInfluence.x * 30; y += puckInfluence.y * 30; return new Vector2(x, y); } calculatePuckInfluence(puckPos, playerPos) { const direction = puckPos.subtract(playerPos).normalize(); const distance = puckPos.distance(playerPos); const influence = Math.max(0, 1 - distance / 200); return direction.multiply(influence); } updatePlayerBehavior(player, context) { const distanceToPuck = player.position.distance(context.puckPosition); const isNearPuck = distanceToPuck < 100; const isClosestToPuck = context.closestPlayers[player.team] === player; const isClosestDefender = context.puckOwner && context.puckOwner.team !== player.team && context.closestPlayers[player.team] === player; if (player.state.hasPuck) { player.aiState.behavior = 'puck_carrier'; this.executePuckCarrierBehavior(player, context); } else if (context.puckOwner && context.puckOwner.team === player.team) { player.aiState.behavior = 'support'; this.executeSupportBehavior(player, context); } else if (isClosestDefender) { // Closest defender directly targets the puck carrier player.aiState.behavior = 'aggressive_pressure'; this.executeAggressivePressureBehavior(player, context); } else if (context.puckOwner && context.puckOwner.team !== player.team) { player.aiState.behavior = 'pressure'; this.executePressureBehavior(player, context); } else if (!context.puckOwner && isClosestToPuck && isNearPuck) { // Only allow the closest player to chase loose pucks player.aiState.behavior = 'chase'; this.executeChaseBehavior(player, context); } else { player.aiState.behavior = 'formation'; this.executeFormationBehavior(player, context); } } executePuckCarrierBehavior(player, context) { const enemyGoal = player.team === 'home' ? new Vector2(950, 300) : new Vector2(50, 300); const distanceToGoal = player.position.distance(enemyGoal); const urgency = context.urgency; if (distanceToGoal < 200 && urgency > 0.5) { player.aiState.action = 'shoot'; } else if (this.isUnderPressure(player, context)) { player.aiState.action = 'pass'; } else { player.aiState.action = 'advance'; player.targetPosition = enemyGoal.copy(); } } executeSupportBehavior(player, context) { const puckCarrier = context.puckOwner; const supportPosition = this.calculateSupportPosition(player, puckCarrier, context); player.targetPosition = supportPosition; player.aiState.action = 'support'; } executeAggressivePressureBehavior(player, context) { const opponent = context.puckOwner; // Closest defender directly targets the puck carrier's position player.targetPosition = opponent.position.copy(); player.aiState.action = 'aggressive_pressure'; } executePressureBehavior(player, context) { const opponent = context.puckOwner; const pressurePosition = this.calculatePressurePosition(player, opponent, context); player.targetPosition = pressurePosition; player.aiState.action = 'pressure'; } executeChaseBehavior(player, context) { player.targetPosition = context.puckPosition.copy(); player.aiState.action = 'chase'; } executeFormationBehavior(player, context) { if (player.aiState.formationTarget) { player.targetPosition = player.aiState.formationTarget.copy(); } player.aiState.action = 'formation'; } isUnderPressure(player, context) { // Check if opponent players are nearby and approaching const nearbyOpponents = this.getNearbyOpponents(player, context, 80); return nearbyOpponents.length > 0; } getNearbyOpponents(player, context, radius) { // This would typically be passed from the game engine // For now, return empty array as placeholder return []; } calculateSupportPosition(player, puckCarrier, context) { const enemyGoal = player.team === 'home' ? new Vector2(950, 300) : new Vector2(50, 300); const basePosition = puckCarrier.position.lerp(enemyGoal, 0.7); const offset = this.getRoleOffset(player.role); return basePosition.add(offset); } calculatePressurePosition(player, opponent, context) { const ownGoal = player.team === 'home' ? new Vector2(50, 300) : new Vector2(950, 300); return opponent.position.lerp(ownGoal, 0.3); } getRoleOffset(role) { const offsets = { 'LW': new Vector2(-50, -80), 'RW': new Vector2(-50, 80), 'C': new Vector2(0, 0), 'LD': new Vector2(-100, -40), 'RD': new Vector2(-100, 40) }; return offsets[role] || new Vector2(0, 0); } setStrategy(strategy) { if (this.strategyWeights[strategy]) { this.currentStrategy = strategy; } } getStrategy() { return this.currentStrategy; } }