- Add isGoalScored flag to GameState for behavior tree decision-making - Update SkaterBehavior to skate to center ice (0,0) when goal scored - Update GoalieBehavior to return to center of goal when goal scored - Remove update loop freeze from GameScene, now handled by behaviors - Update CLAUDE.md with comprehensive subsystem documentation All player logic now contained within the behavior system instead of GameScene update loop, improving separation of concerns and making post-goal behavior more realistic (players skate to faceoff positions). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
A hockey match engine simulation built with PhaserJS and TypeScript. The engine uses continuous positioning (exact X/Y coordinates on a 2D rink) combined with behavior trees for AI decision-making to create a realistic hockey match simulation.
Core Concept: Players have exact positions on a 60x30m international hockey rink, with AI running behavior trees at 60 FPS to make tactical decisions (passing, shooting, positioning, etc.).
Development Commands
# Start development server (runs on port 3000)
pnpm run dev
# Build for production
pnpm run build
# Preview production build
pnpm run preview
Coordinate System
Critical: The rink uses a centered coordinate system:
- Origin (0, 0) = center of rink
- Rink dimensions: 60m length × 30m width
- Left goal: x = -26m, Right goal: x = +26m
- All positions use meters as floats (not integers)
- Screen rendering: 1 meter = 20 pixels (SCALE constant)
Architecture
Project Structure
src/
config/
constants.ts # All game constants (rink dimensions, colors, speeds, AI parameters)
game/
main.ts # Phaser game initialization and config
GameScene.ts # Main game scene (rendering, game loop, physics, event handling)
Goal.ts # Goal structure with physics bodies and scoring detection
entities/
Player.ts # Player entity with physics, movement, and detection systems
Puck.ts # Puck entity with physics and state management
systems/
BehaviorTree.ts # Main behavior tree system for AI decision-making
BehaviorNodes.ts # Base classes (Selector, Sequence, Condition, Action)
behaviors/
SkaterBehavior.ts # Top-level skater AI (context selector)
GoalieBehavior.ts # Goalie-specific AI
TransitionBehavior.ts # Loose puck / neutral zone behavior
offensive/
PuckCarrierBehavior.ts # Shoot/carry/evade decisions
OffensiveSupportBehavior.ts # Support when teammate has puck (planned)
defensive/
DefensiveBehavior.ts # Chase and pressure opponent
types/
game.ts # TypeScript types and interfaces
utils/
coordinates.ts # Game ↔ screen coordinate conversion
math.ts # Distance, angle utilities
Implementation Status
✓ Completed Phases:
- Phase 1: Environment setup, rink rendering
- Phase 2: Player entities with smooth acceleration/deceleration and rotation
- Phase 3: Puck physics and possession mechanics
- Phase 4: Puck carrier behavior (shoot/carry/evade)
- Phase 6: Basic defensive behavior (chase puck carrier)
- Phase 7: Basic goalie AI (track puck position)
- Phase 8: Goal detection, scoring system, pause-aware reset timers
🚧 In Progress / Partial:
- Phase 5: Offensive support (placeholder only)
- Tackle mechanics (implemented but needs tuning)
- Puck reception (implemented with skill-based probability)
📋 Planned:
- Passing AI (identify and execute passes)
- Advanced offensive positioning (heat maps, formations)
- Faceoffs and game flow states
- Save mechanics for goalies
Core Systems Documentation
1. Behavior Tree System
Location: src/systems/BehaviorTree.ts, src/systems/BehaviorNodes.ts
Purpose: AI decision-making engine that runs at 60 FPS for each player.
Architecture:
- BehaviorNode: Abstract base class for all nodes
- Selector: OR logic - tries children until one succeeds
- Sequence: AND logic - all children must succeed
- Condition: Boolean predicate functions
- Action: Leaf nodes that return PlayerAction
Decision Tree Structure:
Root Selector
├─ Goalie? → GoalieBehavior
└─ Skater? → SkaterBehavior
├─ Has puck? → PuckCarrierBehavior (shoot/carry/evade)
├─ Teammate has puck? → OffensiveSupportBehavior (planned)
├─ Opponent has puck? → DefensiveBehavior (chase)
└─ Loose puck → TransitionBehavior (chase loose puck)
Key Files:
- BehaviorTree.ts:26-60: Main tree evaluation
- SkaterBehavior.ts:21-39: Context-based selector
2. Player Entity System
Location: src/entities/Player.ts
Purpose: Player physics, movement, rotation, and nearby player detection.
Key Features:
Movement System (Player.ts:186-286):
- Smooth acceleration with ease-out curve (PLAYER_ACCELERATION)
- Speed-dependent rotation (faster = wider turns)
- Deceleration when near target or after goals
- Stop threshold to prevent jittering (MOVEMENT_STOP_THRESHOLD)
Player Detection System (Player.ts:324-387):
- Physics-based circular sensor (NEAR_PLAYER_DETECTION_RADIUS = 4m)
- Field-of-view filtering (NEAR_PLAYER_DETECTION_ANGLE = 180°)
- Maintains sets:
nearbyPlayers,nearbyOpponents,nearbyTeammates - Methods:
getDetectedOpponents(),getDetectedTeammates()
Tackle System (Player.ts:290-312):
- Cooldown timer (TACKLE_COOLDOWN = 1000ms)
- Fall duration when tackled (TACKLE_FALL_DURATION = 500ms)
- Speed requirement (TACKLE_MIN_SPEED = 2 m/s)
- Angle-based effectiveness modifiers
Player Attributes (types/game.ts:27-33):
speed: Movement speed (0-100) → m/s via SPEED_SCALE_FACTORskill: Pass/shot accuracy, decision qualitytackling: Tackle success probabilitybalance: Resist being tackledhandling: Puck reception and control
3. Puck Entity System
Location: src/entities/Puck.ts
Purpose: Puck physics, state management, and possession tracking.
Puck States (types/game.ts:22):
loose: Free on ice, physics-drivencarried: Held by player (carrier ID tracked)passing: In flight between players (future)shot: Shot toward goal
Physics Configuration (Puck.ts:36-56):
- Circular collision body (PUCK_RADIUS = 0.2m)
- Max velocity: MAX_PUCK_VELOCITY = 50 m/s
- Drag (ice friction): PUCK_DRAG = 200 pixels/s²
- Bounce coefficient: PUCK_BOUNCE = 0.6 (loses energy)
- World bounds collision enabled
Key Methods:
setCarrier(playerId, team): Assign possessionsetLoose(): Release to physics simulationupdate(): Sync game coordinates with physics body
4. Puck Carrier Behavior
Location: src/systems/behaviors/offensive/PuckCarrierBehavior.ts
Purpose: Tactical decisions when player has possession.
Decision Priority:
- Evade if opponent threatens head-on (PuckCarrierBehavior.ts:104-254)
- Shoot if good opportunity (PuckCarrierBehavior.ts:262-308)
- Carry toward opponent's net (default)
Evasion System:
- Uses physics-based player detection (NEAR_PLAYER_DETECTION_RADIUS)
- Checks if opponent is within 60° of goal direction
- Maintains consistent evasion direction per opponent (stored in
activeEvasionsmap) - Evasion angle: ±60° (EVASION_ANGLE) from goal direction
- Clears evasion when opponent leaves detection zone
Shooting Logic:
- Must be in front of goal (not behind)
- Distance ≤ SHOOTING_RANGE (10m)
- Angle-based accuracy:
- Close range (≤3m): 120° shooting angle allowed
- Normal range: 60° shooting angle required
- Aims at center of net (targetY = 0)
Debug Visualization (when DEBUG = true):
- Green line to goal
- Red circle around threats being evaded
- Yellow arrow showing evasion direction
- Orange circles for detected opponents
5. GameScene System
Location: src/game/GameScene.ts
Purpose: Main game loop, physics management, collision detection, event handling.
Key Responsibilities:
Physics Setup:
- Player-player collisions (including tackle mechanics)
- Player-puck overlaps (pickup and reception)
- Puck-goal post collisions with bounce
- Detection sensor overlaps (nearby player tracking)
Puck Management:
- Carrier positioning: puck stays 1m in front of carrier
- Pickup radius: PUCK_PICKUP_RADIUS = 1.5m
- Reception skill checks (PUCK_RECEPTION_BASE_CHANCE)
- Speed-based reception difficulty
Shooting System:
- Shot speed: SHOT_SPEED = 30 m/s (or 0.6× for close range)
- Post bounce: reduces speed and adds random angle variation
- Goal detection via overlap with scoring zone
Tackle System:
- Cooldown enforcement (TACKLE_COOLDOWN)
- Angle-based effectiveness (head-on = 100%, side = 70%, etc.)
- Closing speed modifiers
- Puck loose probability (TACKLE_PUCK_LOOSE_CHANCE = 60%)
Goal System:
- Freeze game on goal event
- 3-second pause-aware timer
- Player deceleration (GOAL_DECELERATION_RATE)
- Reset positions to center ice
6. Coordinate System
Location: src/utils/coordinates.ts
Purpose: Convert between game coordinates (meters) and screen coordinates (pixels).
Critical Details:
- Game coordinates: Origin (0, 0) at center of rink, meters as floats
- Screen coordinates: Origin at top-left, pixels
- Scale factor: 1 meter = 20 pixels (SCALE constant)
- Y-axis inversion: Game Y increases upward, screen Y increases downward
Key Methods:
gameToScreen(scene, gameX, gameY): Meters → pixelsscreenToGame(scene, screenX, screenY): Pixels → metersgetScreenCenter(scene): Canvas center in pixels
Rink Layout:
- Rink: 60m × 30m
- Left goal: x = -26m (GOAL_LINE_OFFSET)
- Right goal: x = +26m
- Blue lines: x = ±9m (BLUE_LINE_OFFSET)
- Center ice: (0, 0)
7. Constants Configuration
Location: src/config/constants.ts
Purpose: Centralized configuration for all game parameters.
Categories:
Game Settings:
- DEBUG, FPS, UI_BOTTOM_PADDING
Rink Dimensions:
- RINK_LENGTH, RINK_WIDTH, SCALE, corner radius, zone lines
Player Constants:
- Radius (goalie vs skater), rotation speed, acceleration/deceleration
- Speed scale factor, movement threshold
Puck Constants:
- Radius, pickup range, carry distance, max velocity
- Drag, bounce, shot speed, reception parameters
AI/Behavior:
- Shooting range and angle thresholds (normal vs close-range)
- Goalie range, detection radius and angle
- Evasion angle
Tackle Mechanics:
- Success modifiers, cooldown, fall duration
- Angle thresholds and modifiers
- Closing speed thresholds
Collision/Physics:
- Post bounce reduction, puck loose chance
- Reception skill scaling
Type Definitions
Location: src/types/game.ts
Key Types:
PlayerPosition: 'LW' | 'C' | 'RW' | 'LD' | 'RD' | 'G'
TeamSide: 'home' | 'away'
PlayerState: 'offensive' | 'defensive'
PuckState: 'loose' | 'carried' | 'passing' | 'shot'
PlayerAttributes: {
speed, skill, tackling, balance, handling
}
GameState: {
puck: Puck
allPlayers: Player[]
}
PlayerAction: {
type: 'move' | 'chase_puck' | 'skate_with_puck' | 'shoot' | 'idle'
targetX?, targetY?
}
Technical Stack
- Framework: Phaser 3.90.0 (Arcade Physics engine)
- TypeScript: Strict mode enabled
- Build Tool: Vite 5.4
- Target FPS: 60 (configured in constants.ts)
- Physics: Arcade physics with zero gravity (top-down view)
- Coordinate System: Centered origin (0,0), meters as float values
- Rendering Scale: 1 meter = 20 pixels
Debug Features
Toggle: Set DEBUG = true in constants.ts
Visual Overlays:
- Player movement: Line and marker showing target position
- Player detection: Magenta wedge showing field-of-view sensor
- Puck carrier evasion:
- Green line to goal
- Red circles around detected threats
- Yellow/green arrows showing evasion direction
- Orange circles for all nearby opponents
Console Logging:
- Goal events with team and goal location
- Shot attempts with player ID
- Tackle results and success calculations
Development Workflow
Hot Reload Development:
pnpm run dev # Starts on localhost:3000 with hot reload
Testing Approach:
- Test each subsystem incrementally before integration
- Use DEBUG mode to visualize AI decision-making
- Tune constants based on gameplay feel (see constants.ts)
- Validate physics with edge cases (goal posts, rink boundaries)
Key Tuning Parameters:
- Player acceleration/deceleration curves → smooth movement feel
- Detection radius/angle → evasion responsiveness
- Shooting angles → goal frequency balance
- Tackle modifiers → physical gameplay intensity
- Puck drag/bounce → realistic puck physics
Documentation:
- PLAN.md: Phase-by-phase implementation plan
- NOTES.md: Architecture decisions and design rationale
- REVIEW.md: Progress reviews and lessons learned
- CLAUDE.md (this file): System reference for AI assistant
Implementation Patterns
Adding New Behaviors:
- Create behavior class extending
BehaviorNodeinsrc/systems/behaviors/ - Implement
tick(player, gameState)method - Add to appropriate selector in
SkaterBehaviororGoalieBehavior - Test in isolation before integration
Adding New Constants:
- Add to appropriate section in constants.ts
- Use semantic names (e.g.,
SHOOTING_RANGEnotMAX_DIST) - Include units in comments (meters, m/s, radians, pixels)
- Export for use across codebase
Physics Bodies:
- Players: Circular bodies (radius varies by position)
- Puck: Circular body with drag and bounce
- Goals: Static rectangular bodies for posts/bars
- Sensors: Physics zones with
overlapnotcollide
Coordinate Conversions:
- Always use
CoordinateUtils.gameToScreen()andscreenToGame() - Never mix coordinate systems in calculations
- Store positions in game coordinates (meters), convert for rendering