--- phase: 02-core-gameplay plan: 01 subsystem: gameplay tags: [canvas, vanilla-js, ai, physics, input, pong] # Dependency graph requires: - phase: 01-foundation provides: GameConfig, GameState.paddle1, GameState.ball, Physics (update/collide/serve), Input (W/S), Renderer, GameLoop provides: - GameState.paddle2 object (mirrors paddle1 structure, positioned on right side) - GameConfig.WIN_SCORE and AI_EASY/AI_MEDIUM/AI_HARD difficulty presets - Input.getVerticalInput2() for ArrowUp/ArrowDown keys - Input.cleanup() with named handler refs for event listener removal - AI object with predictive ray-cast interception (wall-bounce simulation) - Physics._checkPaddle2Collision() with 5-zone deflection sending ball LEFT - Physics.update() extended with paddle2 movement branch (mode: '2p' or 'ai') - GameState.score1, score2, mode, difficulty, gameState, winner fields affects: [02-02, 02-core-gameplay] # Tech tracking tech-stack: added: [] patterns: - "AI module as plain object with init/update/_predictBallY — same module-object pattern as Physics/Input" - "Predictive ray-cast AI: steps through future positions with wall bounces instead of linear interpolation" - "Difficulty preset as config object: { speed, reactionDelay, errorMargin } — all tunable from GameConfig" - "Named event handler refs on Input (_handleKeyDown/_handleKeyUp) enable cleanup() for memory management" key-files: created: [] modified: - index.html key-decisions: - "AI waits when ball moves toward player (vx < 0), reacts only when ball approaches (vx > 0) — avoids premature repositioning" - "Deadzone of 3px in AI movement prevents micro-jitter when paddle center is already near target" - "Physics.onResize() now calls this.init() to fully reposition both paddles on window resize" - "Paddle2 collision pushes ball.x = paddle.x - ball.radius - 1 (mirrored from paddle1's right-push)" patterns-established: - "Mode guard in Physics.update(): GameState.mode === '2p' branches to human input, 'ai' branches to AI.update()" - "Collision guards: ball.vx < 0 for paddle1, ball.vx > 0 for paddle2 — prevents wrong-direction collision checks" requirements-completed: [CORE-08, AI-01, AI-02, AI-03, AI-04] # Metrics duration: 2min completed: 2026-03-10 --- # Phase 2 Plan 01: Paddle2 and AI Opponent Summary **GameState.paddle2 + full AI opponent module with predictive ray-cast interception and three tunable difficulty levels (easy/medium/hard)** ## Performance - **Duration:** ~2 min - **Started:** 2026-03-10T20:04:26Z - **Completed:** 2026-03-10T20:06:12Z - **Tasks:** 2 - **Files modified:** 1 ## Accomplishments - Added paddle2 to GameState (mirrors paddle1 structure) and positioned it on right side in Physics.init() - Extended Input with ArrowUp/ArrowDown support, named handler refs, and cleanup() method - Added AI module with predictive ray-cast (_predictBallY simulates wall bounces) plus reaction delay and error margin per difficulty - Added Physics._checkPaddle2Collision() using same 5-zone deflection as paddle1 but sends ball leftward - Extended Physics.update() with paddle2 movement branch (2p human or ai mode) ## Task Commits Each task was committed atomically: 1. **Task 1: Extend GameConfig, GameState, and Input for two-player support** - `a2f0bc3` (feat) 2. **Task 2: Add AI module and extend Physics for paddle2 movement and collision** - `398bc4a` (feat) ## Files Created/Modified - `/home/dabit/projects/gijs/index.html` - Added WIN_SCORE/AI presets to GameConfig; paddle2/score/mode/difficulty/gameState/winner to GameState; refactored Input with named handlers + getVerticalInput2(); extended Physics.init() and onResize(); added _checkPaddle2Collision(); extended Physics.update() for paddle2; added AI object; added AI.init() to init block ## Decisions Made - Physics.onResize() now calls this.init() instead of just updating width/height, so paddle2 gets repositioned on resize - AI reaction timer resets to 0 whenever ball.vx < 0 (ball moving toward player) — AI fully waits before reacting again once ball returns - Deadzone of 3px prevents AI paddle jitter when already near target ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - paddle2 exists with valid x/y/width/height/speed/color — ready for Renderer to draw it in Plan 02-02 - AI.update() fires when GameState.mode === 'ai' — mode selection UI needed (Plan 02-02) - Score tracking fields (score1, score2) and gameState state machine fields are wired and ready for Plan 02-02 scoring logic --- *Phase: 02-core-gameplay* *Completed: 2026-03-10*