Restructure player AI from individual to
This commit is contained in:
parent
af1cbf8110
commit
f7cec84973
@ -134,7 +134,7 @@ class Player {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Main AI decision making system - handles reaction timing, faceoffs, and behavioral switching
|
* Main AI decision making system - handles reaction timing, faceoffs, and behavioral switching
|
||||||
* Delegates to specific behavior methods based on puck possession
|
* Delegates to specific behavior methods based on team puck possession
|
||||||
* @param {Object} gameState - Current game state including faceoff status
|
* @param {Object} gameState - Current game state including faceoff status
|
||||||
* @param {Object} puck - Puck object with position and velocity
|
* @param {Object} puck - Puck object with position and velocity
|
||||||
* @param {Array} players - Array of all players on the ice
|
* @param {Array} players - Array of all players on the ice
|
||||||
@ -157,22 +157,93 @@ class Player {
|
|||||||
const teammates = players.filter(p => p.team === this.team && p.id !== this.id);
|
const teammates = players.filter(p => p.team === this.team && p.id !== this.id);
|
||||||
const opponents = players.filter(p => p.team !== this.team);
|
const opponents = players.filter(p => p.team !== this.team);
|
||||||
|
|
||||||
if (this.state.hasPuck) {
|
// Determine team possession
|
||||||
this.behaviorWithPuck(gameState, puck, teammates, opponents);
|
const teammatePuckCarrier = teammates.find(p => p.state.hasPuck);
|
||||||
|
const opponentPuckCarrier = opponents.find(p => p.state.hasPuck);
|
||||||
|
const myTeamHasPuck = this.state.hasPuck || teammatePuckCarrier;
|
||||||
|
|
||||||
|
if (myTeamHasPuck) {
|
||||||
|
this.behaviorWhenTeamHasPuck(gameState, puck, teammates, opponents, distanceToPuck);
|
||||||
|
} else if (opponentPuckCarrier) {
|
||||||
|
this.behaviorWhenOpponentHasPuck(gameState, puck, teammates, opponents, opponentPuckCarrier);
|
||||||
} else {
|
} else {
|
||||||
this.behaviorWithoutPuck(gameState, puck, teammates, opponents, distanceToPuck);
|
this.behaviorWhenPuckIsLoose(gameState, puck, teammates, opponents, distanceToPuck);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offensive AI behavior when player has possession of the puck
|
* AI behavior when this player's team has possession of the puck
|
||||||
|
* Handles both when this player has the puck and when a teammate has it
|
||||||
|
* @param {Object} gameState - Current game state with rink dimensions
|
||||||
|
* @param {Object} puck - Puck object with position and velocity
|
||||||
|
* @param {Array} teammates - Array of teammate player objects
|
||||||
|
* @param {Array} opponents - Array of opposing player objects
|
||||||
|
* @param {number} distanceToPuck - Pre-calculated distance to puck
|
||||||
|
*/
|
||||||
|
behaviorWhenTeamHasPuck(gameState, puck, teammates, opponents, distanceToPuck) {
|
||||||
|
if (this.state.hasPuck) {
|
||||||
|
// This player has the puck - offensive behavior
|
||||||
|
this.offensiveBehaviorWithPuck(gameState, puck, teammates, opponents);
|
||||||
|
} else {
|
||||||
|
// Teammate has the puck - support behavior
|
||||||
|
this.supportOffensiveBehavior(gameState, puck, teammates, opponents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI behavior when the opponent team has possession of the puck
|
||||||
|
* All players focus on defensive positioning and pressure
|
||||||
|
* @param {Object} gameState - Current game state
|
||||||
|
* @param {Object} puck - Puck object with position
|
||||||
|
* @param {Array} teammates - Array of teammate player objects
|
||||||
|
* @param {Array} opponents - Array of opposing player objects
|
||||||
|
* @param {Object} opponentPuckCarrier - The opponent player who has the puck
|
||||||
|
*/
|
||||||
|
behaviorWhenOpponentHasPuck(gameState, puck, teammates, opponents, opponentPuckCarrier) {
|
||||||
|
// Check if this player is the closest defender to the puck carrier
|
||||||
|
const isClosestDefender = this.isClosestDefenderToPuckCarrier(opponentPuckCarrier, teammates);
|
||||||
|
|
||||||
|
if (isClosestDefender) {
|
||||||
|
// Closest defender aggressively targets the puck carrier
|
||||||
|
this.moveToPosition(opponentPuckCarrier.position);
|
||||||
|
this.aiState.behavior = 'aggressive_pressure';
|
||||||
|
} else {
|
||||||
|
// Other players position defensively
|
||||||
|
this.defendPosition(gameState, opponentPuckCarrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI behavior when the puck is loose (no one has possession)
|
||||||
|
* Players compete to gain control while maintaining team structure
|
||||||
|
* @param {Object} gameState - Current game state
|
||||||
|
* @param {Object} puck - Puck object with position
|
||||||
|
* @param {Array} teammates - Array of teammate player objects
|
||||||
|
* @param {Array} opponents - Array of opposing player objects
|
||||||
|
* @param {number} distanceToPuck - Pre-calculated distance to puck
|
||||||
|
*/
|
||||||
|
behaviorWhenPuckIsLoose(gameState, puck, teammates, opponents, distanceToPuck) {
|
||||||
|
const isClosestToPuck = this.isClosestPlayerToPuck(puck, teammates);
|
||||||
|
const allPlayers = [...teammates, ...opponents, this];
|
||||||
|
|
||||||
|
if (isClosestToPuck && distanceToPuck < 200) {
|
||||||
|
// Only chase if this player is closest to the puck on their team
|
||||||
|
this.chasePuck(puck);
|
||||||
|
} else {
|
||||||
|
// Maintain formation position while puck is contested
|
||||||
|
this.moveToFormationPosition(gameState, puck, allPlayers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offensive behavior when this specific player has the puck
|
||||||
* Prioritizes shooting, then passing under pressure, then advancing toward goal
|
* Prioritizes shooting, then passing under pressure, then advancing toward goal
|
||||||
* @param {Object} gameState - Current game state with rink dimensions
|
* @param {Object} gameState - Current game state with rink dimensions
|
||||||
* @param {Object} puck - Puck object to shoot or pass
|
* @param {Object} puck - Puck object to shoot or pass
|
||||||
* @param {Array} teammates - Array of teammate player objects
|
* @param {Array} teammates - Array of teammate player objects
|
||||||
* @param {Array} opponents - Array of opposing player objects
|
* @param {Array} opponents - Array of opposing player objects
|
||||||
*/
|
*/
|
||||||
behaviorWithPuck(gameState, puck, teammates, opponents) {
|
offensiveBehaviorWithPuck(gameState, puck, teammates, opponents) {
|
||||||
const enemyGoal = this.team === 'home' ?
|
const enemyGoal = this.team === 'home' ?
|
||||||
new Vector2(gameState.rink.width - 50, gameState.rink.centerY) :
|
new Vector2(gameState.rink.width - 50, gameState.rink.centerY) :
|
||||||
new Vector2(50, gameState.rink.centerY);
|
new Vector2(50, gameState.rink.centerY);
|
||||||
@ -203,38 +274,21 @@ class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defensive AI behavior when player doesn't have puck possession
|
* Support behavior when a teammate has the puck
|
||||||
* Chooses between chasing loose puck, defending against opponents, or maintaining formation
|
* Positions for passes, creates scoring opportunities, and maintains offensive formation
|
||||||
* @param {Object} gameState - Current game state
|
* @param {Object} gameState - Current game state
|
||||||
* @param {Object} puck - Puck object with position
|
* @param {Object} puck - Puck object with position
|
||||||
* @param {Array} teammates - Array of teammate player objects
|
* @param {Array} teammates - Array of teammate player objects
|
||||||
* @param {Array} opponents - Array of opposing player objects
|
* @param {Array} opponents - Array of opposing player objects
|
||||||
* @param {number} distanceToPuck - Pre-calculated distance to puck
|
|
||||||
*/
|
*/
|
||||||
behaviorWithoutPuck(gameState, puck, teammates, opponents, distanceToPuck) {
|
supportOffensiveBehavior(gameState, puck, teammates, opponents) {
|
||||||
const puckOwner = opponents.find(p => p.state.hasPuck) || teammates.find(p => p.state.hasPuck);
|
|
||||||
const isClosestToPuck = this.isClosestPlayerToPuck(puck, teammates);
|
|
||||||
const allPlayers = [...teammates, ...opponents, this];
|
const allPlayers = [...teammates, ...opponents, this];
|
||||||
|
const puckCarrier = teammates.find(p => p.state.hasPuck);
|
||||||
|
|
||||||
if (!puckOwner && isClosestToPuck && distanceToPuck < 200) {
|
// Move to an offensive formation position that supports the puck carrier
|
||||||
// Only chase if this player is closest to the puck on their team
|
const supportPosition = this.getOffensiveSupportPosition(gameState, puck, puckCarrier, opponents);
|
||||||
this.chasePuck(puck);
|
this.moveToPosition(supportPosition);
|
||||||
} else if (puckOwner && puckOwner.team !== this.team) {
|
this.aiState.behavior = 'offensive_support';
|
||||||
// Check if this player is the closest defender to the puck carrier
|
|
||||||
const isClosestDefender = this.isClosestDefenderToPuckCarrier(puckOwner, teammates);
|
|
||||||
|
|
||||||
if (isClosestDefender) {
|
|
||||||
// Closest defender aggressively targets the puck carrier
|
|
||||||
this.moveToPosition(puckOwner.position);
|
|
||||||
this.aiState.behavior = 'aggressive_pressure';
|
|
||||||
} else if (distanceToPuck < 150 && Math.random() < 0.2) {
|
|
||||||
this.checkPlayer(puckOwner);
|
|
||||||
} else {
|
|
||||||
this.defendPosition(gameState, puckOwner);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.moveToFormationPosition(gameState, puck, allPlayers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,6 +446,75 @@ class Player {
|
|||||||
return this.getContextualPosition(rink, side, isAttacking, puck);
|
return this.getContextualPosition(rink, side, isAttacking, puck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates offensive support position when a teammate has the puck
|
||||||
|
* Positions player to receive passes, create scoring chances, and maintain offensive pressure
|
||||||
|
* @param {Object} gameState - Current game state with rink dimensions
|
||||||
|
* @param {Object} puck - Puck object with position
|
||||||
|
* @param {Object} puckCarrier - Teammate who has the puck
|
||||||
|
* @param {Array} opponents - Array of opposing player objects
|
||||||
|
* @returns {Vector2} Calculated offensive support position
|
||||||
|
*/
|
||||||
|
getOffensiveSupportPosition(gameState, puck, puckCarrier, opponents) {
|
||||||
|
const rink = gameState.rink;
|
||||||
|
const enemyGoal = this.team === 'home' ?
|
||||||
|
new Vector2(rink.width - 50, rink.centerY) :
|
||||||
|
new Vector2(50, rink.centerY);
|
||||||
|
|
||||||
|
// Base position is an aggressive offensive formation
|
||||||
|
const side = this.team === 'home' ? 1 : -1;
|
||||||
|
const attackZone = this.team === 'home' ? rink.width * 0.75 : rink.width * 0.25;
|
||||||
|
|
||||||
|
let baseX, baseY;
|
||||||
|
|
||||||
|
switch (this.role) {
|
||||||
|
case 'C':
|
||||||
|
// Center positions for rebounds and passes
|
||||||
|
baseX = attackZone;
|
||||||
|
baseY = rink.centerY + (puck.position.y > rink.centerY ? -60 : 60);
|
||||||
|
break;
|
||||||
|
case 'LW':
|
||||||
|
// Left wing positions for cross-ice passes and wraparounds
|
||||||
|
baseX = attackZone - 30;
|
||||||
|
baseY = rink.centerY - 140;
|
||||||
|
break;
|
||||||
|
case 'RW':
|
||||||
|
// Right wing positions for cross-ice passes and wraparounds
|
||||||
|
baseX = attackZone - 30;
|
||||||
|
baseY = rink.centerY + 140;
|
||||||
|
break;
|
||||||
|
case 'LD':
|
||||||
|
// Left defense supports from the point
|
||||||
|
baseX = attackZone - 120;
|
||||||
|
baseY = rink.centerY - 100;
|
||||||
|
break;
|
||||||
|
case 'RD':
|
||||||
|
// Right defense supports from the point
|
||||||
|
baseX = attackZone - 120;
|
||||||
|
baseY = rink.centerY + 100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return this.getFormationPosition(gameState, puck, [puckCarrier, ...opponents, this]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust position to avoid clustering with puck carrier
|
||||||
|
if (puckCarrier) {
|
||||||
|
const distanceToPuckCarrier = new Vector2(baseX, baseY).distance(puckCarrier.position);
|
||||||
|
if (distanceToPuckCarrier < 80) {
|
||||||
|
// Spread out from puck carrier
|
||||||
|
const awayFromCarrier = new Vector2(baseX, baseY).subtract(puckCarrier.position).normalize();
|
||||||
|
baseX += awayFromCarrier.x * 50;
|
||||||
|
baseY += awayFromCarrier.y * 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep positions within rink bounds
|
||||||
|
baseX = Math.max(50, Math.min(rink.width - 50, baseX));
|
||||||
|
baseY = Math.max(50, Math.min(rink.height - 50, baseY));
|
||||||
|
|
||||||
|
return new Vector2(baseX, baseY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if this player's team is in attacking or defending mode
|
* Determines if this player's team is in attacking or defending mode
|
||||||
* Based on puck possession and puck location on the rink
|
* Based on puck possession and puck location on the rink
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user